/*
* Copyright (C) 2012 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;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import android.content.Context;
import android.content.pm.PackageParser;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.test.mock.MockContext;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LongSparseArray;
import com.android.internal.os.AtomicFile;
import java.lang.reflect.Constructor;
import org.mockito.Mock;
import org.mockito.Mockito;
import com.android.internal.R;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.PublicKey;
public class PackageManagerSettingsTests extends AndroidTestCase {
private static final String PACKAGE_NAME_2 = "com.google.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
private static final String PACKAGE_NAME_1 = "com.google.app1";
private static final boolean localLOGV = true;
public static final String TAG = "PackageManagerSettingsTests";
protected final String PREFIX = "android.content.pm";
private void writeFile(File file, byte[] data) {
file.mkdirs();
try {
AtomicFile aFile = new AtomicFile(file);
FileOutputStream fos = aFile.startWrite();
fos.write(data);
aFile.finishWrite(fos);
} catch (IOException ioe) {
Log.e(TAG, "Cannot write file " + file.getPath());
}
}
private void writePackagesXml() {
writeFile(new File(getContext().getFilesDir(), "system/packages.xml"),
(""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ " "
+ " "
+ " "
+ " "
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ " "
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "").getBytes());
}
private void writeStoppedPackagesXml() {
writeFile(new File(getContext().getFilesDir(), "system/packages-stopped.xml"),
( ""
+ ""
+ ""
+ ""
+ "")
.getBytes());
}
private void writePackagesList() {
writeFile(new File(getContext().getFilesDir(), "system/packages.list"),
( "com.google.app1 11000 0 /data/data/com.google.app1 seinfo1"
+ "com.google.app2 11001 0 /data/data/com.google.app2 seinfo2"
+ "com.android.app3 11030 0 /data/data/com.android.app3 seinfo3")
.getBytes());
}
private void deleteSystemFolder() {
File systemFolder = new File(getContext().getFilesDir(), "system");
deleteFolder(systemFolder);
}
private static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File file : files) {
deleteFolder(file);
}
}
folder.delete();
}
@Override
protected void setUp() throws Exception {
super.setUp();
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
}
private void writeOldFiles() {
deleteSystemFolder();
writePackagesXml();
writeStoppedPackagesXml();
writePackagesList();
}
private void createUserManagerServiceRef() throws ReflectiveOperationException {
Constructor umsc =
UserManagerService.class.getDeclaredConstructor(
Context.class,
PackageManagerService.class,
Object.class,
Object.class,
File.class,
File.class);
umsc.setAccessible(true);
UserManagerService ums = umsc.newInstance(getContext(), null,
new Object(), new Object(), getContext().getFilesDir(),
new File(getContext().getFilesDir(), "user"));
}
private void verifyKeySetMetaData(Settings settings)
throws ReflectiveOperationException, IllegalAccessException {
ArrayMap packages = settings.mPackages;
KeySetManagerService ksms = settings.mKeySetManagerService;
/* verify keyset and public key ref counts */
assertEquals(2, KeySetUtils.getKeySetRefCount(ksms, 1));
assertEquals(1, KeySetUtils.getKeySetRefCount(ksms, 2));
assertEquals(1, KeySetUtils.getKeySetRefCount(ksms, 3));
assertEquals(1, KeySetUtils.getKeySetRefCount(ksms, 4));
assertEquals(2, KeySetUtils.getPubKeyRefCount(ksms, 1));
assertEquals(2, KeySetUtils.getPubKeyRefCount(ksms, 2));
assertEquals(1, KeySetUtils.getPubKeyRefCount(ksms, 3));
/* verify public keys properly read */
PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
PublicKey keyC = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
assertEquals(keyA, KeySetUtils.getPubKey(ksms, 1));
assertEquals(keyB, KeySetUtils.getPubKey(ksms, 2));
assertEquals(keyC, KeySetUtils.getPubKey(ksms, 3));
/* verify mapping is correct (ks -> pub keys) */
LongSparseArray> ksMapping = KeySetUtils.getKeySetMapping(ksms);
ArraySet mapping = ksMapping.get(1);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(1)));
mapping = ksMapping.get(2);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(2)));
mapping = ksMapping.get(3);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(3)));
mapping = ksMapping.get(4);
assertEquals(2, mapping.size());
assertTrue(mapping.contains(new Long(1)));
assertTrue(mapping.contains(new Long(2)));
/* verify lastIssuedIds are consistent */
assertEquals(new Long(3), KeySetUtils.getLastIssuedKeyId(ksms));
assertEquals(new Long(4), KeySetUtils.getLastIssuedKeySetId(ksms));
/* verify packages have been given the appropriat information */
PackageSetting ps = packages.get("com.google.app1");
assertEquals(1, ps.keySetData.getProperSigningKeySet());
ps = packages.get("com.google.app2");
assertEquals(1, ps.keySetData.getProperSigningKeySet());
assertEquals(new Long(4), ps.keySetData.getAliases().get("AB"));
ps = packages.get("com.android.app3");
assertEquals(2, ps.keySetData.getProperSigningKeySet());
assertEquals(new Long(3), ps.keySetData.getAliases().get("C"));
assertEquals(1, ps.keySetData.getUpgradeKeySets().length);
assertEquals(3, ps.keySetData.getUpgradeKeySets()[0]);
}
/* make sure our initialized keysetmanagerservice metadata matches packages.xml */
public void testReadKeySetSettings()
throws ReflectiveOperationException, IllegalAccessException {
/* write out files and read */
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false, null));
verifyKeySetMetaData(settings);
}
/* read in data, write it out, and read it back in. Verify same. */
public void testWriteKeySetSettings()
throws ReflectiveOperationException, IllegalAccessException {
/* write out files and read */
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false, null));
/* write out, read back in and verify the same */
settings.writeLPr();
assertEquals(true, settings.readLPw(null, null, 0, false, null));
verifyKeySetMetaData(settings);
}
public void testSettingsReadOld() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false, null));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_1);
assertEquals(COMPONENT_ENABLED_STATE_DEFAULT, ps.getEnabled(0));
assertEquals(true, ps.getNotLaunched(0));
ps = settings.peekPackageLPr(PACKAGE_NAME_2);
assertEquals(false, ps.getStopped(0));
assertEquals(COMPONENT_ENABLED_STATE_DISABLED_USER, ps.getEnabled(0));
assertEquals(COMPONENT_ENABLED_STATE_DEFAULT, ps.getEnabled(1));
}
public void testNewPackageRestrictionsFile() throws ReflectiveOperationException {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false, null));
settings.writeLPr();
// Create Settings again to make it read from the new files
settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false, null));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
assertEquals(COMPONENT_ENABLED_STATE_DISABLED_USER, ps.getEnabled(0));
assertEquals(COMPONENT_ENABLED_STATE_DEFAULT, ps.getEnabled(1));
}
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false, null));
// Enable/Disable a package
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_1);
ps.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null);
ps.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 1, null);
assertEquals(COMPONENT_ENABLED_STATE_DISABLED, ps.getEnabled(0));
assertEquals(COMPONENT_ENABLED_STATE_ENABLED, ps.getEnabled(1));
// Enable/Disable a component
ArraySet components = new ArraySet();
String component1 = PACKAGE_NAME_1 + "/.Component1";
components.add(component1);
ps.setDisabledComponents(components, 0);
ArraySet componentsDisabled = ps.getDisabledComponents(0);
assertEquals(1, componentsDisabled.size());
assertEquals(component1, componentsDisabled.toArray()[0]);
boolean hasEnabled =
ps.getEnabledComponents(0) != null && ps.getEnabledComponents(1).size() > 0;
assertEquals(false, hasEnabled);
// User 1 should not have any disabled components
boolean hasDisabled =
ps.getDisabledComponents(1) != null && ps.getDisabledComponents(1).size() > 0;
assertEquals(false, hasDisabled);
ps.setEnabledComponents(components, 1);
assertEquals(1, ps.getEnabledComponents(1).size());
hasEnabled = ps.getEnabledComponents(0) != null && ps.getEnabledComponents(0).size() > 0;
assertEquals(false, hasEnabled);
}
// Checks if a package that is locked to a different region is rejected
// from being installed
public void testPrebundledDifferentRegionReject() {
Settings settings = new Settings(getContext().getFilesDir());
String expectedPackageNeededForRegion = "org.cyanogenmod.restricted.package";
Resources resources = Mockito.mock(Resources.class);
String[] regionRestrictedPackages = new String[] {
expectedPackageNeededForRegion
};
Mockito.when(resources.getStringArray(R.array.config_restrict_to_region_locked_devices))
.thenReturn(regionRestrictedPackages);
assertFalse(settings.shouldPrebundledPackageBeInstalledForRegion(resources,
expectedPackageNeededForRegion, resources));
}
// Checks if a package that is locked to the current region is accepted
// This also covers the test for a package that needs to be installed on a
// non region locked device
public void testPrebundledMatchingRegionAccept() {
Settings settings = new Settings(getContext().getFilesDir());
String expectedPackageNeededForRegion = "org.cyanogenmod.restricted.package";
Resources resources = Mockito.mock(Resources.class);
String[] regionLockedPackages = new String[] {
expectedPackageNeededForRegion
};
Mockito.when(resources.getStringArray(R.array.config_region_locked_packages))
.thenReturn(regionLockedPackages);
Mockito.when(resources.getStringArray(R.array.config_restrict_to_region_locked_devices))
.thenReturn(regionLockedPackages);
assertTrue(settings.shouldPrebundledPackageBeInstalledForRegion(resources,
expectedPackageNeededForRegion, resources));
}
// Shamelessly kanged from KeySetManagerServiceTest
public PackageSetting generateFakePackageSetting(String name) {
return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
"", 1, 0, 0);
}
// Checks if a package that was installed and currently isn't installed for the owner
// is accepted for a secondary user
public void testPrebundledSecondaryUserAccept() {
Settings settings = new Settings(getContext().getFilesDir());
String expectedPackageToBeInstalled = "org.cyanogenmod.secondaryuser.package";
PackageSetting packageSetting =
generateFakePackageSetting(expectedPackageToBeInstalled);
int userOwner = UserHandle.USER_OWNER;
int userSecondary = 1000;
// Return true that the package was installed for the owner at some point
settings.markPrebundledPackageInstalledLPr(userOwner, expectedPackageToBeInstalled);
assertTrue(settings.wasPrebundledPackageInstalledLPr(userOwner,
expectedPackageToBeInstalled));
// Return false that the package was installed for the secondary user at some point
// DON'T MARK PREBUNDLED PACKAGE INSTALLED
// Return false that the package is currently not installed for the owner
packageSetting.setInstalled(false, userOwner);
assertFalse(packageSetting.getInstalled(userOwner));
// Return false that the package is currently not installed for the secondary user
packageSetting.setInstalled(false, userSecondary);
assertFalse(packageSetting.getInstalled(userSecondary));
assertFalse(settings.shouldPrebundledPackageBeInstalledForUserLPr(packageSetting,
userSecondary, expectedPackageToBeInstalled));
}
// Checks if a package that was installed for a secondary user and currently isn't installed
// for the user is accepted to be reinstalled
public void testPrebundledSecondaryUserReinstallAccept() {
Settings settings = new Settings(getContext().getFilesDir());
String expectedPackageToBeInstalled = "org.cyanogenmod.secondaryuser.package";
PackageSetting packageSetting =
generateFakePackageSetting(expectedPackageToBeInstalled);
int userSecondary = 1000;
// Return true that the package was installed for the secondary user at some point
settings.markPrebundledPackageInstalledLPr(userSecondary, expectedPackageToBeInstalled);
assertTrue(settings.wasPrebundledPackageInstalledLPr(userSecondary,
expectedPackageToBeInstalled));
// Return false that the package is currently not installed for the secondary user
packageSetting.setInstalled(false, userSecondary);
assertFalse(packageSetting.getInstalled(userSecondary));
assertFalse(settings.shouldPrebundledPackageBeInstalledForUserLPr(packageSetting,
userSecondary, expectedPackageToBeInstalled));
}
}