/* * Copyright (C) 2006 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 android.content.pm; import com.android.frameworks.coretests.R; import com.android.internal.content.PackageHelper; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.net.Uri; import android.os.Environment; import android.os.FileUtils; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; import android.os.storage.IMountService; import android.os.storage.StorageListener; import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.util.DisplayMetrics; import android.util.Log; import java.io.File; import java.io.IOException; import java.io.InputStream; public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; public static final String TAG="PackageManagerTests"; public final long MAX_WAIT_TIME = 25*1000; public final long WAIT_TIME_INCR = 5*1000; private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec"; private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO; private static final int APP_INSTALL_DEVICE = PackageHelper.APP_INSTALL_INTERNAL; private static final int APP_INSTALL_SDCARD = PackageHelper.APP_INSTALL_EXTERNAL; private boolean mOrigState; void failStr(String errMsg) { Log.w(TAG, "errMsg="+errMsg); fail(errMsg); } void failStr(Exception e) { failStr(e.getMessage()); } @Override protected void setUp() throws Exception { super.setUp(); mOrigState = checkMediaState(Environment.MEDIA_MOUNTED); if (!mountMedia()) { Log.i(TAG, "sdcard not mounted? Some of these tests might fail"); } } @Override protected void tearDown() throws Exception { // Restore media state. boolean newState = checkMediaState(Environment.MEDIA_MOUNTED); if (newState != mOrigState) { if (mOrigState) { mountMedia(); } else { unmountMedia(); } } super.tearDown(); } private class PackageInstallObserver extends IPackageInstallObserver.Stub { public int returnCode; private boolean doneFlag = false; public void packageInstalled(String packageName, int returnCode) { synchronized(this) { this.returnCode = returnCode; doneFlag = true; notifyAll(); } } public boolean isDone() { return doneFlag; } } abstract class GenericReceiver extends BroadcastReceiver { private boolean doneFlag = false; boolean received = false; Intent intent; IntentFilter filter; abstract boolean notifyNow(Intent intent); @Override public void onReceive(Context context, Intent intent) { if (notifyNow(intent)) { synchronized (this) { received = true; doneFlag = true; this.intent = intent; notifyAll(); } } } public boolean isDone() { return doneFlag; } public void setFilter(IntentFilter filter) { this.filter = filter; } } class InstallReceiver extends GenericReceiver { String pkgName; InstallReceiver(String pkgName) { this.pkgName = pkgName; IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addDataScheme("package"); super.setFilter(filter); } public boolean notifyNow(Intent intent) { String action = intent.getAction(); if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) { return false; } Uri data = intent.getData(); String installedPkg = data.getEncodedSchemeSpecificPart(); if (pkgName.equals(installedPkg)) { return true; } return false; } } private PackageManager getPm() { return mContext.getPackageManager(); } private IPackageManager getIPm() { IPackageManager ipm = IPackageManager.Stub.asInterface( ServiceManager.getService("package")); return ipm; } public boolean invokeInstallPackage(Uri packageURI, int flags, GenericReceiver receiver) { PackageInstallObserver observer = new PackageInstallObserver(); final boolean received = false; mContext.registerReceiver(receiver, receiver.filter); final boolean DEBUG = true; try { // Wait on observer synchronized(observer) { synchronized (receiver) { getPm().installPackage(packageURI, observer, flags, null); long waitTime = 0; while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { try { observer.wait(WAIT_TIME_INCR); waitTime += WAIT_TIME_INCR; } catch (InterruptedException e) { Log.i(TAG, "Interrupted during sleep", e); } } if(!observer.isDone()) { fail("Timed out waiting for packageInstalled callback"); } if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) { Log.i(TAG, "Failed to install with error code = " + observer.returnCode); return false; } // Verify we received the broadcast waitTime = 0; while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) { try { receiver.wait(WAIT_TIME_INCR); waitTime += WAIT_TIME_INCR; } catch (InterruptedException e) { Log.i(TAG, "Interrupted during sleep", e); } } if(!receiver.isDone()) { fail("Timed out waiting for PACKAGE_ADDED notification"); } return receiver.received; } } } finally { mContext.unregisterReceiver(receiver); } } public void invokeInstallPackageFail(Uri packageURI, int flags, int result) { PackageInstallObserver observer = new PackageInstallObserver(); try { // Wait on observer synchronized(observer) { getPm().installPackage(packageURI, observer, flags, null); long waitTime = 0; while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { try { observer.wait(WAIT_TIME_INCR); waitTime += WAIT_TIME_INCR; } catch (InterruptedException e) { Log.i(TAG, "Interrupted during sleep", e); } } if(!observer.isDone()) { fail("Timed out waiting for packageInstalled callback"); } assertEquals(observer.returnCode, result); } } finally { } } Uri getInstallablePackage(int fileResId, File outFile) { Resources res = mContext.getResources(); InputStream is = null; try { is = res.openRawResource(fileResId); } catch (NotFoundException e) { failStr("Failed to load resource with id: " + fileResId); } FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO, -1, -1); assertTrue(FileUtils.copyToFile(is, outFile)); FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO, -1, -1); return Uri.fromFile(outFile); } private PackageParser.Package parsePackage(Uri packageURI) { final String archiveFilePath = packageURI.getPath(); PackageParser packageParser = new PackageParser(archiveFilePath); File sourceFile = new File(archiveFilePath); DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0); packageParser = null; return pkg; } private boolean checkSd(long pkgLen) { String status = Environment.getExternalStorageState(); if (!status.equals(Environment.MEDIA_MOUNTED)) { return false; } long sdSize = -1; StatFs sdStats = new StatFs( Environment.getExternalStorageDirectory().getPath()); sdSize = (long)sdStats.getAvailableBlocks() * (long)sdStats.getBlockSize(); // TODO check for thresholds here return pkgLen <= sdSize; } private boolean checkInt(long pkgLen) { StatFs intStats = new StatFs(Environment.getDataDirectory().getPath()); long intSize = (long)intStats.getBlockCount() * (long)intStats.getBlockSize(); long iSize = (long)intStats.getAvailableBlocks() * (long)intStats.getBlockSize(); // TODO check for thresholds here? return pkgLen <= iSize; } private static final int INSTALL_LOC_INT = 1; private static final int INSTALL_LOC_SD = 2; private static final int INSTALL_LOC_ERR = -1; private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) { // Flags explicitly over ride everything else. if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 ) { return INSTALL_LOC_INT; } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0 ) { return INSTALL_LOC_SD; } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) { return INSTALL_LOC_INT; } // Manifest option takes precedence next if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { if (checkSd(pkgLen)) { return INSTALL_LOC_SD; } if (checkInt(pkgLen)) { return INSTALL_LOC_INT; } return INSTALL_LOC_ERR; } if (expInstallLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { if (checkInt(pkgLen)) { return INSTALL_LOC_INT; } return INSTALL_LOC_ERR; } if (expInstallLocation == PackageInfo.INSTALL_LOCATION_AUTO) { // Check for free memory internally if (checkInt(pkgLen)) { return INSTALL_LOC_INT; } // Check for free memory externally if (checkSd(pkgLen)) { return INSTALL_LOC_SD; } return INSTALL_LOC_ERR; } // Check for settings preference. boolean checkSd = false; int userPref = getDefaultInstallLoc(); if (userPref == APP_INSTALL_DEVICE) { if (checkInt(pkgLen)) { return INSTALL_LOC_INT; } return INSTALL_LOC_ERR; } else if (userPref == APP_INSTALL_SDCARD) { if (checkSd(pkgLen)) { return INSTALL_LOC_SD; } return INSTALL_LOC_ERR; } // Default system policy for apps with no manifest option specified. // Check for free memory internally if (checkInt(pkgLen)) { return INSTALL_LOC_INT; } return INSTALL_LOC_ERR; } private void assertInstall(PackageParser.Package pkg, int flags, int expInstallLocation) { try { String pkgName = pkg.packageName; ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0); assertNotNull(info); assertEquals(pkgName, info.packageName); File dataDir = Environment.getDataDirectory(); String appInstallPath = new File(dataDir, "app").getPath(); String drmInstallPath = new File(dataDir, "app-private").getPath(); File srcDir = new File(info.sourceDir); String srcPath = srcDir.getParent(); File publicSrcDir = new File(info.publicSourceDir); String publicSrcPath = publicSrcDir.getParent(); long pkgLen = new File(info.sourceDir).length(); if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); assertEquals(srcPath, drmInstallPath); assertEquals(publicSrcPath, appInstallPath); assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath())); } else { assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen); if (rLoc == INSTALL_LOC_INT) { assertEquals(srcPath, appInstallPath); assertEquals(publicSrcPath, appInstallPath); assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath())); // Make sure the native library dir is not a symlink final File nativeLibDir = new File(info.nativeLibraryDir); assertTrue("Native library dir should exist at " + info.nativeLibraryDir, nativeLibDir.exists()); try { assertEquals("Native library dir should not be a symlink", info.nativeLibraryDir, nativeLibDir.getCanonicalPath()); } catch (IOException e) { fail("Can't read " + nativeLibDir.getPath()); } } else if (rLoc == INSTALL_LOC_SD){ assertTrue("Application flags (" + info.flags + ") should contain FLAG_EXTERNAL_STORAGE", (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); assertTrue("The APK path (" + srcPath + ") should start with " + SECURE_CONTAINERS_PREFIX, srcPath .startsWith(SECURE_CONTAINERS_PREFIX)); assertTrue("The public APK path (" + publicSrcPath + ") should start with " + SECURE_CONTAINERS_PREFIX, publicSrcPath .startsWith(SECURE_CONTAINERS_PREFIX)); assertTrue("The native library path (" + info.nativeLibraryDir + ") should start with " + SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX)); // Make sure the native library in /data/data//lib is a // symlink to the ASEC final File nativeLibSymLink = new File(info.dataDir, "lib"); assertTrue("Native library symlink should exist at " + nativeLibSymLink.getPath(), nativeLibSymLink.exists()); try { assertEquals(nativeLibSymLink.getPath() + " should be a symlink to " + info.nativeLibraryDir, info.nativeLibraryDir, nativeLibSymLink .getCanonicalPath()); } catch (IOException e) { fail("Can't read " + nativeLibSymLink.getPath()); } } else { // TODO handle error. Install should have failed. fail("Install should have failed"); } } } catch (NameNotFoundException e) { failStr("failed with exception : " + e); } } private void assertNotInstalled(String pkgName) { try { ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0); fail(pkgName + " shouldnt be installed"); } catch (NameNotFoundException e) { } } class InstallParams { Uri packageURI; PackageParser.Package pkg; InstallParams(String outFileName, int rawResId) { this.pkg = getParsedPackage(outFileName, rawResId); this.packageURI = Uri.fromFile(new File(pkg.mScanPath)); } InstallParams(PackageParser.Package pkg) { this.packageURI = Uri.fromFile(new File(pkg.mScanPath)); this.pkg = pkg; } long getApkSize() { File file = new File(pkg.mScanPath); return file.length(); } } private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp) { return installFromRawResource("install.apk", R.raw.install, flags, cleanUp, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } static final String PERM_PACKAGE = "package"; static final String PERM_DEFINED = "defined"; static final String PERM_UNDEFINED = "undefined"; static final String PERM_USED = "used"; static final String PERM_NOTUSED = "notused"; private void assertPermissions(String[] cmds) { final PackageManager pm = getPm(); String pkg = null; PackageInfo pkgInfo = null; String mode = PERM_DEFINED; int i = 0; while (i < cmds.length) { String cmd = cmds[i++]; if (cmd == PERM_PACKAGE) { pkg = cmds[i++]; try { pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES); } catch (NameNotFoundException e) { pkgInfo = null; } } else if (cmd == PERM_DEFINED || cmd == PERM_UNDEFINED || cmd == PERM_USED || cmd == PERM_NOTUSED) { mode = cmds[i++]; } else { if (mode == PERM_DEFINED) { try { PermissionInfo pi = pm.getPermissionInfo(cmd, 0); assertNotNull(pi); assertEquals(pi.packageName, pkg); assertEquals(pi.name, cmd); assertNotNull(pkgInfo); boolean found = false; for (int j=0; j * Right now we allow all packages to be installed regardless of their * features. */ @LargeTest public void testUsesFeatureUnknownFeature() { int retCode = PackageManager.INSTALL_SUCCEEDED; installFromRawResource("install.apk", R.raw.install_uses_feature, 0, true, false, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } /*---------- Recommended install location tests ----*/ /* * TODO's * check version numbers for upgrades * check permissions of installed packages * how to do tests on updated system apps? * verify updates to system apps cannot be installed on the sdcard. */ }