filter, null, null, null);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiverInternal(sPackageRemovedReceiver,
sdFilter, null, null, null);
}
String pkgList[] = null;
String action = intent.getAction();
boolean immediateGc = false;
- if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
immediateGc = true;
} else {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MEDIA_RESOURCES_AVAILABLE =
- "android.intent.action.MEDIA_RESOURCES_AVAILABILE";
+ public static final String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE =
+ "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
/**
* Broadcast Action: Resources for a set of packages are currently
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MEDIA_RESOURCES_UNAVAILABLE =
- "android.intent.action.MEDIA_RESOURCES_UNAVAILABILE";
+ public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE =
+ "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABILE";
/**
* Broadcast Action: The current system wallpaper has changed. See
/**
* This field is part of
- * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE},
- * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE}
+ * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
+ * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
* and contains a string array of all of the components that have changed.
* @hide
*/
/**
* This field is part of
- * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE},
- * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE}
+ * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
+ * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
* and contains an integer array of uids of all of the components
* that have changed.
* @hide
public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
/**
+ * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package couldn't be installed in the specified install
+ * location.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
+
+ /**
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser was given a path that is not a file, or does not end with the expected
mContext.registerReceiver(receiver, intentFilter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(receiver, sdFilter);
}
mContext.registerReceiver(mPackageChangedReceiver, packageFilter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mPackageChangedReceiver, sdFilter);
}
if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
Intent.ACTION_PACKAGE_CHANGED.equals(action) ||
- Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) ||
- Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+ Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
+ Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
if (DBG) Log.d(TAG, "Got " + action);
// Update list of searchable activities
getSearchables().buildSearchableList();
<uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_TRANSITION_TYPES" />
<uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_NOTIFICATION_TYPES" />
+ <!-- package manager test permissions -->
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+
<application android:theme="@style/Theme">
<uses-library android:name="android.test.runner" />
<activity android:name="StubTestBrowserActivity" android:label="Stubbed Test Browser">
context.registerReceiver(broadcastReceiver, packageFilter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(broadcastReceiver, sdFilter);
// boot completed
mContext.registerReceiver(this, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(this, sdFilter);
}
synchronized (mLock) {
String action = intent.getAction();
String pkgList[] = null;
- if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
} else {
Uri data = intent.getData();
mContext.registerReceiver(mBroadcastReceiver, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
}
} else {
boolean added = false;
String pkgList[] = null;
- if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) {
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
added = true;
- } if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+ } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
added = false;
} else {
mContext.registerReceiver(mBroadcastReceiver, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
}
}
added = Intent.ACTION_PACKAGE_ADDED.equals(action);
replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
- } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) {
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
added = true;
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
added = false;
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
}
if (pkg != null) {
pkgList = new String[] { pkg };
}
- } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) ||
- Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
+ Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
}
if (pkgList == null || pkgList.length == 0) {
mContext.registerReceiver(mBroadcastReceiver, packageFilt);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
IntentFilter screenOnOffFilt = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
// listen for settings changes
if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)
- || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+ || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
synchronized (mLock) {
int uidList[] = null;
- if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+ if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
} else {
uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)};
*/
public int mountVolume(String path) {
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
-
int rc = MountServiceResultCode.OperationSucceeded;
try {
public int unmountVolume(String path) {
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ // Check if media has been mounted
+ String oldState = mLegacyState;
+ if (!oldState.equals(Environment.MEDIA_MOUNTED)) {
+ return VoldResponseCode.OpFailedVolNotMounted;
+ }
+ // Notify PackageManager of potential media removal and deal with
+ // return code later on. The caller of this api should be aware or have been
+ // notified that the applications installed on the media will be killed.
+ mPms.updateExternalMediaStatus(false);
try {
mConnector.doCommand(String.format("volume unmount %s", path));
return MountServiceResultCode.OperationSucceeded;
} catch (NativeDaemonConnectorException e) {
+ // Don't worry about mismatch in PackageManager since the
+ // call back will handle the status changes any way.
int code = e.getCode();
if (code == VoldResponseCode.OpFailedVolNotMounted) {
return MountServiceResultCode.OperationFailedVolumeNotMounted;
updateAdbNotification();
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)
- || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+ || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
String pkgList[] = null;
- if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+ if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
} else {
Uri uri = intent.getData();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mIntentReceiver, filter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mIntentReceiver, sdFilter);
SettingsObserver observer = new SettingsObserver(mHandler);
File tmpPackageFile = new File(args.getCodePath());
boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0);
- boolean replacingExistingPackage = false;
+ boolean replace = false;
int scanMode = SCAN_MONITOR | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE
| (newInstall ? SCAN_NEW_INSTALL : 0);
// Result object to be returned
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
- main_flow: try {
- // Retrieve PackageSettings and parse package
- int parseFlags = PackageParser.PARSE_CHATTY |
- (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |
- (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
- parseFlags |= mDefParseFlags;
- PackageParser pp = new PackageParser(tmpPackageFile.getPath());
- pp.setSeparateProcesses(mSeparateProcesses);
- final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
- null, mMetrics, parseFlags);
- if (pkg == null) {
- res.returnCode = pp.getParseError();
- break main_flow;
- }
- String pkgName = res.name = pkg.packageName;
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
- if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
- res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
- break main_flow;
- }
+ // Retrieve PackageSettings and parse package
+ int parseFlags = PackageParser.PARSE_CHATTY |
+ (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |
+ (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
+ parseFlags |= mDefParseFlags;
+ PackageParser pp = new PackageParser(tmpPackageFile.getPath());
+ pp.setSeparateProcesses(mSeparateProcesses);
+ final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
+ null, mMetrics, parseFlags);
+ if (pkg == null) {
+ res.returnCode = pp.getParseError();
+ return;
+ }
+ String pkgName = res.name = pkg.packageName;
+ if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+ if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
+ res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
+ return;
}
- if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {
- res.returnCode = pp.getParseError();
- break main_flow;
+ }
+ if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {
+ res.returnCode = pp.getParseError();
+ return;
+ }
+ // Some preinstall checks
+ if (forwardLocked && onSd) {
+ // Make sure forward locked apps can only be installed
+ // on internal storage
+ Log.w(TAG, "Cannot install protected apps on sdcard");
+ res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ return;
+ }
+ // Get rid of all references to package scan path via parser.
+ pp = null;
+ String oldCodePath = null;
+ boolean systemApp = false;
+ synchronized (mPackages) {
+ // Check if installing already existing package
+ if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0
+ && mPackages.containsKey(pkgName)) {
+ replace = true;
}
-
- // Get rid of all references to package scan path via parser.
- pp = null;
- String oldCodePath = null;
- synchronized (mPackages) {
- //check if installing already existing package
- if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0
- && mPackages.containsKey(pkgName)) {
- replacingExistingPackage = true;
- }
- PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (ps != null) {
- oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
+ if (ps.pkg != null && ps.pkg.applicationInfo != null) {
+ systemApp = (ps.pkg.applicationInfo.flags &
+ ApplicationInfo.FLAG_SYSTEM) != 0;
}
}
+ }
- if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
- res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- break main_flow;
- }
- // Set application objects path explicitly after the rename
- setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
- if(replacingExistingPackage) {
- replacePackageLI(pkg, parseFlags, scanMode,
- installerPackageName, res);
- } else {
- installNewPackageLI(pkg, parseFlags, scanMode,
- installerPackageName,res);
- }
- } finally {
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- }
+ if (systemApp && onSd) {
+ // Disable updates to system apps on sdcard
+ Log.w(TAG, "Cannot install updates to system apps on sdcard");
+ res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ return;
+ }
+ if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
+ res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ return;
+ }
+ // Set application objects path explicitly after the rename
+ setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
+ if(replace) {
+ replacePackageLI(pkg, parseFlags, scanMode,
+ installerPackageName, res);
+ } else {
+ installNewPackageLI(pkg, parseFlags, scanMode,
+ installerPackageName,res);
}
}
}
public void updateExternalMediaStatus(final boolean mediaStatus) {
- final boolean DEBUG = true;
- if (DEBUG) Log.i(TAG, "updateExterMediaStatus::");
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
+ mediaStatus+", mMediaMounted=" + mMediaMounted);
if (mediaStatus == mMediaMounted) {
return;
}
HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
int uidList[] = new int[list.length];
int num = 0;
- for (int i = 0; i < uidList.length; i++) {
- uidList[i] = Process.LAST_APPLICATION_UID;
- }
synchronized (mPackages) {
Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
for (String cid : list) {
processCids.put(args, ps.codePathString);
int uid = ps.userId;
if (uid != -1) {
- int idx = Arrays.binarySearch(uidList, uid);
- if (idx < 0) {
- uidList[-idx] = uid;
- num++;
- }
+ uidList[num++] = uid;
}
}
}
- int uidArr[] = uidList;
- if ((num > 0) && (num < uidList.length)) {
+ int uidArr[] = null;
+ if (num > 0) {
+ // Sort uid list
+ Arrays.sort(uidList, 0, num);
+ // Throw away duplicates
uidArr = new int[num];
- for (int i = 0; i < num; i++) {
- uidArr[i] = uidList[i];
+ uidArr[0] = uidList[0];
+ int di = 0;
+ for (int i = 1; i < num; i++) {
+ if (uidList[i-1] != uidList[i]) {
+ uidArr[di++] = uidList[i];
+ }
+ }
+ if (true) {
+ for (int j = 0; j < num; j++) {
+ Log.i(TAG, "uidArr[" + j + "]=" + uidArr[j]);
+ }
}
}
if (mediaStatus) {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
loadMediaPackages(processCids, uidArr);
} else {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
unloadMediaPackages(processCids, uidArr);
}
}
Bundle extras = new Bundle();
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
pkgList.toArray(new String[size]));
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
- String action = mediaStatus ? Intent.ACTION_MEDIA_RESOURCES_AVAILABLE
- : Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE;
+ if (uidArr != null) {
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
+ }
+ String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
+ : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
sendPackageBroadcast(action, null, extras);
}
}
ArrayList<String> pkgList = new ArrayList<String>();
Set<SdInstallArgs> keys = processCids.keySet();
for (SdInstallArgs args : keys) {
- String cid = args.cid;
String codePath = processCids.get(args);
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install pkg : "
+ + args.cid + " from " + args.cachePath);
if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
- Log.i(TAG, "Failed to install package: " + codePath + " from sdcard");
+ Log.e(TAG, "Failed to install package: " + codePath + " from sdcard");
continue;
}
// Parse package
final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
codePath, mMetrics, parseFlags);
if (pkg == null) {
- Log.w(TAG, "Failed to install package : " + cid + " from sd card");
+ Log.e(TAG, "Trying to install pkg : "
+ + args.cid + " from " + args.cachePath);
continue;
}
setApplicationInfoPaths(pkg, codePath, codePath);
}
}
args.doPostInstall(retCode);
- pkgList.add(pkg.packageName);
}
// Send broadcasts first
- sendResourcesChangedBroadcast(true, pkgList, uidArr);
- Runtime.getRuntime().gc();
- // If something failed do we clean up here or next install?
+ if (pkgList.size() > 0) {
+ sendResourcesChangedBroadcast(true, pkgList, uidArr);
+ Runtime.getRuntime().gc();
+ // If something failed do we clean up here or next install?
+ }
}
void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
ArrayList<String> pkgList = new ArrayList<String>();
+ ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
Set<SdInstallArgs> keys = processCids.keySet();
for (SdInstallArgs args : keys) {
String cid = args.cid;
String pkgName = args.getPackageName();
- // STOPSHIP Send broadcast to apps to remove references
- // STOPSHIP Unmount package
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName);
// Delete package internally
PackageRemovedInfo outInfo = new PackageRemovedInfo();
synchronized (mInstallLock) {
pkgList.add(pkgName);
} else {
Log.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
+ failedList.add(args);
}
}
}
// Send broadcasts
- sendResourcesChangedBroadcast(false, pkgList, uidArr);
- Runtime.getRuntime().gc();
+ if (pkgList.size() > 0) {
+ sendResourcesChangedBroadcast(false, pkgList, uidArr);
+ Runtime.getRuntime().gc();
+ }
// Do clean up. Just unmount
- for (SdInstallArgs args : keys) {
+ for (SdInstallArgs args : failedList) {
synchronized (mInstallLock) {
args.doPostDeleteLI(false);
}
intent.getAction());
if (intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
|| intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
- || Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())
+ || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())
|| uidRemoved) {
if (checkComponentPermission(
android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
} else {
// If resources are unvailble just force stop all
// those packages and flush the attribute cache as well.
- if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) {
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
if (list != null && (list.length > 0)) {
for (String pkg : list) {
skipPackages = new String[] { pkgName };
}
}
- } else if (intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(intent.getAction())) {
+ } else if (intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
}
if (skipPackages != null && (skipPackages.length > 0)) {
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
filter.addDataScheme("package");
mContext.registerReceiver(this, filter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+ IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(this, sdFilter);
}
@Override
public void onReceive(Context context, Intent intent) {
String pkgList[] = null;
- if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) {
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
} else {
Uri data = intent.getData();
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
+ <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
package com.android.unit_tests;
+import android.os.IMountService.Stub;
+
import android.net.Uri;
import android.os.FileUtils;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstallObserver;
-import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IPackageDeleteObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageStats;
import android.util.Log;
import android.os.Environment;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.IMountService;
+import android.os.MountServiceResultCode;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
public class PackageManagerTests extends AndroidTestCase {
- private static final boolean localLOGV = false;
+ private static final boolean localLOGV = true;
public static final String TAG="PackageManagerTests";
- public final long MAX_WAIT_TIME=60*1000;
- public final long WAIT_TIME_INCR=10*1000;
+ public final long MAX_WAIT_TIME=120*1000;
+ public final long WAIT_TIME_INCR=20*1000;
+
+ void failStr(String errMsg) {
+ Log.w(TAG, "errMsg="+errMsg);
+ fail(errMsg);
+ }
+ void failStr(Exception e) {
+ Log.w(TAG, "e.getMessage="+e.getMessage());
+ Log.w(TAG, "e="+e);
+ }
@Override
protected void setUp() throws Exception {
try {
// Wait on observer
synchronized(observer) {
+ synchronized (receiver) {
getPm().installPackage(packageURI, observer, flags, null);
long waitTime = 0;
while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
return false;
}
- synchronized (receiver) {
// Verify we received the broadcast
waitTime = 0;
while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
PackageParser.Package pkg = parsePackage(packageURI);
assertNotNull(pkg);
InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+ InstallParams ip = null;
try {
try {
assertTrue(invokeInstallPackage(packageURI, flags,
}
// Verify installed information
assertInstall(pkg.packageName, flags);
- return new InstallParams(pkg, outFileName, packageURI);
+ ip = new InstallParams(pkg, outFileName, packageURI);
+ return ip;
} finally {
if (cleanUp) {
- getPm().deletePackage(pkg.packageName, null, 0);
- if (outFile != null && outFile.exists()) {
- outFile.delete();
- }
+ cleanUpInstall(ip);
}
}
}
/* ------------------------- Test replacing packages --------------*/
class ReplaceReceiver extends GenericReceiver {
String pkgName;
- boolean removed = false;
+ final static int INVALID = -1;
+ final static int REMOVED = 1;
+ final static int ADDED = 2;
+ final static int REPLACED = 3;
+ int removed = INVALID;
+ // for updated system apps only
+ boolean update = false;
ReplaceReceiver(String pkgName) {
this.pkgName = pkgName;
filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ if (update) {
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ }
+ filter.addDataScheme("package");
super.setFilter(filter);
}
public boolean notifyNow(Intent intent) {
String action = intent.getAction();
- if (Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
- Uri data = intent.getData();
- String installedPkg = data.getEncodedSchemeSpecificPart();
- if (pkgName.equals(installedPkg)) {
- if (removed) {
- return true;
- } else {
- removed = true;
- }
+ Uri data = intent.getData();
+ String installedPkg = data.getEncodedSchemeSpecificPart();
+ if (pkgName == null || !pkgName.equals(installedPkg)) {
+ return false;
+ }
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ removed = REMOVED;
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ if (removed != REMOVED) {
+ return false;
+ }
+ boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (!replacing) {
+ return false;
+ }
+ removed = ADDED;
+ if (!update) {
+ return true;
}
+ } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+ if (removed != ADDED) {
+ return false;
+ }
+ removed = REPLACED;
+ return true;
}
return false;
}
*/
public void replaceFromRawResource(int flags) {
InstallParams ip = installFromRawResource(flags, false);
- boolean result = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
+ boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
GenericReceiver receiver;
- if (result) {
+ if (replace) {
receiver = new ReplaceReceiver(ip.pkg.packageName);
+ Log.i(TAG, "Creating replaceReceiver");
} else {
receiver = new InstallReceiver(ip.pkg.packageName);
}
try {
try {
assertEquals(invokeInstallPackage(ip.packageURI, flags,
- ip.pkg.packageName, receiver), result);
- if (result) {
+ ip.pkg.packageName, receiver), replace);
+ if (replace) {
assertInstall(ip.pkg.packageName, flags);
}
} catch (Exception e) {
failStr("Failed with exception : " + e);
}
} finally {
- getPm().deletePackage(ip.pkg.packageName, null, 0);
- File outFile = new File(ip.outFileName);
- if (outFile != null && outFile.exists()) {
- outFile.delete();
- }
+ cleanUpInstall(ip);
}
}
replaceFromRawResource(PackageManager.INSTALL_ON_SDCARD);
}
- void failStr(String errMsg) {
- Log.w(TAG, "errMsg="+errMsg);
- fail(errMsg);
+ @MediumTest
+ public void testReplaceNormalInternal() {
+ replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
}
- void failStr(Exception e) {
- Log.w(TAG, "e.getMessage="+e.getMessage());
- Log.w(TAG, "e="+e);
+
+ @MediumTest
+ public void testReplaceFwdLockedInternal() {
+ replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING |
+ PackageManager.INSTALL_FORWARD_LOCK);
+ }
+
+ @MediumTest
+ public void testReplaceSdcard() {
+ replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING |
+ PackageManager.INSTALL_ON_SDCARD);
+ }
+
+ /* -------------- Delete tests ---*/
+ class DeleteObserver extends IPackageDeleteObserver.Stub {
+
+ public boolean succeeded;
+ private boolean doneFlag = false;
+
+ public boolean isDone() {
+ return doneFlag;
+ }
+
+ public void packageDeleted(boolean succeeded) throws RemoteException {
+ synchronized(this) {
+ this.succeeded = succeeded;
+ doneFlag = true;
+ notifyAll();
+ }
+ }
+ }
+
+ class DeleteReceiver extends GenericReceiver {
+ String pkgName;
+
+ DeleteReceiver(String pkgName) {
+ this.pkgName = pkgName;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ super.setFilter(filter);
+ }
+
+ public boolean notifyNow(Intent intent) {
+ String action = intent.getAction();
+ if (!Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ return false;
+ }
+ Uri data = intent.getData();
+ String installedPkg = data.getEncodedSchemeSpecificPart();
+ if (pkgName.equals(installedPkg)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public boolean invokeDeletePackage(Uri packageURI, int flags,
+ final String pkgName, GenericReceiver receiver) throws Exception {
+ DeleteObserver observer = new DeleteObserver();
+ final boolean received = false;
+ mContext.registerReceiver(receiver, receiver.filter);
+ try {
+ // Wait on observer
+ synchronized(observer) {
+ synchronized (receiver) {
+ getPm().deletePackage(pkgName, observer, flags);
+ long waitTime = 0;
+ while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ observer.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!observer.isDone()) {
+ throw new Exception("Timed out waiting for packageInstalled callback");
+ }
+ // Verify we received the broadcast
+ waitTime = 0;
+ while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ receiver.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!receiver.isDone()) {
+ throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+ }
+ return receiver.received;
+ }
+ }
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ public void deleteFromRawResource(int iFlags, int dFlags) {
+ InstallParams ip = installFromRawResource(iFlags, false);
+ boolean retainData = ((dFlags & PackageManager.DONT_DELETE_DATA) != 0);
+ GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
+ DeleteObserver observer = new DeleteObserver();
+ try {
+ assertTrue(invokeDeletePackage(ip.packageURI, dFlags,
+ ip.pkg.packageName, receiver));
+ ApplicationInfo info = null;
+ Log.i(TAG, "okay4");
+ try {
+ info = getPm().getApplicationInfo(ip.pkg.packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException e) {
+ info = null;
+ }
+ if (retainData) {
+ assertNotNull(info);
+ assertEquals(info.packageName, ip.pkg.packageName);
+ File file = new File(info.dataDir);
+ assertTrue(file.exists());
+ } else {
+ assertNull(info);
+ }
+ } catch (Exception e) {
+ failStr(e);
+ } finally {
+ cleanUpInstall(ip);
+ }
+ }
+
+ @MediumTest
+ public void testDeleteNormalInternal() {
+ deleteFromRawResource(0, 0);
+ }
+
+ @MediumTest
+ public void testDeleteFwdLockedInternal() {
+ deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, 0);
+ }
+
+ @MediumTest
+ public void testDeleteSdcard() {
+ deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, 0);
+ }
+
+ @MediumTest
+ public void testDeleteNormalInternalRetainData() {
+ deleteFromRawResource(0, PackageManager.DONT_DELETE_DATA);
+ }
+
+ @MediumTest
+ public void testDeleteFwdLockedInternalRetainData() {
+ deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DONT_DELETE_DATA);
+ }
+
+ @MediumTest
+ public void testDeleteSdcardRetainData() {
+ deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, PackageManager.DONT_DELETE_DATA);
}
+
+ /* sdcard mount/unmount tests ******/
+
+ class SdMountReceiver extends GenericReceiver {
+ String pkgNames[];
+ boolean status = true;
+
+ SdMountReceiver(String[] pkgNames) {
+ this.pkgNames = pkgNames;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ super.setFilter(filter);
+ }
+
+ public boolean notifyNow(Intent intent) {
+ Log.i(TAG, "okay 1");
+ String action = intent.getAction();
+ if (!Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ return false;
+ }
+ String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ for (String pkg : pkgNames) {
+ boolean found = false;
+ for (String rpkg : rpkgList) {
+ if (rpkg.equals(pkg)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ status = false;
+ return true;
+ }
+ }
+ return true;
+ }
+ }
+
+ class SdUnMountReceiver extends GenericReceiver {
+ String pkgNames[];
+ boolean status = true;
+
+ SdUnMountReceiver(String[] pkgNames) {
+ this.pkgNames = pkgNames;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ super.setFilter(filter);
+ }
+
+ public boolean notifyNow(Intent intent) {
+ String action = intent.getAction();
+ if (!Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ return false;
+ }
+ String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ for (String pkg : pkgNames) {
+ boolean found = false;
+ for (String rpkg : rpkgList) {
+ if (rpkg.equals(pkg)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ status = false;
+ return true;
+ }
+ }
+ return true;
+ }
+ }
+
+ IMountService getMs() {
+ IBinder service = ServiceManager.getService("mount");
+ if (service != null) {
+ return IMountService.Stub.asInterface(service);
+ } else {
+ Log.e(TAG, "Can't get mount service");
+ }
+ return null;
+ }
+
+ boolean getMediaState() {
+ try {
+ String mPath = Environment.getExternalStorageDirectory().toString();
+ String state = getMs().getVolumeState(mPath);
+ return Environment.MEDIA_MOUNTED.equals(state);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ boolean mountMedia() {
+ if (getMediaState()) {
+ return true;
+ }
+ try {
+ String mPath = Environment.getExternalStorageDirectory().toString();
+ int ret = getMs().mountVolume(mPath);
+ return ret == MountServiceResultCode.OperationSucceeded;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ private boolean unmountMedia() {
+ if (!getMediaState()) {
+ return true;
+ }
+ try {
+ String mPath = Environment.getExternalStorageDirectory().toString();
+ int ret = getMs().unmountVolume(mPath);
+ return ret == MountServiceResultCode.OperationSucceeded;
+ } catch (RemoteException e) {
+ return true;
+ }
+ }
+
+ /*
+ * Install package on sdcard. Unmount and then mount the media.
+ * (Use PackageManagerService private api for now)
+ * Make sure the installed package is available.
+ * STOPSHIP will uncomment when MountService api's to mount/unmount
+ * are made asynchronous.
+ */
+ public void xxxtestMountSdNormalInternal() {
+ assertTrue(mountFromRawResource());
+ }
+
+ private boolean mountFromRawResource() {
+ // Install pkg on sdcard
+ InstallParams ip = installFromRawResource(PackageManager.INSTALL_ON_SDCARD |
+ PackageManager.INSTALL_REPLACE_EXISTING, false);
+ if (localLOGV) Log.i(TAG, "Installed pkg on sdcard");
+ boolean origState = getMediaState();
+ SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName});
+ try {
+ if (localLOGV) Log.i(TAG, "Unmounting media");
+ // Unmount media
+ assertTrue(unmountMedia());
+ if (localLOGV) Log.i(TAG, "Unmounted media");
+ try {
+ if (localLOGV) Log.i(TAG, "Sleeping for 10 second");
+ Thread.sleep(10*1000);
+ } catch (InterruptedException e) {
+ failStr(e);
+ }
+ // Register receiver here
+ PackageManager pm = getPm();
+ mContext.registerReceiver(receiver, receiver.filter);
+
+ // Wait on receiver
+ synchronized (receiver) {
+ if (localLOGV) Log.i(TAG, "Mounting media");
+ // Mount media again
+ assertTrue(mountMedia());
+ if (localLOGV) Log.i(TAG, "Mounted media");
+ if (localLOGV) Log.i(TAG, "Waiting for notification");
+ long waitTime = 0;
+ // Verify we received the broadcast
+ waitTime = 0;
+ while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ receiver.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!receiver.isDone()) {
+ failStr("Timed out waiting for EXTERNAL_APPLICATIONS notification");
+ }
+ return receiver.received;
+ }
+ } catch (InterruptedException e) {
+ failStr(e);
+ return false;
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ // Restore original media state
+ if (origState) {
+ mountMedia();
+ } else {
+ unmountMedia();
+ }
+ if (localLOGV) Log.i(TAG, "Cleaning up install");
+ cleanUpInstall(ip);
+ }
+ }
+
+ void cleanUpInstall(InstallParams ip) {
+ if (ip == null) {
+ return;
+ }
+ Runtime.getRuntime().gc();
+ Log.i(TAG, "Deleting package : " + ip.pkg.packageName);
+ getPm().deletePackage(ip.pkg.packageName, null, 0);
+ File outFile = new File(ip.outFileName);
+ if (outFile != null && outFile.exists()) {
+ outFile.delete();
+ }
+ }
+ /*
+ * 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.
+ */
}