/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.dex; import android.os.Build; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import dalvik.system.VMRuntime; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; @RunWith(AndroidJUnit4.class) @SmallTest public class PackageDexUsageTests { private PackageDexUsage mPackageDexUsage; private TestData mFooBaseUser0; private TestData mFooSplit1User0; private TestData mFooSplit2UsedByOtherApps0; private TestData mFooSecondary1User0; private TestData mFooSecondary1User1; private TestData mFooSecondary2UsedByOtherApps0; private TestData mInvalidIsa; private TestData mBarBaseUser0; private TestData mBarSecondary1User0; private TestData mBarSecondary2User1; @Before public void setup() { mPackageDexUsage = new PackageDexUsage(); String fooPackageName = "com.google.foo"; String fooCodeDir = "/data/app/com.google.foo/"; String fooDataDir = "/data/user/0/com.google.foo/"; String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]); mFooBaseUser0 = new TestData(fooPackageName, fooCodeDir + "base.apk", 0, isa, false, true); mFooSplit1User0 = new TestData(fooPackageName, fooCodeDir + "split-1.apk", 0, isa, false, true); mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName, fooCodeDir + "split-2.apk", 0, isa, true, true); mFooSecondary1User0 = new TestData(fooPackageName, fooDataDir + "sec-1.dex", 0, isa, false, false); mFooSecondary1User1 = new TestData(fooPackageName, fooDataDir + "sec-1.dex", 1, isa, false, false); mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName, fooDataDir + "sec-2.dex", 0, isa, true, false); mInvalidIsa = new TestData(fooPackageName, fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true); String barPackageName = "com.google.bar"; String barCodeDir = "/data/app/com.google.bar/"; String barDataDir = "/data/user/0/com.google.bar/"; String barDataDir1 = "/data/user/1/com.google.bar/"; mBarBaseUser0 = new TestData(barPackageName, barCodeDir + "base.apk", 0, isa, false, true); mBarSecondary1User0 = new TestData(barPackageName, barDataDir + "sec-1.dex", 0, isa, false, false); mBarSecondary2User1 = new TestData(barPackageName, barDataDir1 + "sec-2.dex", 1, isa, false, false); } @Test public void testRecordPrimary() { // Assert new information. assertTrue(record(mFooBaseUser0)); assertPackageDexUsage(mFooBaseUser0); writeAndReadBack(); assertPackageDexUsage(mFooBaseUser0); } @Test public void testRecordSplit() { // Assert new information. assertTrue(record(mFooSplit1User0)); assertPackageDexUsage(mFooSplit1User0); writeAndReadBack(); assertPackageDexUsage(mFooSplit1User0); } @Test public void testRecordSplitPrimarySequence() { // Assert new information. assertTrue(record(mFooBaseUser0)); // Assert no new information. assertFalse(record(mFooSplit1User0)); assertPackageDexUsage(mFooBaseUser0); writeAndReadBack(); assertPackageDexUsage(mFooBaseUser0); // Write Split2 which is used by other apps. // Assert new information. assertTrue(record(mFooSplit2UsedByOtherApps0)); assertPackageDexUsage(mFooSplit2UsedByOtherApps0); writeAndReadBack(); assertPackageDexUsage(mFooSplit2UsedByOtherApps0); } @Test public void testRecordSecondary() { assertTrue(record(mFooSecondary1User0)); assertPackageDexUsage(null, mFooSecondary1User0); writeAndReadBack(); assertPackageDexUsage(null, mFooSecondary1User0); // Recording again does not add more data. assertFalse(record(mFooSecondary1User0)); assertPackageDexUsage(null, mFooSecondary1User0); } @Test public void testRecordBaseAndSecondarySequence() { // Write split. assertTrue(record(mFooSplit2UsedByOtherApps0)); // Write secondary. assertTrue(record(mFooSecondary1User0)); // Check. assertPackageDexUsage(mFooSplit2UsedByOtherApps0, mFooSecondary1User0); writeAndReadBack(); assertPackageDexUsage(mFooSplit2UsedByOtherApps0, mFooSecondary1User0); // Write another secondary. assertTrue(record(mFooSecondary2UsedByOtherApps0)); // Check. assertPackageDexUsage( mFooSplit2UsedByOtherApps0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0); writeAndReadBack(); assertPackageDexUsage( mFooSplit2UsedByOtherApps0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0); } @Test public void testMultiplePackages() { assertTrue(record(mFooBaseUser0)); assertTrue(record(mFooSecondary1User0)); assertTrue(record(mFooSecondary2UsedByOtherApps0)); assertTrue(record(mBarBaseUser0)); assertTrue(record(mBarSecondary1User0)); assertTrue(record(mBarSecondary2User1)); assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0); assertPackageDexUsage(mBarBaseUser0, mBarSecondary1User0, mBarSecondary2User1); writeAndReadBack(); assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0); assertPackageDexUsage(mBarBaseUser0, mBarSecondary1User0, mBarSecondary2User1); } @Test public void testPackageNotFound() { assertNull(mPackageDexUsage.getPackageUseInfo("missing.package")); } @Test public void testAttemptToChangeOwner() { assertTrue(record(mFooSecondary1User0)); try { record(mFooSecondary1User1); fail("Expected exception"); } catch (IllegalArgumentException e) { // expected } } @Test public void testInvalidIsa() { try { record(mInvalidIsa); fail("Expected exception"); } catch (IllegalArgumentException e) { // expected } } @Test public void testReadWriteEmtpy() { // Expect no exceptions when writing/reading without data. writeAndReadBack(); } @Test public void testSyncData() { // Write some records. assertTrue(record(mFooBaseUser0)); assertTrue(record(mFooSecondary1User0)); assertTrue(record(mFooSecondary2UsedByOtherApps0)); assertTrue(record(mBarBaseUser0)); assertTrue(record(mBarSecondary1User0)); assertTrue(record(mBarSecondary2User1)); // Verify all is good. assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0); assertPackageDexUsage(mBarBaseUser0, mBarSecondary1User0, mBarSecondary2User1); writeAndReadBack(); assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0, mFooSecondary2UsedByOtherApps0); assertPackageDexUsage(mBarBaseUser0, mBarSecondary1User0, mBarSecondary2User1); // Simulate that only user 1 is available. Map> packageToUsersMap = new HashMap<>(); packageToUsersMap.put(mBarSecondary2User1.mPackageName, new HashSet<>(Arrays.asList(mBarSecondary2User1.mOwnerUserId))); mPackageDexUsage.syncData(packageToUsersMap); // Assert that only user 1 files are there. assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1); assertNull(mPackageDexUsage.getPackageUseInfo(mFooBaseUser0.mPackageName)); } @Test public void testRemovePackage() { // Record Bar secondaries for two different users. assertTrue(record(mBarSecondary1User0)); assertTrue(record(mBarSecondary2User1)); // Remove the package. assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName)); // Assert that we can't find the package anymore. assertNull(mPackageDexUsage.getPackageUseInfo(mBarSecondary1User0.mPackageName)); } @Test public void testRemoveNonexistentPackage() { // Record Bar secondaries for two different users. assertTrue(record(mBarSecondary1User0)); // Remove the package. assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName)); // Remove the package again. It should return false because the package no longer // has a record in the use info. assertFalse(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName)); } @Test public void testRemoveUserPackage() { // Record Bar secondaries for two different users. assertTrue(record(mBarSecondary1User0)); assertTrue(record(mBarSecondary2User1)); // Remove user 0 files. assertTrue(mPackageDexUsage.removeUserPackage(mBarSecondary1User0.mPackageName, mBarSecondary1User0.mOwnerUserId)); // Assert that only user 1 files are there. assertPackageDexUsage(null, mBarSecondary2User1); } @Test public void testRemoveDexFile() { // Record Bar secondaries for two different users. assertTrue(record(mBarSecondary1User0)); assertTrue(record(mBarSecondary2User1)); // Remove mBarSecondary1User0 file. assertTrue(mPackageDexUsage.removeDexFile(mBarSecondary1User0.mPackageName, mBarSecondary1User0.mDexFile, mBarSecondary1User0.mOwnerUserId)); // Assert that only user 1 files are there. assertPackageDexUsage(null, mBarSecondary2User1); } @Test public void testClearUsedByOtherApps() { // Write a package which is used by other apps. assertTrue(record(mFooSplit2UsedByOtherApps0)); assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName)); // Check that the package is no longer used by other apps. TestData noLongerUsedByOtherApps = new TestData( mFooSplit2UsedByOtherApps0.mPackageName, mFooSplit2UsedByOtherApps0.mDexFile, mFooSplit2UsedByOtherApps0.mOwnerUserId, mFooSplit2UsedByOtherApps0.mLoaderIsa, /*mIsUsedByOtherApps*/false, mFooSplit2UsedByOtherApps0.mPrimaryOrSplit); assertPackageDexUsage(noLongerUsedByOtherApps); } @Test public void testClearUsedByOtherAppsNonexistent() { // Write a package which is used by other apps. assertTrue(record(mFooSplit2UsedByOtherApps0)); assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName)); // Clearing again should return false as there should be no update on the use info. assertFalse(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName)); } private void assertPackageDexUsage(TestData primary, TestData... secondaries) { String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName; boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps; PackageUseInfo pInfo = mPackageDexUsage.getPackageUseInfo(packageName); // Check package use info assertNotNull(pInfo); assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps()); Map dexUseInfoMap = pInfo.getDexUseInfoMap(); assertEquals(secondaries.length, dexUseInfoMap.size()); // Check dex use info for (TestData testData : secondaries) { DexUseInfo dInfo = dexUseInfoMap.get(testData.mDexFile); assertNotNull(dInfo); assertEquals(testData.mUsedByOtherApps, dInfo.isUsedByOtherApps()); assertEquals(testData.mOwnerUserId, dInfo.getOwnerUserId()); assertEquals(1, dInfo.getLoaderIsas().size()); assertTrue(dInfo.getLoaderIsas().contains(testData.mLoaderIsa)); } } private boolean record(TestData testData) { return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile, testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps, testData.mPrimaryOrSplit); } private void writeAndReadBack() { try { StringWriter writer = new StringWriter(); mPackageDexUsage.write(writer); mPackageDexUsage = new PackageDexUsage(); mPackageDexUsage.read(new StringReader(writer.toString())); } catch (IOException e) { fail("Unexpected IOException: " + e.getMessage()); } } private static class TestData { private final String mPackageName; private final String mDexFile; private final int mOwnerUserId; private final String mLoaderIsa; private final boolean mUsedByOtherApps; private final boolean mPrimaryOrSplit; private TestData(String packageName, String dexFile, int ownerUserId, String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit) { mPackageName = packageName; mDexFile = dexFile; mOwnerUserId = ownerUserId; mLoaderIsa = loaderIsa; mUsedByOtherApps = isUsedByOtherApps; mPrimaryOrSplit = primaryOrSplit; } } }