| /* |
| * 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<String, Set<Integer>> 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<String, DexUseInfo> 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; |
| } |
| |
| } |
| } |