Apps on sdcard: Add new broadcasts

Add new broadcasts ACTION_MEDIA_RESOURCES_AVAILABLE and
ACTION_MEDIA_RESOURCES_UNAVAILABLE that get broadcast by
PackageManagerService when sdcard gets mounted/unmounted
by MountService so that packages on sdcard get recognized by
various system services as being installed/available or
removed/unavailable by the system.
The broadcasts are sent before the actual package cleanup which includes
mounting/unmounting the packages and we force a gc right after so
that any lingering file references to resources on sdcard get
released.
This commit is contained in:
Suchi Amalapurapu
2010-01-28 09:57:30 -08:00
parent 57405b93f1
commit 08675a3376
15 changed files with 801 additions and 270 deletions

View File

@ -63,6 +63,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
@ -2179,21 +2180,32 @@ class PackageManagerService extends IPackageManager.Stub {
parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
}
String codePath = null;
String resPath = null;
if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) {
if (ps != null && ps.resourcePathString != null) {
pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
resPath = ps.resourcePathString;
} else {
// Should not happen at all. Just log an error.
Log.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
}
} else {
pkg.applicationInfo.publicSourceDir = pkg.mScanPath;
resPath = pkg.mScanPath;
}
pkg.applicationInfo.sourceDir = pkg.mScanPath;
codePath = pkg.mScanPath;
// Set application objects path explicitly.
setApplicationInfoPaths(pkg, codePath, resPath);
// Note that we invoke the following method only if we are about to unpack an application
return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
}
private static void setApplicationInfoPaths(PackageParser.Package pkg,
String destCodePath, String destResPath) {
pkg.mPath = pkg.mScanPath = destCodePath;
pkg.applicationInfo.sourceDir = destCodePath;
pkg.applicationInfo.publicSourceDir = destResPath;
}
private static String fixProcessName(String defProcessName,
String processName, int uid) {
if (processName == null) {
@ -4025,7 +4037,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
synchronized (mInstallLock) {
res.removedInfo.args.cleanUpResourcesLI();
res.removedInfo.args.doPostDeleteLI(true);
}
}
}
@ -4051,13 +4063,14 @@ class PackageManagerService extends IPackageManager.Stub {
abstract void createCopyFile();
abstract int copyApk(IMediaContainerService imcs);
abstract void doPreInstall(int status);
abstract int doPreInstall(int status);
abstract boolean doRename(int status, String pkgName, String oldCodePath);
abstract void doPostInstall(int status);
abstract int doPostInstall(int status);
abstract String getCodePath();
abstract String getResourcePath();
// Need installer lock especially for dex file removal.
abstract void cleanUpResourcesLI();
abstract boolean doPostDeleteLI(boolean delete);
}
class FileInstallArgs extends InstallArgs {
@ -4114,10 +4127,11 @@ class PackageManagerService extends IPackageManager.Stub {
return ret;
}
void doPreInstall(int status) {
int doPreInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
}
return status;
}
boolean doRename(int status, final String pkgName, String oldCodePath) {
@ -4144,10 +4158,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
void doPostInstall(int status) {
int doPostInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
}
return status;
}
String getResourcePath() {
@ -4220,6 +4235,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
return true;
}
boolean doPostDeleteLI(boolean delete) {
cleanUpResourcesLI();
return true;
}
}
class SdInstallArgs extends InstallArgs {
@ -4234,7 +4254,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
SdInstallArgs(String fullCodePath, String fullResourcePath) {
super(null, null, 0, null);
super(null, null, ApplicationInfo.FLAG_ON_SDCARD, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
@ -4243,6 +4263,11 @@ class PackageManagerService extends IPackageManager.Stub {
cachePath = subStr1;
}
SdInstallArgs(String cid) {
super(null, null, ApplicationInfo.FLAG_ON_SDCARD, null);
this.cid = cid;
}
void createCopyFile() {
cid = getTempContainerId();
}
@ -4254,16 +4279,8 @@ class PackageManagerService extends IPackageManager.Stub {
getEncryptKey(), RES_FILE_NAME);
} catch (RemoteException e) {
}
if (cachePath != null) {
// Mount container once its created with system_uid
cachePath = mountSdDir(cid, Process.SYSTEM_UID);
}
if (cachePath == null) {
return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
} else {
return PackageManager.INSTALL_SUCCEEDED;
}
return (cachePath == null) ? PackageManager.INSTALL_FAILED_CONTAINER_ERROR :
PackageManager.INSTALL_SUCCEEDED;
}
@Override
@ -4276,25 +4293,81 @@ class PackageManagerService extends IPackageManager.Stub {
return cachePath + "/" + RES_FILE_NAME;
}
void doPreInstall(int status) {
int doPreInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
// Destroy container
destroySdDir(cid);
} else {
// STOPSHIP Remove once new api is added in MountService
//boolean mounted = isContainerMounted(cid);
boolean mounted = false;
if (!mounted) {
cachePath = mountSdDir(cid, Process.SYSTEM_UID);
if (cachePath == null) {
return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
}
}
}
return status;
}
boolean doRename(int status, final String pkgName,
String oldCodePath) {
String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME);
String newCachePath = null;
/*final int RENAME_FAILED = 1;
final int MOUNT_FAILED = 2;
final int DESTROY_FAILED = 3;
final int PASS = 4;
int errCode = RENAME_FAILED;
if (mounted) {
// Unmount the container
if (!unMountSdDir(cid)) {
Log.i(TAG, "Failed to unmount " + cid + " before renaming");
return false;
}
mounted = false;
}
if (renameSdDir(cid, newCacheId)) {
errCode = MOUNT_FAILED;
if ((newCachePath = mountSdDir(newCacheId, Process.SYSTEM_UID)) != null) {
errCode = PASS;
}
}
String errMsg = "";
switch (errCode) {
case RENAME_FAILED:
errMsg = "RENAME_FAILED";
break;
case MOUNT_FAILED:
errMsg = "MOUNT_FAILED";
break;
case DESTROY_FAILED:
errMsg = "DESTROY_FAILED";
break;
default:
errMsg = "PASS";
break;
}
Log.i(TAG, "Status: " + errMsg);
if (errCode != PASS) {
return false;
}
Log.i(TAG, "Succesfully renamed " + cid + " to " +newCacheId +
" at path: " + cachePath + " to new path: " + newCachePath);
cid = newCacheId;
cachePath = newCachePath;
return true;
*/
// STOPSHIP TEMPORARY HACK FOR RENAME
// Create new container at newCachePath
String codePath = getCodePath();
String newCachePath = null;
final int CREATE_FAILED = 1;
final int COPY_FAILED = 3;
final int FINALIZE_FAILED = 5;
final int PASS = 7;
int errCode = CREATE_FAILED;
if ((newCachePath = createSdDir(new File(codePath), newCacheId)) != null) {
errCode = COPY_FAILED;
// Copy file from codePath
@ -4335,13 +4408,18 @@ class PackageManagerService extends IPackageManager.Stub {
return true;
}
void doPostInstall(int status) {
int doPostInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
} else {
// Unmount container
// Rename and remount based on package name and new uid
// STOP SHIP Change this once new api is added.
//boolean mounted = isContainerMounted(cid);
boolean mounted = false;
if (!mounted) {
mountSdDir(cid, Process.SYSTEM_UID);
}
}
return status;
}
private void cleanUp() {
@ -4363,32 +4441,66 @@ class PackageManagerService extends IPackageManager.Stub {
}
cleanUp();
}
boolean matchContainer(String app) {
if (cid.startsWith(app)) {
return true;
}
return false;
}
String getPackageName() {
int idx = cid.lastIndexOf("-");
if (idx == -1) {
return cid;
}
return cid.substring(0, idx);
}
boolean doPostDeleteLI(boolean delete) {
boolean ret = false;
boolean mounted = isContainerMounted(cid);
if (mounted) {
// Unmount first
ret = unMountSdDir(cid);
}
if (ret && delete) {
cleanUpResourcesLI();
}
return ret;
}
};
// Utility method used to create code paths based on package name and available index.
private static String getNextCodePath(String oldCodePath, String prefix, String suffix) {
String idxStr = "";
int idx = 1;
// Fall back to default value of idx=1 if prefix is not
// part of oldCodePath
if (oldCodePath != null) {
String subStr = oldCodePath;
if (subStr.startsWith(prefix)) {
subStr = subStr.substring(prefix.length());
}
// Drop the suffix right away
if (subStr.endsWith(suffix)) {
subStr = subStr.substring(0, subStr.length() - suffix.length());
}
if (subStr != null) {
if (subStr.startsWith("-")) {
subStr = subStr.substring(1);
}
try {
idx = Integer.parseInt(subStr);
if (idx <= 1) {
idx++;
} else {
idx--;
// If oldCodePath already contains prefix find out the
// ending index to either increment or decrement.
int sidx = subStr.lastIndexOf(prefix);
if (sidx != -1) {
subStr = subStr.substring(sidx + prefix.length());
if (subStr != null) {
if (subStr.startsWith("-")) {
subStr = subStr.substring(1);
}
try {
idx = Integer.parseInt(subStr);
if (idx <= 1) {
idx++;
} else {
idx--;
}
} catch(NumberFormatException e) {
}
} catch(NumberFormatException e) {
}
}
}
@ -4753,6 +4865,9 @@ class PackageManagerService extends IPackageManager.Stub {
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;
}
}
@ -4761,9 +4876,8 @@ class PackageManagerService extends IPackageManager.Stub {
res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
break main_flow;
}
// TODO rename pkg.mScanPath In scanPackageLI let it just set values based on mScanPath
pkg.applicationInfo.sourceDir = pkg.mScanPath= pkg.mPath = args.getCodePath();
pkg.applicationInfo.publicSourceDir = args.getResourcePath();
// Set application objects path explicitly after the rename
setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
if(replacingExistingPackage) {
replacePackageLI(pkg, parseFlags, scanMode,
installerPackageName, res);
@ -4977,9 +5091,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Delete the resources here after sending the broadcast to let
// other processes clean up before deleting resources.
synchronized (mInstallLock) {
if (info.args != null) {
info.args.cleanUpResourcesLI();
if (info.args != null) {
synchronized (mInstallLock) {
info.args.doPostDeleteLI(deleteCodeAndResources);
}
}
return res;
@ -6948,6 +7062,17 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
private Set<String> findPackagesWithFlag(int flag) {
Set<String> ret = new HashSet<String>();
for (PackageSetting ps : mPackages.values()) {
// Has to match atleast all the flag bits set on flag
if ((ps.pkgFlags & flag) == flag) {
ret.add(ps.name);
}
}
return ret;
}
private void removeUserIdLP(int uid) {
if (uid >= FIRST_APPLICATION_UID) {
int N = mUserIds.size();
@ -7992,9 +8117,20 @@ class PackageManagerService extends IPackageManager.Stub {
return true;
}
private String getSdDir(String pkgName) {
return getMountService().getSecureContainerPath(pkgName);
}
private boolean renameSdDir(String oldId, String newId) {
try {
getMountService().renameSecureContainer(oldId, newId);
return true;
} catch (IllegalStateException e) {
Log.i(TAG, "Failed ot rename " + oldId + " to " + newId +
" with exception : " + e);
}
return false;
}
private String getSdDir(String pkgName) {
return getMountService().getSecureContainerPath(pkgName);
}
private boolean finalizeSdDir(String pkgName) {
int rc = getMountService().finalizeSecureContainer(pkgName);
@ -8019,6 +8155,16 @@ class PackageManagerService extends IPackageManager.Stub {
return list.length == 0 ? null : list;
}
static boolean isContainerMounted(String cid) {
// STOPSHIP
// New api from MountService
try {
return (getMountService().getSecureContainerPath(cid) != null);
} catch (IllegalStateException e) {
}
return false;
}
static String getTempContainerId() {
String prefix = "smdl1tmp";
int tmpIdx = 1;
@ -8063,6 +8209,8 @@ class PackageManagerService extends IPackageManager.Stub {
}
public void updateExternalMediaStatus(final boolean mediaStatus) {
final boolean DEBUG = true;
if (DEBUG) Log.i(TAG, "updateExterMediaStatus::");
if (mediaStatus == mMediaMounted) {
return;
}
@ -8071,54 +8219,155 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
final String list[] = getSecureContainerList();
if (list == null || list.length == 0) {
return;
}
for (int i = 0; i < list.length; i++) {
String mountPkg = list[i];
// TODO compare with default package
synchronized (mPackages) {
PackageSetting ps = mSettings.mPackages.get(mountPkg);
if (ps != null && (ps.pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) != 0) {
if (mediaStatus) {
String pkgPath = getSdDir(mountPkg);
if (pkgPath == null) {
continue;
}
pkgPath = ps.codePathString;
int parseFlags = PackageParser.PARSE_CHATTY |
PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
PackageParser pp = new PackageParser(pkgPath);
pp.setSeparateProcesses(mSeparateProcesses);
final PackageParser.Package pkg = pp.parsePackage(new File(pkgPath),
null, mMetrics, parseFlags);
if (pkg == null) {
Log.w(TAG, "Failed to install package : " + mountPkg + " from sd card");
continue;
}
int scanMode = SCAN_MONITOR;
// Scan the package
if (scanPackageLI(pkg, parseFlags, scanMode) != null) {
// Grant permissions
grantPermissionsLP(pkg, false);
// Persist settings
mSettings.writeLP();
} else {
Log.i(TAG, "Failed to install package: " + mountPkg + " from sdcard");
}
} else {
// Delete package
PackageRemovedInfo outInfo = new PackageRemovedInfo();
boolean res = deletePackageLI(mountPkg, false, PackageManager.DONT_DELETE_DATA, outInfo);
if (!res) {
Log.e(TAG, "Failed to delete pkg from sdcard : " + mountPkg);
}
}
}
}
}
updateExternalMediaStatusInner(mediaStatus);
}
});
}
void updateExternalMediaStatusInner(boolean mediaStatus) {
final String list[] = getSecureContainerList();
if (list == null || list.length == 0) {
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) {
SdInstallArgs args = new SdInstallArgs(cid);
String removeEntry = null;
for (String app : appList) {
if (args.matchContainer(app)) {
removeEntry = app;
break;
}
}
if (removeEntry == null) {
// No matching app on device. Skip entry or may be cleanup?
// Ignore default package
continue;
}
appList.remove(removeEntry);
PackageSetting ps = mSettings.mPackages.get(removeEntry);
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++;
}
}
}
}
int uidArr[] = uidList;
if ((num > 0) && (num < uidList.length)) {
uidArr = new int[num];
for (int i = 0; i < num; i++) {
uidArr[i] = uidList[i];
}
}
if (mediaStatus) {
loadMediaPackages(processCids, uidArr);
} else {
unloadMediaPackages(processCids, uidArr);
}
}
private void sendResourcesChangedBroadcast(boolean mediaStatus,
ArrayList<String> pkgList, int uidArr[]) {
int size = pkgList.size();
if (size > 0) {
// Send broadcasts here
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;
sendPackageBroadcast(action, null, extras);
}
}
void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
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 (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
Log.i(TAG, "Failed to install package: " + codePath + " from sdcard");
continue;
}
// Parse package
int parseFlags = PackageParser.PARSE_CHATTY |
PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
PackageParser pp = new PackageParser(codePath);
pp.setSeparateProcesses(mSeparateProcesses);
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");
continue;
}
setApplicationInfoPaths(pkg, codePath, codePath);
int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
synchronized (mInstallLock) {
// Scan the package
if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
synchronized (mPackages) {
// Grant permissions
grantPermissionsLP(pkg, false);
// Persist settings
mSettings.writeLP();
retCode = PackageManager.INSTALL_SUCCEEDED;
pkgList.add(pkg.packageName);
}
} else {
Log.i(TAG, "Failed to install package: " + pkg.packageName + " from sdcard");
}
}
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?
}
void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
ArrayList<String> pkgList = new ArrayList<String>();
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
// Delete package internally
PackageRemovedInfo outInfo = new PackageRemovedInfo();
synchronized (mInstallLock) {
boolean res = deletePackageLI(pkgName, false,
PackageManager.DONT_DELETE_DATA, outInfo);
if (res) {
pkgList.add(pkgName);
} else {
Log.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
}
}
}
// Send broadcasts
sendResourcesChangedBroadcast(false, pkgList, uidArr);
Runtime.getRuntime().gc();
// Do clean up. Just unmount
for (SdInstallArgs args : keys) {
synchronized (mInstallLock) {
args.doPostDeleteLI(false);
}
}
}
}