blob: 2d07e651fdd15884913a9ac0553bd430015113bb [file] [log] [blame]
/*
* 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.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import dalvik.system.VMRuntime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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 DexManagerTests {
private DexManager mDexManager;
private TestData mFooUser0;
private TestData mBarUser0;
private TestData mBarUser1;
private TestData mInvalidIsa;
private TestData mDoesNotExist;
private int mUser0;
private int mUser1;
@Before
public void setup() {
mUser0 = 0;
mUser1 = 1;
String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
String foo = "foo";
String bar = "bar";
mFooUser0 = new TestData(foo, isa, mUser0);
mBarUser0 = new TestData(bar, isa, mUser0);
mBarUser1 = new TestData(bar, isa, mUser1);
mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
mDexManager = new DexManager();
// Foo and Bar are available to user0.
// Only Bar is available to user1;
Map<Integer, List<PackageInfo>> existingPackages = new HashMap<>();
existingPackages.put(mUser0, Arrays.asList(mFooUser0.mPackageInfo, mBarUser0.mPackageInfo));
existingPackages.put(mUser1, Arrays.asList(mBarUser1.mPackageInfo));
mDexManager.load(existingPackages);
}
@Test
public void testNotifyPrimaryUse() {
// The main dex file and splits are re-loaded by the app.
notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
// Package is not used by others, so we should get nothing back.
assertNull(getPackageUseInfo(mFooUser0));
}
@Test
public void testNotifyPrimaryForeignUse() {
// Foo loads Bar main apks.
notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
// Bar is used by others now and should be in our records
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertNotNull(pui);
assertTrue(pui.isUsedByOtherApps());
assertTrue(pui.getDexUseInfoMap().isEmpty());
}
@Test
public void testNotifySecondary() {
// Foo loads its own secondary files.
List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
assertNotNull(pui);
assertFalse(pui.isUsedByOtherApps());
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@Test
public void testNotifySecondaryForeign() {
// Foo loads bar secondary files.
List<String> barSecondaries = mBarUser0.getSecondaryDexPaths();
notifyDexLoad(mFooUser0, barSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertNotNull(pui);
assertFalse(pui.isUsedByOtherApps());
assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
}
@Test
public void testNotifySequence() {
// Foo loads its own secondary files.
List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
// Foo loads Bar own secondary files.
List<String> barSecondaries = mBarUser0.getSecondaryDexPaths();
notifyDexLoad(mFooUser0, barSecondaries, mUser0);
// Foo loads Bar primary files.
notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
// Bar loads its own secondary files.
notifyDexLoad(mBarUser0, barSecondaries, mUser0);
// Bar loads some own secondary files which foo didn't load.
List<String> barSecondariesForOwnUse = mBarUser0.getSecondaryDexPathsForOwnUse();
notifyDexLoad(mBarUser0, barSecondariesForOwnUse, mUser0);
// Check bar usage. Should be used by other app (for primary and barSecondaries).
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertNotNull(pui);
assertTrue(pui.isUsedByOtherApps());
assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
assertSecondaryUse(mFooUser0, pui, barSecondariesForOwnUse,
/*isUsedByOtherApps*/false, mUser0);
// Check foo usage. Should not be used by other app.
pui = getPackageUseInfo(mFooUser0);
assertNotNull(pui);
assertFalse(pui.isUsedByOtherApps());
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@Test
public void testPackageUseInfoNotFound() {
// Assert we don't get back data we did not previously record.
assertNull(getPackageUseInfo(mFooUser0));
}
@Test
public void testInvalidIsa() {
// Notifying with an invalid ISA should be ignored.
notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
assertNull(getPackageUseInfo(mInvalidIsa));
}
@Test
public void testNotExistingPackate() {
// Notifying about the load of a package which was previously not
// register in DexManager#load should be ignored.
notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
assertNull(getPackageUseInfo(mDoesNotExist));
}
@Test
public void testCrossUserAttempt() {
// Bar from User1 tries to load secondary dex files from User0 Bar.
// Request should be ignored.
notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
assertNull(getPackageUseInfo(mBarUser1));
}
@Test
public void testPackageNotInstalledForUser() {
// User1 tries to load Foo which is installed for User0 but not for User1.
// Note that the PackageManagerService already filters this out but we
// still check that nothing goes unexpected in DexManager.
notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
assertNull(getPackageUseInfo(mBarUser1));
}
@Test
public void testNotifyPackageInstallUsedByOther() {
TestData newPackage = new TestData("newPackage",
VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
List<String> newSecondaries = newPackage.getSecondaryDexPaths();
// Before we notify about the installation of the newPackage if mFoo
// is trying to load something from it we should not find it.
notifyDexLoad(mFooUser0, newSecondaries, mUser0);
assertNull(getPackageUseInfo(newPackage));
// Notify about newPackage install and let mFoo load its dexes.
mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
notifyDexLoad(mFooUser0, newSecondaries, mUser0);
// We should get back the right info.
PackageUseInfo pui = getPackageUseInfo(newPackage);
assertNotNull(pui);
assertFalse(pui.isUsedByOtherApps());
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
}
@Test
public void testNotifyPackageInstallSelfUse() {
TestData newPackage = new TestData("newPackage",
VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
List<String> newSecondaries = newPackage.getSecondaryDexPaths();
// Packages should be able to find their own dex files even if the notification about
// their installation is delayed.
notifyDexLoad(newPackage, newSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(newPackage);
assertNotNull(pui);
assertFalse(pui.isUsedByOtherApps());
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
for (String dex : secondaries) {
DexUseInfo dui = pui.getDexUseInfoMap().get(dex);
assertNotNull(dui);
assertEquals(isUsedByOtherApps, dui.isUsedByOtherApps());
assertEquals(ownerUserId, dui.getOwnerUserId());
assertEquals(1, dui.getLoaderIsas().size());
assertTrue(dui.getLoaderIsas().contains(testData.mLoaderIsa));
}
}
private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, dexPaths,
testData.mLoaderIsa, loaderUserId);
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
}
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
PackageInfo pi = new PackageInfo();
pi.packageName = packageName;
pi.applicationInfo = getMockApplicationInfo(packageName, userId);
return pi;
}
private static ApplicationInfo getMockApplicationInfo(String packageName, int userId) {
ApplicationInfo ai = new ApplicationInfo();
String codeDir = "/data/app/" + packageName;
ai.setBaseCodePath(codeDir + "/base.dex");
ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"});
ai.dataDir = "/data/user/" + userId + "/" + packageName;
ai.packageName = packageName;
return ai;
}
private static class TestData {
private final PackageInfo mPackageInfo;
private final String mLoaderIsa;
private TestData(String packageName, String loaderIsa, int userId) {
mPackageInfo = getMockPackageInfo(packageName, userId);
mLoaderIsa = loaderIsa;
}
private String getPackageName() {
return mPackageInfo.packageName;
}
List<String> getSecondaryDexPaths() {
List<String> paths = new ArrayList<>();
paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary1.dex");
paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary2.dex");
paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary3.dex");
return paths;
}
List<String> getSecondaryDexPathsForOwnUse() {
List<String> paths = new ArrayList<>();
paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary4.dex");
paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary5.dex");
return paths;
}
List<String> getBaseAndSplitDexPaths() {
List<String> paths = new ArrayList<>();
paths.add(mPackageInfo.applicationInfo.sourceDir);
for (String split : mPackageInfo.applicationInfo.splitSourceDirs) {
paths.add(split);
}
return paths;
}
}
}