您的位置:首页 > 其它

PacakgeManagerService学习笔记八-APK installation

2014-02-07 17:57 309 查看
<----接上---->

/*DefaultContainerService.java*/

1. getMinimalPackageInfo: 获取一个PackageInfoLite对象

public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags, long threshold) {
PackageInfoLite ret = new PackageInfoLite();
if (fileUri == null) {
Slog.i(TAG, "Invalid package uri " + fileUri);
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
return ret;
}
String scheme = fileUri.getScheme();
if (scheme != null && !scheme.equals("file")) {
Slog.w(TAG, "Falling back to installing on internal storage only");
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL;
return ret;
}
String archiveFilePath = fileUri.getPath();
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
/*解析该APK*/
PackageParser.PackageLite pkg = PackageParser.parsePackageLite(archiveFilePath, 0);
if (pkg == null) {
Slog.w(TAG, "Failed to parse package");

final File apkFile = new File(archiveFilePath);
if (!apkFile.exists()) {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
} else {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
}

return ret;
}
ret.packageName = pkg.packageName;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
/*取得一个合理的安装位置*/
ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
archiveFilePath, flags, threshold);

return ret;
}2. recommandedAppInstallLocation: 获取一个合理的安装位置
1> 当APK的AndroidManifest.xml中设置的位置为AUTO,在具体对应时倾向内部存储空间

2> 用户在Settings数据库中设置的安装位置

3> 检查外部存储或者内部存储是否有足够的空间

private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags,
long threshold) {
int prefer;
boolean checkBoth = false;

check_inner : {
/*
* Explicit install flags should override the manifest settings.
*/
if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
/*
* Forward-locked applications cannot be installed on SD card,
* so only allow checking internal storage.
*/
prefer = PREFER_INTERNAL;
break check_inner;
} else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
prefer = PREFER_INTERNAL;
break check_inner;
} else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
prefer = PREFER_EXTERNAL;
break check_inner;
}

/* No install flags. Check for manifest option. */
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
prefer = PREFER_INTERNAL;
break check_inner;
} else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
prefer = PREFER_EXTERNAL;
checkBoth = true;
break check_inner;
} else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
// We default to preferring internal storage.
prefer = PREFER_INTERNAL;
checkBoth = true;
break check_inner;
}

// Pick user preference
int installPreference = Settings.System.getInt(getApplicationContext()
.getContentResolver(),
Settings.Secure.DEFAULT_INSTALL_LOCATION,
PackageHelper.APP_INSTALL_AUTO);
if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) {
prefer = PREFER_INTERNAL;
break check_inner;
} else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) {
prefer = PREFER_EXTERNAL;
break check_inner;
}

/*
* Fall back to default policy of internal-only if nothing else is
* specified.
*/
prefer = PREFER_INTERNAL;
}

final boolean emulated = Environment.isExternalStorageEmulated();

final File apkFile = new File(archiveFilePath);

boolean fitsOnInternal = false;
if (checkBoth || prefer == PREFER_INTERNAL) {
try {
fitsOnInternal = isUnderInternalThreshold(apkFile, threshold);
} catch (FileNotFoundException e) {
return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
}
}

boolean fitsOnSd = false;
if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
try {
fitsOnSd = isUnderExternalThreshold(apkFile);
} catch (FileNotFoundException e) {
return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
}
}

if (prefer == PREFER_INTERNAL) {
if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
} else if (!emulated && prefer == PREFER_EXTERNAL) {
if (fitsOnSd) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
}

if (checkBoth) {
if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else if (!emulated && fitsOnSd) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
}

/*
* If they requested to be on the external media by default, return that
* the media was unavailable. Otherwise, indicate there was insufficient
* storage space available.
*/
if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)
&& !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
} else {
return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}
经过以上步骤,会得到一个合理的安装位置,下面开始复制文件:
3. InstallArgs的copyAPK方法

1> /data/app/创建一个临时文件,临时文件名为vmdl-随机数.tmp, 用这样的文件名是防止触发PKMS扫描启动。PKMS通过inotify机制监控了文件系统。

int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (temp) {
// Generate temp file name
createCopyFile();
}
// Get a ParcelFileDescriptor to write to the output file
File codeFile = new File(codeFileName);
if (!created) {
try {
codeFile.createNewFile();
// Set permissions
if (!setPermissions()) {
// Failed setting permissions.
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
} catch (IOException e) {
Slog.w(TAG, "Failed to create file " + codeFile);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
}
ParcelFileDescriptor out = null;
try {
out = ParcelFileDescriptor.open(codeFile, ParcelFileDescriptor.MODE_READ_WRITE);
} catch (FileNotFoundException e) {
Slog.e(TAG, "Failed to create file descriptor for : " + codeFileName);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
// Copy the resource now
int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
ret = imcs.copyResource(packageURI, out);
} finally {
try { if (out != null) out.close(); } catch (IOException e) {}
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

return ret;
}

4. handleReturnCode: handleStartCopy执行完后会调用该方法
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}5. processPendingInstall主要完成的工作:
1> 调用InstallArgs的doPreInstall方法; 

2> 调用installPackageLI方法进行APK安装,该方法内部将调用doRename方法对临时文件改名,另外,还需要扫描此APK文件,将此APK信息加入到PKMS的成员中

3> 调用InstallArgs的doPostInstall方法

4> APK安装完成,向mHandler发送POST_INSTALL消息,该消息标记一个token,通过它可以从mRunningInstalls数组中取得一个PostInstallData对象

private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
// Result object to be returned
/*创建一个PackageInstalledInfo对象*/
PackageInstalledInfo res = new PackageInstalledInfo();
res.returnCode = currentStatus;
res.uid = -1;
res.pkg = null;
res.removedInfo = new PackageRemovedInfo();
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
/*调用FileInstallArgs的doPreInstall*/
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
/*调用installPackageLI进行安装*/
installPackageLI(args, true, res);
}
/*调用FileInstallArgs的doPostInstall*/
args.doPostInstall(res.returnCode);
}

// A restore should be performed at this point if (a) the install
// succeeded, (b) the operation is not an update, and (c) the new
// package has a backupAgent defined.
final boolean update = res.removedInfo.removedPackage != null;
boolean doRestore = (!update
&& res.pkg != null
&& res.pkg.applicationInfo.backupAgentName != null);

// Set up the post-install work request bookkeeping. This will be used
// and cleaned up by the post-install event handling regardless of whether
// there's a restore pass performed. Token values are >= 1.
int token;
if (mNextInstallToken < 0) mNextInstallToken = 1;
token = mNextInstallToken++;

PostInstallData data = new PostInstallData(args, res);
/*保存对象到mRunningInstalls*/
mRunningInstalls.put(token, data);
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);

if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
// Pass responsibility to the Backup Manager. It will perform a
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
if (DEBUG_INSTALL) Log.v(TAG, "token " + token
+ " to BM for possible restore");
try {
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} catch (RemoteException e) {
// can't happen; the backup manager is local
} catch (Exception e) {
Slog.e(TAG, "Exception trying to enqueue restore", e);
doRestore = false;
}
} else {
Slog.e(TAG, "Backup Manager not found!");
doRestore = false;
}
}

if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
/*发一个消息给mHandler*/
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
});
}

6. POST_INSTALL
/*省略*/
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
PostInstallData data = mRunningInstalls.get(msg.arg1);
mRunningInstalls.delete(msg.arg1);
boolean deleteOld = false;

if (data != null) {
InstallArgs args = data.args;
PackageInstalledInfo res = data.res;

if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
res.removedInfo.sendBroadcast(false, true);
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, res.uid);
final boolean update = res.removedInfo.removedPackage != null;
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
/*发送PACKAGE_ADDED广播*/
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
extras, null, null);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
extras, null, null);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
res.pkg.applicationInfo.packageName, null);
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
deleteOld = true;
}
}
// Force a gc to clear up things
Runtime.getRuntime().gc();
// We delete after a gc for applications on sdcard.
if (deleteOld) {
synchronized (mInstallLock) {
res.removedInfo.args.doPostDeleteLI(true);
}
}
if (args.observer != null) {
try {
/*通知pm安装结果*/
args.observer.packageInstalled(res.name, res.returnCode);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
} else {
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
} break;
/*省略*/至此,APK安装完成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: