1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.aconfig.storage.test;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 
22 import android.aconfig.DeviceProtos;
23 import android.aconfig.nano.Aconfig.parsed_flag;
24 import android.aconfig.storage.AconfigStorageReadAPI;
25 import android.aconfig.storage.FlagReadContext;
26 import android.aconfig.storage.FlagReadContext.StoredFlagType;
27 import android.aconfig.storage.PackageReadContext;
28 import android.aconfig.storage.SipHasher13;
29 import android.aconfig.storage.StorageInternalReader;
30 
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.junit.runners.JUnit4;
34 
35 import java.io.IOException;
36 import java.nio.MappedByteBuffer;
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 @RunWith(JUnit4.class)
41 public class AconfigStorageReadAPITest {
42 
43     private String mStorageDir = "/data/local/tmp/aconfig_java_api_test";
44 
45     @Test
testPackageContextQuery()46     public void testPackageContextQuery() {
47         MappedByteBuffer packageMap = null;
48         try {
49             packageMap =
50                     AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/maps/mockup.package.map");
51         } catch (IOException ex) {
52             assertTrue(ex.toString(), false);
53         }
54         assertTrue(packageMap != null);
55 
56         try {
57             PackageReadContext context =
58                     AconfigStorageReadAPI.getPackageReadContext(
59                             packageMap, "com.android.aconfig.storage.test_1");
60             assertEquals(context.mPackageId, 0);
61             assertEquals(context.mBooleanStartIndex, 0);
62 
63             context =
64                     AconfigStorageReadAPI.getPackageReadContext(
65                             packageMap, "com.android.aconfig.storage.test_2");
66             assertEquals(context.mPackageId, 1);
67             assertEquals(context.mBooleanStartIndex, 3);
68 
69             context =
70                     AconfigStorageReadAPI.getPackageReadContext(
71                             packageMap, "com.android.aconfig.storage.test_4");
72             assertEquals(context.mPackageId, 2);
73             assertEquals(context.mBooleanStartIndex, 6);
74         } catch (IOException ex) {
75             assertTrue(ex.toString(), false);
76         }
77     }
78 
79     @Test
testNonExistPackageContextQuery()80     public void testNonExistPackageContextQuery() {
81         MappedByteBuffer packageMap = null;
82         try {
83             packageMap =
84                     AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/maps/mockup.package.map");
85         } catch (IOException ex) {
86             assertTrue(ex.toString(), false);
87         }
88         assertTrue(packageMap != null);
89 
90         try {
91             PackageReadContext context =
92                     AconfigStorageReadAPI.getPackageReadContext(packageMap, "unknown");
93             assertEquals(context.mPackageId, -1);
94             assertEquals(context.mBooleanStartIndex, -1);
95         } catch (IOException ex) {
96             assertTrue(ex.toString(), false);
97         }
98     }
99 
100     @Test
testFlagContextQuery()101     public void testFlagContextQuery() {
102         MappedByteBuffer flagMap = null;
103         try {
104             flagMap = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/maps/mockup.flag.map");
105         } catch (IOException ex) {
106             assertTrue(ex.toString(), false);
107         }
108         assertTrue(flagMap != null);
109 
110         class Baseline {
111             public int mPackageId;
112             public String mFlagName;
113             public StoredFlagType mFlagType;
114             public int mFlagIndex;
115 
116             public Baseline(
117                     int packageId, String flagName, StoredFlagType flagType, int flagIndex) {
118                 mPackageId = packageId;
119                 mFlagName = flagName;
120                 mFlagType = flagType;
121                 mFlagIndex = flagIndex;
122             }
123         }
124 
125         List<Baseline> baselines = new ArrayList();
126         baselines.add(new Baseline(0, "enabled_ro", StoredFlagType.ReadOnlyBoolean, 1));
127         baselines.add(new Baseline(0, "enabled_rw", StoredFlagType.ReadWriteBoolean, 2));
128         baselines.add(new Baseline(2, "enabled_rw", StoredFlagType.ReadWriteBoolean, 1));
129         baselines.add(new Baseline(1, "disabled_rw", StoredFlagType.ReadWriteBoolean, 0));
130         baselines.add(new Baseline(1, "enabled_fixed_ro", StoredFlagType.FixedReadOnlyBoolean, 1));
131         baselines.add(new Baseline(1, "enabled_ro", StoredFlagType.ReadOnlyBoolean, 2));
132         baselines.add(new Baseline(2, "enabled_fixed_ro", StoredFlagType.FixedReadOnlyBoolean, 0));
133         baselines.add(new Baseline(0, "disabled_rw", StoredFlagType.ReadWriteBoolean, 0));
134 
135         try {
136             for (Baseline baseline : baselines) {
137                 FlagReadContext context =
138                         AconfigStorageReadAPI.getFlagReadContext(
139                                 flagMap, baseline.mPackageId, baseline.mFlagName);
140                 assertEquals(context.mFlagType, baseline.mFlagType);
141                 assertEquals(context.mFlagIndex, baseline.mFlagIndex);
142             }
143         } catch (IOException ex) {
144             assertTrue(ex.toString(), false);
145         }
146     }
147 
148     @Test
testNonExistFlagContextQuery()149     public void testNonExistFlagContextQuery() {
150         MappedByteBuffer flagMap = null;
151         try {
152             flagMap = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/maps/mockup.flag.map");
153         } catch (IOException ex) {
154             assertTrue(ex.toString(), false);
155         }
156         assertTrue(flagMap != null);
157 
158         try {
159             FlagReadContext context =
160                     AconfigStorageReadAPI.getFlagReadContext(flagMap, 0, "unknown");
161             assertEquals(context.mFlagType, null);
162             assertEquals(context.mFlagIndex, -1);
163 
164             context = AconfigStorageReadAPI.getFlagReadContext(flagMap, 3, "enabled_ro");
165             assertEquals(context.mFlagType, null);
166             assertEquals(context.mFlagIndex, -1);
167         } catch (IOException ex) {
168             assertTrue(ex.toString(), false);
169         }
170     }
171 
172     @Test
testBooleanFlagValueQuery()173     public void testBooleanFlagValueQuery() {
174         MappedByteBuffer flagVal = null;
175         try {
176             flagVal = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/boot/mockup.val");
177         } catch (IOException ex) {
178             assertTrue(ex.toString(), false);
179         }
180         assertTrue(flagVal != null);
181 
182         boolean[] baselines = {false, true, true, false, true, true, true, true};
183         for (int i = 0; i < 8; ++i) {
184             try {
185                 Boolean value = AconfigStorageReadAPI.getBooleanFlagValue(flagVal, i);
186                 assertEquals(value, baselines[i]);
187             } catch (IOException ex) {
188                 assertTrue(ex.toString(), false);
189             }
190         }
191     }
192 
193     @Test
testInvalidBooleanFlagValueQuery()194     public void testInvalidBooleanFlagValueQuery() {
195         MappedByteBuffer flagVal = null;
196         try {
197             flagVal = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/boot/mockup.val");
198         } catch (IOException ex) {
199             assertTrue(ex.toString(), false);
200         }
201         assertTrue(flagVal != null);
202 
203         try {
204             Boolean value = AconfigStorageReadAPI.getBooleanFlagValue(flagVal, 9);
205             assertTrue("should throw", false);
206         } catch (IOException ex) {
207             String expectedErrmsg = "invalid storage file byte offset";
208             assertTrue(ex.toString(), ex.toString().contains(expectedErrmsg));
209         }
210     }
211 
212     @Test
testRustJavaEqualHash()213     public void testRustJavaEqualHash() throws IOException {
214         List<parsed_flag> flags = DeviceProtos.loadAndParseFlagProtos();
215         for (parsed_flag flag : flags) {
216             String packageName = flag.package_;
217             String flagName = flag.name;
218             long rHash = AconfigStorageReadAPI.hash(packageName);
219             long jHash = SipHasher13.hash(packageName.getBytes());
220             assertEquals(rHash, jHash);
221 
222             String fullFlagName = packageName + "/" + flagName;
223             rHash = AconfigStorageReadAPI.hash(fullFlagName);
224             jHash = SipHasher13.hash(fullFlagName.getBytes());
225             assertEquals(rHash, jHash);
226         }
227     }
228 
229     @Test
testRustJavaEqualFlag()230     public void testRustJavaEqualFlag() throws IOException {
231         List<parsed_flag> flags = DeviceProtos.loadAndParseFlagProtos();
232 
233         String mapPath = "/metadata/aconfig/maps/";
234         String flagsPath = "/metadata/aconfig/boot/";
235 
236         for (parsed_flag flag : flags) {
237 
238             String container = flag.container;
239             String packageName = flag.package_;
240             String flagName = flag.name;
241             String fullFlagName = packageName + "/" + flagName;
242 
243             MappedByteBuffer packageMap =
244                     AconfigStorageReadAPI.mapStorageFile(mapPath + container + ".package.map");
245             MappedByteBuffer flagMap =
246                     AconfigStorageReadAPI.mapStorageFile(mapPath + container + ".flag.map");
247             MappedByteBuffer flagValList =
248                     AconfigStorageReadAPI.mapStorageFile(flagsPath + container + ".val");
249 
250             PackageReadContext packageContext =
251                     AconfigStorageReadAPI.getPackageReadContext(packageMap, packageName);
252 
253             FlagReadContext flagContext =
254                     AconfigStorageReadAPI.getFlagReadContext(
255                             flagMap, packageContext.mPackageId, flagName);
256 
257             boolean rVal =
258                     AconfigStorageReadAPI.getBooleanFlagValue(
259                             flagValList,
260                             packageContext.mBooleanStartIndex + flagContext.mFlagIndex);
261 
262             StorageInternalReader reader = new StorageInternalReader(container, packageName);
263             boolean jVal = reader.getBooleanFlagValue(flagContext.mFlagIndex);
264 
265             long rHash = AconfigStorageReadAPI.hash(packageName);
266             long jHash = SipHasher13.hash(packageName.getBytes());
267             assertEquals(rVal, jVal);
268         }
269     }
270 }