1 /*
2  * Copyright (C) 2016 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 com.android.server.pm.dex;
18 
19 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
20 import static com.android.server.pm.dex.PackageDexUsage.MAX_SECONDARY_FILES_PER_OWNER;
21 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 
30 import android.os.Build;
31 import android.platform.test.annotations.Presubmit;
32 
33 import androidx.test.filters.SmallTest;
34 import androidx.test.runner.AndroidJUnit4;
35 
36 import dalvik.system.VMRuntime;
37 
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 
42 import java.io.IOException;
43 import java.io.StringReader;
44 import java.io.StringWriter;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.HashMap;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Set;
52 
53 @Presubmit
54 @RunWith(AndroidJUnit4.class)
55 @SmallTest
56 public class PackageDexUsageTests {
57     private static final String ISA = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
58 
59     private PackageDexUsage mPackageDexUsage;
60 
61     private TestData mFooBaseUser0;
62     private TestData mFooSplit1User0;
63     private TestData mFooSplit2UsedByOtherApps0;
64     private TestData mFooSecondary1User0;
65     private TestData mFooSecondary1User1;
66     private TestData mFooSecondary2UsedByOtherApps0;
67     private TestData mInvalidIsa;
68 
69     private TestData mBarBaseUser0;
70     private TestData mBarSecondary1User0;
71     private TestData mBarSecondary2User1;
72 
73     @Before
setup()74     public void setup() {
75         mPackageDexUsage = new PackageDexUsage();
76 
77         String fooPackageName = "com.google.foo";
78         String fooCodeDir = "/data/app/com.google.foo/";
79         String fooDataDir = "/data/user/0/com.google.foo/";
80 
81         mFooBaseUser0 = new TestData(fooPackageName,
82                 fooCodeDir + "base.apk", 0, ISA, true, fooPackageName);
83 
84         mFooSplit1User0 = new TestData(fooPackageName,
85                 fooCodeDir + "split-1.apk", 0, ISA, true, fooPackageName);
86 
87         mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
88                 fooCodeDir + "split-2.apk", 0, ISA, true, "used.by.other.com");
89 
90         mFooSecondary1User0 = new TestData(fooPackageName,
91                 fooDataDir + "sec-1.dex", 0, ISA, false, fooPackageName);
92 
93         mFooSecondary1User1 = new TestData(fooPackageName,
94                 fooDataDir + "sec-1.dex", 1, ISA, false, fooPackageName);
95 
96         mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
97                 fooDataDir + "sec-2.dex", 0, ISA, false, "used.by.other.com");
98 
99         mInvalidIsa = new TestData(fooPackageName,
100                 fooCodeDir + "base.apk", 0, "INVALID_ISA", true, "INALID_USER");
101 
102         String barPackageName = "com.google.bar";
103         String barCodeDir = "/data/app/com.google.bar/";
104         String barDataDir = "/data/user/0/com.google.bar/";
105         String barDataDir1 = "/data/user/1/com.google.bar/";
106 
107         mBarBaseUser0 = new TestData(barPackageName,
108                 barCodeDir + "base.apk", 0, ISA, true, barPackageName);
109         mBarSecondary1User0 = new TestData(barPackageName,
110                 barDataDir + "sec-1.dex", 0, ISA, false, barPackageName);
111         mBarSecondary2User1 = new TestData(barPackageName,
112                 barDataDir1 + "sec-2.dex", 1, ISA, false, barPackageName);
113     }
114 
115     @Test
testRecordPrimary()116     public void testRecordPrimary() {
117         // Assert new information.
118         assertTrue(record(mFooBaseUser0));
119 
120         assertPackageDexUsage(mFooBaseUser0);
121         writeAndReadBack();
122         assertPackageDexUsage(mFooBaseUser0);
123     }
124 
125     @Test
testRecordSplit()126     public void testRecordSplit() {
127         // Assert new information.
128         assertTrue(record(mFooSplit1User0));
129 
130         assertPackageDexUsage(mFooSplit1User0);
131         writeAndReadBack();
132         assertPackageDexUsage(mFooSplit1User0);
133     }
134 
135     @Test
testRecordSplitPrimarySequence()136     public void testRecordSplitPrimarySequence() {
137         // Assert new information.
138         assertTrue(record(mFooBaseUser0));
139         assertTrue(record(mFooSplit1User0));
140         // Assert no new information if we add again
141         assertFalse(record(mFooBaseUser0));
142         assertFalse(record(mFooSplit1User0));
143 
144         assertPackageDexUsage(mFooBaseUser0);
145         writeAndReadBack();
146         assertPackageDexUsage(mFooBaseUser0);
147 
148         // Write Split2 which is used by other apps.
149         // Assert new information.
150         assertTrue(record(mFooSplit2UsedByOtherApps0));
151         assertPackageDexUsage(mFooSplit2UsedByOtherApps0);
152         writeAndReadBack();
153         assertPackageDexUsage(mFooSplit2UsedByOtherApps0);
154     }
155 
156     @Test
testRecordSecondary()157     public void testRecordSecondary() {
158         assertTrue(record(mFooSecondary1User0));
159 
160         assertPackageDexUsage(null, mFooSecondary1User0);
161         writeAndReadBack();
162         assertPackageDexUsage(null, mFooSecondary1User0);
163 
164         // Recording again does not add more data.
165         assertFalse(record(mFooSecondary1User0));
166         assertPackageDexUsage(null, mFooSecondary1User0);
167     }
168 
169     @Test
testRecordBaseAndSecondarySequence()170     public void testRecordBaseAndSecondarySequence() {
171         // Write split.
172         assertTrue(record(mFooSplit2UsedByOtherApps0));
173         // Write secondary.
174         assertTrue(record(mFooSecondary1User0));
175 
176         // Check.
177         assertPackageDexUsage(mFooSplit2UsedByOtherApps0, mFooSecondary1User0);
178         writeAndReadBack();
179         assertPackageDexUsage(mFooSplit2UsedByOtherApps0, mFooSecondary1User0);
180 
181         // Write another secondary.
182         assertTrue(record(mFooSecondary2UsedByOtherApps0));
183 
184         // Check.
185         assertPackageDexUsage(
186                 mFooSplit2UsedByOtherApps0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0);
187         writeAndReadBack();
188         assertPackageDexUsage(
189                 mFooSplit2UsedByOtherApps0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0);
190     }
191 
192     @Test
testRecordTooManySecondaries()193     public void testRecordTooManySecondaries() {
194         int tooManyFiles = MAX_SECONDARY_FILES_PER_OWNER + 1;
195         List<TestData> expectedSecondaries = new ArrayList<>();
196         for (int i = 1; i <= tooManyFiles; i++) {
197             String fooPackageName = "com.google.foo";
198             TestData testData = new TestData(fooPackageName,
199                     "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false,
200                     fooPackageName);
201             if (i < tooManyFiles) {
202                 assertTrue("Adding " + testData.mDexFile, record(testData));
203                 expectedSecondaries.add(testData);
204             } else {
205                 assertFalse("Adding " + testData.mDexFile, record(testData));
206             }
207             assertPackageDexUsage(
208                     mPackageDexUsage,
209                     /* usdeBy=*/ (Set<String>) null,
210                     /* primaryDex= */ null,
211                     expectedSecondaries);
212         }
213     }
214 
215     @Test
testMultiplePackages()216     public void testMultiplePackages() {
217         assertTrue(record(mFooBaseUser0));
218         assertTrue(record(mFooSecondary1User0));
219         assertTrue(record(mFooSecondary2UsedByOtherApps0));
220         assertTrue(record(mBarBaseUser0));
221         assertTrue(record(mBarSecondary1User0));
222         assertTrue(record(mBarSecondary2User1));
223 
224         assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0);
225         assertPackageDexUsage(mBarBaseUser0, mBarSecondary1User0, mBarSecondary2User1);
226         writeAndReadBack();
227         assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0);
228         assertPackageDexUsage(mBarBaseUser0, mBarSecondary1User0, mBarSecondary2User1);
229     }
230 
231     @Test
testPackageNotFound()232     public void testPackageNotFound() {
233         assertNull(mPackageDexUsage.getPackageUseInfo("missing.package"));
234     }
235 
236     @Test
testAttemptToChangeOwner()237     public void testAttemptToChangeOwner() {
238         assertTrue(record(mFooSecondary1User0));
239         try {
240             record(mFooSecondary1User1);
241             fail("Expected exception");
242         } catch (IllegalArgumentException e) {
243             // expected
244         }
245     }
246 
247     @Test
testInvalidIsa()248     public void testInvalidIsa() {
249         try {
250             record(mInvalidIsa);
251             fail("Expected exception");
252         } catch (IllegalArgumentException e) {
253             // expected
254         }
255     }
256 
257     @Test
testReadWriteEmtpy()258     public void testReadWriteEmtpy() {
259         // Expect no exceptions when writing/reading without data.
260         writeAndReadBack();
261     }
262 
263     @Test
testSyncData()264     public void testSyncData() {
265         // Write some records.
266         assertTrue(record(mFooBaseUser0));
267         assertTrue(record(mFooSecondary1User0));
268         assertTrue(record(mFooSecondary2UsedByOtherApps0));
269         assertTrue(record(mBarBaseUser0));
270         assertTrue(record(mBarSecondary1User0));
271         assertTrue(record(mBarSecondary2User1));
272 
273         // Verify all is good.
274         assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0);
275         assertPackageDexUsage(mBarBaseUser0, mBarSecondary1User0, mBarSecondary2User1);
276         writeAndReadBack();
277         assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0);
278         assertPackageDexUsage(mBarBaseUser0, mBarSecondary1User0, mBarSecondary2User1);
279 
280         // Simulate that only user 1 is available.
281         Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
282         packageToUsersMap.put(mBarSecondary2User1.mPackageName,
283                 new HashSet<>(Arrays.asList(mBarSecondary2User1.mOwnerUserId)));
284         Map<String, Set<String>> packageToCodePaths = new HashMap<>();
285         packageToCodePaths.put(mBarBaseUser0.mPackageName,
286                 new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
287         mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList<String>());
288 
289         // Assert that only user 1 files are there.
290         assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1);
291         assertNull(mPackageDexUsage.getPackageUseInfo(mFooBaseUser0.mPackageName));
292     }
293 
294     @Test
testSyncDataKeepPackages()295     public void testSyncDataKeepPackages() {
296         PackageDexUsage packageDexUsage = new PackageDexUsage();
297         // Write the record we want to keep and which won't be keep by default.
298         Set<String> fooUsers = new HashSet<>(Arrays.asList(
299                 new String[] {mFooBaseUser0.mPackageName}));
300         assertTrue(record(packageDexUsage, mFooBaseUser0, fooUsers));
301         // Write a record that would be kept by default.
302         Set<String> barUsers = new HashSet<>(Arrays.asList(
303                 new String[] {"another.package", mFooBaseUser0.mPackageName}));
304         assertTrue(record(packageDexUsage, mBarBaseUser0, barUsers));
305 
306         // Construct the user packages and their code paths (things that will be
307         // kept by default during sync).
308         Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
309         packageToUsersMap.put(mBarBaseUser0.mPackageName,
310                 new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
311         Map<String, Set<String>> packageToCodePaths = new HashMap<>();
312         packageToCodePaths.put(mBarBaseUser0.mPackageName,
313                 new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
314 
315         // Sync data.
316         List<String> keepData = new ArrayList<String>();
317         keepData.add(mFooBaseUser0.mPackageName);
318         packageDexUsage.syncData(packageToUsersMap, packageToCodePaths, keepData);
319 
320         // Assert that both packages are kept
321         assertPackageDexUsage(packageDexUsage, fooUsers, mFooBaseUser0);
322         // "another.package" should not be in the loading packages after sync.
323         Set<String> expectedBarUsers = new HashSet<>(Arrays.asList(
324                 new String[] {mFooBaseUser0.mPackageName}));
325         assertPackageDexUsage(packageDexUsage, expectedBarUsers,
326                 mBarBaseUser0.updateUsedBy(mFooBaseUser0.mPackageName));
327     }
328 
329     @Test
testRemovePackage()330     public void testRemovePackage() {
331         // Record Bar secondaries for two different users.
332         assertTrue(record(mBarSecondary1User0));
333         assertTrue(record(mBarSecondary2User1));
334 
335         // Remove the package.
336         assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
337         // Assert that we can't find the package anymore.
338         assertNull(mPackageDexUsage.getPackageUseInfo(mBarSecondary1User0.mPackageName));
339     }
340 
341     @Test
testRemoveNonexistentPackage()342     public void testRemoveNonexistentPackage() {
343         // Record Bar secondaries for two different users.
344         assertTrue(record(mBarSecondary1User0));
345 
346         // Remove the package.
347         assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
348         // Remove the package again. It should return false because the package no longer
349         // has a record in the use info.
350         assertFalse(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
351     }
352 
353     @Test
testRemoveUserPackage()354     public void testRemoveUserPackage() {
355         // Record Bar secondaries for two different users.
356         assertTrue(record(mBarSecondary1User0));
357         assertTrue(record(mBarSecondary2User1));
358 
359         // Remove user 0 files.
360         assertTrue(mPackageDexUsage.removeUserPackage(mBarSecondary1User0.mPackageName,
361                 mBarSecondary1User0.mOwnerUserId));
362         // Assert that only user 1 files are there.
363         assertPackageDexUsage(null, mBarSecondary2User1);
364     }
365 
366     @Test
testRemoveDexFile()367     public void testRemoveDexFile() {
368         // Record Bar secondaries for two different users.
369         assertTrue(record(mBarSecondary1User0));
370         assertTrue(record(mBarSecondary2User1));
371 
372         // Remove mBarSecondary1User0 file.
373         assertTrue(mPackageDexUsage.removeDexFile(mBarSecondary1User0.mPackageName,
374                 mBarSecondary1User0.mDexFile, mBarSecondary1User0.mOwnerUserId));
375         // Assert that only user 1 files are there.
376         assertPackageDexUsage(null, mBarSecondary2User1);
377     }
378 
379     @Test
testClearUsedByOtherApps()380     public void testClearUsedByOtherApps() {
381         // Write a package which is used by other apps.
382         assertTrue(record(mFooSplit2UsedByOtherApps0));
383         assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
384 
385         // Check that the package is no longer used by other apps.
386         TestData noLongerUsedByOtherApps = new TestData(
387             mFooSplit2UsedByOtherApps0.mPackageName,
388             mFooSplit2UsedByOtherApps0.mDexFile,
389             mFooSplit2UsedByOtherApps0.mOwnerUserId,
390             mFooSplit2UsedByOtherApps0.mLoaderIsa,
391             mFooSplit2UsedByOtherApps0.mPrimaryOrSplit,
392             /*usedBy=*/ null);
393         assertPackageDexUsage(noLongerUsedByOtherApps);
394     }
395 
396     @Test
testClearUsedByOtherAppsNonexistent()397     public void testClearUsedByOtherAppsNonexistent() {
398         // Write a package which is used by other apps.
399         assertTrue(record(mFooSplit2UsedByOtherApps0));
400         assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
401         // Clearing again should return false as there should be no update on the use info.
402         assertFalse(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
403     }
404 
405     @Test
testRecordDexFileUsers()406     public void testRecordDexFileUsers() {
407         PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
408         Set<String> users = new HashSet<>(Arrays.asList(
409                 new String[] {"another.package.1"}));
410         Set<String> usersExtra = new HashSet<>(Arrays.asList(
411                 new String[] {"another.package.2", "another.package.3"}));
412 
413         assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
414         assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
415 
416         assertTrue(record(packageDexUsageRecordUsers, mFooSecondary2UsedByOtherApps0, users));
417         assertTrue(record(packageDexUsageRecordUsers, mFooSecondary2UsedByOtherApps0, usersExtra));
418 
419         packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
420         // Verify that the users were recorded.
421         Set<String> userAll = new HashSet<>(users);
422         userAll.addAll(usersExtra);
423         assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooSplit2UsedByOtherApps0,
424                 mFooSecondary2UsedByOtherApps0);
425     }
426 
427     @Test
testRecordDexFileUsersAndTheOwningPackage()428     public void testRecordDexFileUsersAndTheOwningPackage() {
429         PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
430         Set<String> users = new HashSet<>(Arrays.asList(
431                 new String[] {mFooSplit2UsedByOtherApps0.mPackageName}));
432         Set<String> usersExtra = new HashSet<>(Arrays.asList(
433                 new String[] {"another.package.2", "another.package.3"}));
434 
435         assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
436         assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
437 
438         packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
439 
440         Set<String> expectedUsers = new HashSet<>(users);
441         expectedUsers.addAll(usersExtra);
442         // Verify that all loading packages were recorded.
443         assertPackageDexUsage(
444                 packageDexUsageRecordUsers, expectedUsers, mFooSplit2UsedByOtherApps0);
445     }
446 
447     @Test
testRecordClassLoaderContextVariableContext()448     public void testRecordClassLoaderContextVariableContext() {
449         // Record a secondary dex file.
450         assertTrue(record(mFooSecondary1User0));
451         // Now update its context.
452         TestData fooSecondary1User0NewContext = mFooSecondary1User0.updateClassLoaderContext(
453                 "PCL[new_context.dex]");
454         assertTrue(record(fooSecondary1User0NewContext));
455 
456         // Now check that the context was switch to variable.
457         TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext(
458                 PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT);
459 
460         assertPackageDexUsage(null, expectedContext);
461         writeAndReadBack();
462         assertPackageDexUsage(null, expectedContext);
463     }
464 
465     @Test
testRecordClassLoaderContextOverwritten()466     public void testRecordClassLoaderContextOverwritten() {
467         // Record a secondary dex file.
468         assertTrue(record(mFooSecondary1User0));
469         // Now update its context.
470         TestData fooSecondary1User0NewContext = mFooSecondary1User0.updateClassLoaderContext(
471                 "PCL[new_context.dex]", true);
472         assertTrue(record(fooSecondary1User0NewContext));
473 
474         // Now check that the context was overwritten.
475         TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext(
476                 "PCL[new_context.dex]", true);
477 
478         assertPackageDexUsage(null, expectedContext);
479     }
480 
481     @Test
testDexUsageClassLoaderContext()482     public void testDexUsageClassLoaderContext() {
483         final boolean isUsedByOtherApps = false;
484         final int userId = 0;
485         PackageDexUsage.DexUseInfo validContext = new DexUseInfo(isUsedByOtherApps, userId,
486                 "valid_context", "arm");
487         assertFalse(validContext.isUnsupportedClassLoaderContext());
488         assertFalse(validContext.isVariableClassLoaderContext());
489 
490         PackageDexUsage.DexUseInfo variableContext = new DexUseInfo(isUsedByOtherApps, userId,
491                 PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, "arm");
492         assertFalse(variableContext.isUnsupportedClassLoaderContext());
493         assertTrue(variableContext.isVariableClassLoaderContext());
494     }
495 
496     @Test
testRead()497     public void testRead() {
498         String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
499         // Equivalent to
500         //   record(mFooSplit2UsedByOtherApps0);
501         //   record(mFooSecondary1User0);
502         //   record(mFooSecondary2UsedByOtherApps0);
503         //   record(mBarBaseUser0);
504         //   record(mBarSecondary1User0);
505         String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__2\n"
506                 + "com.google.foo\n"
507                 + "+/data/app/com.google.foo/split-2.apk\n"
508                 + "@used.by.other.com\n"
509                 + "#/data/user/0/com.google.foo/sec-2.dex\n"
510                 + "0,1," + ISA + "\n"
511                 + "@used.by.other.com\n"
512                 + "PCL[/data/user/0/com.google.foo/sec-2.dex]\n"
513                 + "#/data/user/0/com.google.foo/sec-1.dex\n"
514                 + "0,0," + ISA + "\n"
515                 + "@\n"
516                 + "PCL[/data/user/0/com.google.foo/sec-1.dex]\n"
517                 + "com.google.bar\n"
518                 + "+/data/app/com.google.bar/base.apk\n"
519                 + "@com.google.bar\n"
520                 + "#/data/user/0/com.google.bar/sec-1.dex\n"
521                 + "0,0," + ISA + "\n"
522                 + "@\n"
523                 + "PCL[/data/user/0/com.google.bar/sec-1.dex]";
524 
525         PackageDexUsage packageDexUsage = new PackageDexUsage();
526         try {
527             packageDexUsage.read(new StringReader(content));
528         } catch (IOException e) {
529             fail();
530         }
531 
532         // After the read we must sync the data to fill the missing information on the code paths.
533         Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
534         Map<String, Set<String>> packageToCodePaths = new HashMap<>();
535 
536         // Handle foo package.
537         packageToUsersMap.put(
538                 mFooSplit2UsedByOtherApps0.mPackageName,
539                 new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
540         packageToCodePaths.put(
541                 mFooSplit2UsedByOtherApps0.mPackageName,
542                 new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
543                         mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
544         // Handle bar package.
545         packageToUsersMap.put(
546                 mBarBaseUser0.mPackageName,
547                 new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
548         packageToCodePaths.put(
549                 mBarBaseUser0.mPackageName,
550                 new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
551         // Handle the loading package.
552         packageToUsersMap.put(
553                 mFooSplit2UsedByOtherApps0.mUsedBy,
554                 new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
555 
556         // Sync the data.
557         packageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList<>());
558 
559         // Assert foo code paths.
560         assertPackageDexUsage(
561                 packageDexUsage,
562                 /*nonDefaultUsers=*/ null,
563                 mFooSplit2UsedByOtherApps0,
564                 mFooSecondary2UsedByOtherApps0,
565                 mFooSecondary1User0);
566 
567         // Assert bar code paths.
568         assertPackageDexUsage(
569                 packageDexUsage,
570                 /*nonDefaultUsers=*/ null,
571                 mBarBaseUser0,
572                 mBarSecondary1User0);
573     }
574 
575     @Test
testUnsupportedClassLoaderDiscardedOnRead()576     public void testUnsupportedClassLoaderDiscardedOnRead() throws Exception {
577         String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__2\n"
578                 + mBarSecondary1User0.mPackageName + "\n"
579                 + "#" + mBarSecondary1User0.mDexFile + "\n"
580                 + "0,0," + mBarSecondary1User0.mLoaderIsa + "\n"
581                 + "@\n"
582                 + "=UnsupportedClassLoaderContext=\n"
583 
584                 + mFooSecondary1User0.mPackageName + "\n"
585                 + "#" + mFooSecondary1User0.mDexFile + "\n"
586                 + "0,0," + mFooSecondary1User0.mLoaderIsa + "\n"
587                 + "@\n"
588                 + mFooSecondary1User0.mClassLoaderContext + "\n";
589 
590         mPackageDexUsage.read(new StringReader(content));
591 
592         assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0);
593         assertPackageDexUsage(mBarBaseUser0);
594     }
595 
596     @Test
testEnsureLoadingPackagesCanBeExtended()597     public void testEnsureLoadingPackagesCanBeExtended() {
598         String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
599         String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__2\n"
600                 + "com.google.foo\n"
601                 + "+/data/app/com.google.foo/split-2.apk\n"
602                 + "@\n";
603         PackageDexUsage packageDexUsage = new PackageDexUsage();
604         try {
605             packageDexUsage.read(new StringReader(content));
606         } catch (IOException e) {
607             fail();
608         }
609         record(packageDexUsage, mFooSplit2UsedByOtherApps0, mFooSplit2UsedByOtherApps0.getUsedBy());
610     }
611 
assertPackageDexUsage(TestData primary, TestData... secondaries)612     private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
613         assertPackageDexUsage(mPackageDexUsage, null, primary, secondaries);
614     }
615 
assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users, TestData primary, TestData... secondaries)616     private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
617             TestData primary, TestData... secondaries) {
618         assertPackageDexUsage(packageDexUsage, users, primary, Arrays.asList(secondaries));
619     }
620 
assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users, TestData primary, List<TestData> secondaries)621     private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
622             TestData primary, List<TestData> secondaries) {
623         String packageName = primary == null
624                 ? secondaries.get(0).mPackageName
625                 : primary.mPackageName;
626         boolean primaryUsedByOtherApps = primary != null && primary.isUsedByOtherApps();
627         PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
628 
629         // Check package use info
630         assertNotNull(pInfo);
631         if (primary != null) {
632             if (users != null) {
633                 assertEquals(pInfo.getLoadingPackages(primary.mDexFile), users);
634             } else if (pInfo.getLoadingPackages(primary.mDexFile) != null) {
635                 assertEquals(pInfo.getLoadingPackages(primary.mDexFile), primary.getUsedBy());
636             }
637             assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile));
638         }
639 
640         Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
641         assertEquals(secondaries.size(), dexUseInfoMap.size());
642 
643         // Check dex use info
644         for (TestData testData : secondaries) {
645             DexUseInfo dInfo = dexUseInfoMap.get(testData.mDexFile);
646             assertNotNull(dInfo);
647             if (users != null) {
648                 assertEquals(testData.mDexFile, dInfo.getLoadingPackages(), users);
649             } else {
650                 assertEquals(testData.mDexFile, dInfo.getLoadingPackages(), testData.getUsedBy());
651             }
652             assertEquals(testData.isUsedByOtherApps(), dInfo.isUsedByOtherApps());
653             assertEquals(testData.mOwnerUserId, dInfo.getOwnerUserId());
654             assertEquals(1, dInfo.getLoaderIsas().size());
655             assertTrue(dInfo.getLoaderIsas().contains(testData.mLoaderIsa));
656 
657             assertEquals(testData.mClassLoaderContext, dInfo.getClassLoaderContext());
658         }
659     }
660 
record(TestData testData)661     private boolean record(TestData testData) {
662         return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile,
663                 testData.mOwnerUserId, testData.mLoaderIsa,
664                 testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext,
665                 testData.mOverwriteCLC);
666     }
667 
record(PackageDexUsage packageDexUsage, TestData testData, Set<String> users)668     private boolean record(PackageDexUsage packageDexUsage, TestData testData, Set<String> users) {
669         boolean result = true;
670         for (String user : users) {
671             result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile,
672                     testData.mOwnerUserId, testData.mLoaderIsa,
673                     testData.mPrimaryOrSplit, user, testData.mClassLoaderContext,
674                     testData.mOverwriteCLC);
675         }
676         return result;
677     }
678 
writeAndReadBack()679     private void writeAndReadBack() {
680         mPackageDexUsage = writeAndReadBack(mPackageDexUsage);
681     }
682 
writeAndReadBack(PackageDexUsage packageDexUsage)683     private PackageDexUsage writeAndReadBack(PackageDexUsage packageDexUsage) {
684         try {
685             StringWriter writer = new StringWriter();
686             packageDexUsage.write(writer);
687 
688             PackageDexUsage newPackageDexUsage = new PackageDexUsage();
689             newPackageDexUsage.read(new StringReader(writer.toString()));
690             return newPackageDexUsage;
691         } catch (IOException e) {
692             fail("Unexpected IOException: " + e.getMessage());
693             return null;
694         }
695     }
696 
697     private static class TestData {
698         private final String mPackageName;
699         private final String mDexFile;
700         private final int mOwnerUserId;
701         private final String mLoaderIsa;
702         private final boolean mPrimaryOrSplit;
703         private final String mUsedBy;
704         private final String mClassLoaderContext;
705         private final boolean mOverwriteCLC;
706 
TestData(String packageName, String dexFile, int ownerUserId, String loaderIsa, boolean primaryOrSplit, String usedBy)707         private TestData(String packageName, String dexFile, int ownerUserId,
708                 String loaderIsa, boolean primaryOrSplit, String usedBy) {
709             this(packageName, dexFile, ownerUserId, loaderIsa, primaryOrSplit,
710                     usedBy, "PCL[" + dexFile + "]", false);
711         }
TestData(String packageName, String dexFile, int ownerUserId, String loaderIsa, boolean primaryOrSplit, String usedBy, String classLoaderContext, boolean overwriteCLC)712         private TestData(String packageName, String dexFile, int ownerUserId,
713                 String loaderIsa, boolean primaryOrSplit, String usedBy,
714                 String classLoaderContext, boolean overwriteCLC) {
715             mPackageName = packageName;
716             mDexFile = dexFile;
717             mOwnerUserId = ownerUserId;
718             mLoaderIsa = loaderIsa;
719             mPrimaryOrSplit = primaryOrSplit;
720             mUsedBy = usedBy;
721             mClassLoaderContext = classLoaderContext;
722             mOverwriteCLC = overwriteCLC;
723         }
724 
updateClassLoaderContext(String newContext)725         private TestData updateClassLoaderContext(String newContext) {
726             return updateClassLoaderContext(newContext, mOverwriteCLC);
727         }
728 
updateClassLoaderContext(String newContext, boolean overwriteCLC)729         private TestData updateClassLoaderContext(String newContext, boolean overwriteCLC) {
730             return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa,
731                     mPrimaryOrSplit, mUsedBy, newContext, overwriteCLC);
732         }
733 
updateUsedBy(String newUsedBy)734         private TestData updateUsedBy(String newUsedBy) {
735             return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa,
736                 mPrimaryOrSplit, newUsedBy, mClassLoaderContext, mOverwriteCLC);
737         }
738 
isUsedByOtherApps()739         private boolean isUsedByOtherApps() {
740             return mUsedBy != null && !mPackageName.equals(mUsedBy);
741         }
742 
getUsedBy()743         private Set<String> getUsedBy() {
744             Set<String> users = new HashSet<>();
745             if ((mUsedBy != null) && (mPrimaryOrSplit || isUsedByOtherApps())) {
746                 // We do not store the loading package for secondary dex files
747                 // which are not used by others.
748                 users.add(mUsedBy);
749             }
750             return users;
751         }
752     }
753 }
754