继续上一章讲解。
在Android中,通过发送Intent,就可以启动应用的安装过程,如下所示:
Uri uri = Uri.fromFile(new File(fileName));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri, "application/vnd.android.package-archive");
startActivity(intent);
在Android的系统应用PackageInstaller中有一个PackageInstallerActivity会响应这个Intent。在这个Activity中,有两个重要的成员变量mPm,是ApplicationPackageManager的实例对象,也是PackageManagerService在应用中的代理对象。创建代码如下:
mPm = getPackageManager();
一、管理“安装会话”—-PackageInstallerService
PackageInstallerService是Android5.0新加入的服务,主要用于管理“安装会话(Installer Session)”。在Android5.0中,可以通过PackageInstallerService来分配一个SessionId,这个系统唯一的ID代表一次安装过程,如果一个应用的安装必须分成几个阶段来完成,即使设备重启了,也可以通过这个ID来继续安装过程。
PackageInstallerService中提供了接口createSession()创建一个Session:
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
createSession方法将返回一个系统唯一值作为SessionID。如果希望再次使用这个Session,可以通过接口openSession打开它,代码如下:
public IPackageInstallerSession openSession(int sessionId) {
try {
return openSessionInternal(sessionId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
if (session == null || !isCallingUidOwner(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
session.open();
return session;
}
}
openSession方法返回一个IPackageInstallerSession对象,它是Binder服务PackageInstallerSession的IBinder对象。在PackageInstallerService中mSessions数组保存了所有PackageInstallerSession对象,代码如下:
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
当系统启动时,PackageManagerService初始化时会创建PackageInstallerService服务,在这个服务的初始化函数中,会读取/data/system目录下install_sessions.xml文件,这个文件中保存了系统中未完成的Install Session。PackageInstallerService会根据文件的内容创建PackageInstallerSession对象并插入到mSessions中。
PacakgeInstallerSession中保存了应用安装相关的数据,如,安装包的路径、安装的进度、中间数据保存的目录等。
二、应用安装第一阶段:复制文件
应用中可以调用PackageManager的installPackage()方法来开始安装过程,这个方法会调用PackageManagerService的installPackage()方法或installPackageAsUser()接口来执行安装过程。整个过程比较复杂,我们先看看安装序列图,如下:
应用安装的过程大概分成两个阶段,第一阶段把需要安装的应用复制到/data/app目录下,第二阶段是对apk文件进行扫描优化,然后装载到内存中。
PackageManagerService的installPackage方法用来给当前用户安装应用,而installPackageAsUser()方法给指定的用户安装应用。installPackage()方法只是使用当前用户的uid来调用installPackageAsUser()方法。代码如下:
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);//检查调用进程的权限
final int callingUid = Binder.getCallingUid();//检查调用进程的用户是否有权限安装应用
enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {//检查指定的用户是否被限制安装应用
try {
if (observer != null) {
observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
}
} catch (RemoteException re) {
}
return;
}
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
installFlags |= PackageManager.INSTALL_FROM_ADB;//调用进程uid为0或2000,设置installFlags
} else {
// Caller holds INSTALL_PACKAGES permission, so we're less strict
// about installerPackageName.
installFlags &= ~PackageManager.INSTALL_FROM_ADB;
installFlags &= ~PackageManager.INSTALL_ALL_USERS;
}
UserHandle user;
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {//如果installFlags带有标记INSTALL_ALL_USERS,则给所有用户安装
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
verificationParams.setInstallerUid(callingUid);
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
//保存参数到InstallParams对象并发送消息
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, observer, installFlags,
installerPackageName, verificationParams, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
installPackageAsUser()方法首先检查调用进程是否有安装应用的权限,再检查调用进程的所属用户是否有权限安装应用,
最后检查指定的用户是否被限制安装应用。如果参数installFlags带有标记INSTALL_ALL_USERS,则该应用将给系统中
所有用户安装,否则只给指定的用户安装。
installPackageAsUser()方法,把调用的参数保存在InstallParams对象中,然后发送INIT_COPY消息。
InstallParams是安装过程中主要的数据结构,从HandleParams类派生,用来记录安装应用的参数。
从HandleParams派生的还有MoveParams和MeasureParams,MoveParams用于将应用移动到SD卡,
MeasureParams用于方法getPackageSizeInfo中。InstallParams成员变量mArgs,是FileInstallArgs类型,用来执行
apk文件的复制。
我们看下INIT_COPY消息的处理,在doHandleMessage()方法中,如下:
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
// If a bind was already initiated we dont really
// need to do anything. The pending install
// will be processed later on.
if (!mBound) {
// If this is the only one pending we might
// have to bind to the service again.
if (!connectToService()) {//绑定DefaultContainerService服务
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();
return;//连接服务失败,退出
} else {
// Once we bind to the service, the first连接成功,把安装信息保存到mPedingInstalls中
// pending request will be processed.
mPendingInstalls.add(idx, params);//待收到连接的返回消息后再继续安装
}
} else {
mPendingInstalls.add(idx, params);//插入安装信息
// Already bound to the service. Just make
// sure we trigger off processing the first request.
if (idx == 0) {//如果mPendingInstalls只有一项,立刻发MCS_BOUND消息
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
在INIT_COPY消息的处理中
将绑定DefaultContainerService,因为这是一个异步的过程,要等待绑定的结果通过onServiceConnected()返回,所以,这里将安装的参数信息放到mPendingInstalls列表中。如果这个service以前就绑定好了,现在不在需要再绑定,安装信息也会先放到mPendingInstalls中。如果有多个安装请求同时到达,通过mPendingInstalls列表可以对它们进行排队。如果列表中只有一项,说明没有更多的安装请求,此时会立即
发送MCS_BOUND消息,进入下一步的处理。而onServiceConnected()方法的处理同样是发送MCS_BOUND消息,如下:
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
IMediaContainerService imcs =
IMediaContainerService.Stub.asInterface(service);
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
};
我们看下MCS_BOUND消息是怎么处理的,如下:
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {//如果DefaultContainerService没有连接成功
// Something seriously wrong. Bail out
Slog.e(TAG, "Cannot bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();//通过参数中带的回调接口通知调用者出错了
}
mPendingInstalls.clear();
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
if (params.startCopy()) {//执行安装
// We are done... look for more work or to
// go idle.
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Checking for more work or unbind...");
// Delete pending install
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);//工作完成,删除第一项
}
if (mPendingInstalls.size() == 0) {
if (mBound) {//如果没有安装信息了,发送延时10秒的MCS_UNBIND消息
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting delayed MCS_UNBIND");
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
// Unbind after a little delay, to avoid
// continual thrashing.
sendMessageDelayed(ubmsg, 10000);
}
} else {
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.mPendingInstalls还有安装信息,发送MCS_BOUND消息
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting MCS_BOUND for next work");
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
MCS_BOUND消息的处理过程就是调用InstallParams类的startCopy()方法来执行安装。只要mPendingInstalls中还有安装信息,就会重复发送MCS_BOUND消息,直到所有的应用都安装完毕,然后发送一个延时10秒的MCS_UNBIND消息。
MCS_UNBIND消息的处理如下:
case MCS_UNBIND: {
// If there is no actual work left, then time to unbind.
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_unbind");
if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) {
if (mBound) {
if (DEBUG_INSTALL) Slog.i(TAG, "calling disconnectService()");
disconnectService();//断开与DefaultContainerService的连接
}
} else if (mPendingInstalls.size() > 0) {
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
mHandler.sendEmptyMessage(MCS_BOUND);
}
break;
}
处理MCS_UNBIND消息时,如果发现mPendingInstalls列表中又有数据了,则发送MCS_BOUND消息继续安装;否则断开和DefaultContainerService的连接,安装过程就此结束。
继续分析下start_copy()方法,InstallParams类继承HandlerParams抽象类,InstallParams类没有重写startCopy方法,因此调用HandlerParams的startCopy方法,如下:
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
if (++mRetries > MAX_RETRIES) {//如果重试超过4次,退出;MAX_RETRIES=4
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);//发送MCS_GIVE_UP消息
handleServiceError();
return false;
} else {
handleStartCopy();//执行安装
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);//安装出错,发送MCS_RECONNECT消息,重新连接
res = false;
}
handleReturnCode();
return res;
}
startCopy()方法通过调用handleStartCopy方法来完成安装过程。但是考虑到安装过程的不确定性,
startCopy()主要的工作是进行错误处理,如果捕获到handleStartCopy()方法抛出的异常,startCopy方法将发送消息MCS_RECONNECT。
case MCS_RECONNECT: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
if (mPendingInstalls.size() > 0) {
if (mBound) {
disconnectService();
}
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
}
mPendingInstalls.clear();
}
}
break;
}
在MCS_RECONNECT的处理中,会重新绑定DefaultContainerService,如果绑定成功,安装过程就会重新开始,startCopy方法会被再次调用。重试的次数记录在mRetries变量中,
如果超过4次,安装失败。如果安装成功,调用handleReturnCode方法继续处理。先分析下handleStartCopy方法,如下:
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else if (origin.cid != null) {
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
PackageInfoLite pkgLite = null;
if (onInt && onSd) {//如果既有安装在内部的标志,又有安装在SD卡上的标志,设置错误后返回
// Check if both bits are set.
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);//获取安装包包含有应用信息的PackageInfoLite对象
/*
* If we have too little free space, try to free cache
* before giving up.如果安装的位置空间不够,释放cache空间
*/
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
final StorageManager storage = StorageManager.from(mContext);
long lowThreshold = storage.getStorageLowBytes(
Environment.getDataDirectory());
final long sizeBytes = mContainerService.calculateInstalledSize(
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
}
/*
* The cache free must have deleted the file we
* downloaded to install.
*
* TODO: fix the "freeCache" call to not delete
* the file we care about.
*/
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}
}
......
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
/*
* ADB installs appear as UserHandle.USER_ALL, and can only be performed by
* UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
*/
int userIdentifier = getUser().getIdentifier();
if (userIdentifier == UserHandle.USER_ALL
&& ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
userIdentifier = UserHandle.USER_OWNER;
}
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, userIdentifier);
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(userIdentifier, installFlags)) {//执行应用的校验,方法是通过向带校验功能的组件发Intent来完成。
…… } else { /* * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. *///如果无需校验,调用IntallArgs的copyApk方法继续处理 ret = args.copyApk(mContainerService, true); } } mRet = ret; }
handleStartCopy()方法先调用getMinimalPackageInfo()来确定安装位置是否还有足够空间,并在PackageInfoLite对象的recommendedInstallLocation变量中记录错误原因。发现安装空间不够后,handleStartCopy()方法会调用installer的freeCache()方法来释放一部分空间。
接下来handleStartCopy()方法会调用createInstallArgs()来创建InstallArgs对象,在createInstallArgs()方法中,如果应用要求安装在SD卡上,或者是一个forward lock的应用,则创建AsecInstallArgs对象,否则创建FileInstallArgs对象。这样安装流程将会分化,这里主要分析下FileInstallArgs。如下:
private InstallArgs createInstallArgs(InstallParams params) {
if (installOnSd(params.installFlags) || params.isForwardLocked()) {
return new AsecInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
}
接下来handleStartCopy()方法处理APK的校验,这个校验的过程是通过发送Intent.ACTION_PACKAGE_NEEDS_VERIFICATION给系统中所有接收该Intent的应用来完成的。如果无需校验,则直接调用InstallArgs对象的copyApk()方法,FileInstallArgs继承了InstallArgs类,这里调用的是FileInstallArgs类的copyApk()方法,如下:
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (origin.staged) {
Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
try {//在/data/app/下生成临时文件
final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
//为临时文件创建文件描述符ParcelFileDescriptor,它能通过Binder传递
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid filename: " + name);
}
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
}
};
//使用服务DefaultContainerService的copyPackage()方法复制文件
int ret = PackageManager.INSTALL_SUCCEEDED;
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
//安装应用中自带的native的动态库
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
return ret;
}
copyApk()同样是
调用DefaultContainerService的copyPackage()将应用的文件复制到/data/app下。如果应用中还有native的动态库,也会把包在apk文件中的动态库的文件提取出来。执行完copyApk后,安装的第一阶段工作就完成了,
应用安装到了/data/app目录下。
三、装载应用
第二阶段就是将应用的格式转化为oat格式,为应用创建数据目录,最后把应用的信息装载进PMS的数据结构中。
在前面的startCopy()方法中已经看到,最后它会调用handleReturnCode()方法,如下:
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);
}
}
handleReturnCode()方法只是调用了processpendingInstall()方法,如下:
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 res = new PackageInstalledInfo();
res.returnCode = currentStatus;
res.uid = -1;
res.pkg = null;
res.removedInfo = new PackageRemovedInfo();
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {//如果应用安装成功了
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageLI(args, res);//装载安装的应用
}
args.doPostInstall(res.returnCode, res.uid);
}
// 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 not opted out of backup participation.
final boolean update = res.removedInfo.removedPackage != null;
final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
boolean doRestore = !update
&& ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
// 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.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 {
if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
}
} 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) {//发送POST_INSTALL消息
// 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);
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
});
}
processPendingInstall()方法中
post了一个消息,这样安装过程将以异步的方式继续执行。在post消息的处理中,首先调用installPackageLI()来装载应用,接下来的一大段代码都是执行备份操作,备份是通过BackupManagerService来完成的。备份完成后,通过
发送POST_INSTALL消息来继续处理。先分析下installPackageLI()方法,如下:
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
final int installFlags = args.installFlags;
String installerPackageName = args.installerPackageName;
File tmpPackageFile = new File(args.getCodePath());
boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);
boolean replace = false;
final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
// Result object to be returned
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
if (true != mSecurityBridge.approveAppInstallRequest(
args.getResourcePath(),
Uri.fromFile(args.origin.file).toSafeString())) {
res.returnCode = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
return;
}
// Retrieve PackageSettings and parse package创建apk文件的解析器
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
final PackageParser.Package pkg;
try {//解析apk文件
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
}
// Mark that we have an install time CPU ABI override.
pkg.cpuAbiOverride = args.abiOverride;
String pkgName = res.name = pkg.packageName;
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
return;
}
}
if (android.app.AppOpsManager.isStrictEnable()
&& ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)
&& Secure.getInt(mContext.getContentResolver(),
Secure.INSTALL_NON_MARKET_APPS, 0) <= 0) {
res.returnCode = PackageManager.INSTALL_FAILED_UNKNOWN_SOURCES;
return;
}
//收集应用的签名
try {
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
/* If the installer passed in a manifest digest, compare it now. */
if (args.manifestDigest != null) {
if (DEBUG_INSTALL) {
final String parsedManifest = pkg.manifestDigest == null ? "null"
: pkg.manifestDigest.toString();
Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
+ parsedManifest);
}
if (!args.manifestDigest.equals(pkg.manifestDigest)) {
res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");
return;
}
} else if (DEBUG_INSTALL) {
final String parsedManifest = pkg.manifestDigest == null
? "null" : pkg.manifestDigest.toString();
Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);
}
// 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 ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
String oldName = mSettings.mRenamedPackages.get(pkgName);
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
// This package is derived from an original package,
// and this device has been updating from that original
// name. We must continue using the original name, so
// rename the new package here.
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
+ oldName + " pkgName=" + pkgName);
} else if (mPackages.containsKey(pkgName)) {
// This package, under its official name, already exists
// on the device; we should replace it.
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
}
}
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
// Quick sanity check that we're signed correctly if updating;
// we'll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {
try {
verifySignaturesLP(ps, pkg);
} catch (PackageManagerException e) {
res.setError(e.error, e.getMessage());
return;
}
} else {
if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
}
//判断安装的应用是否存在同名的应用,如果存在,检查应用是否带有系统标志
oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
if (ps.pkg != null && ps.pkg.applicationInfo != null) {
systemApp = (ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0;
}
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
。。。。。。
installPackageLI()方法首先解析了安装的应用文件,得到解析的结果后,主要是判断新安装的应用是否和系统中已安装的应用构成升级关系,如果是,调用replacePackageLI()方法继续处理,否则调用installNewPackageLI()方法处理。
再接着看下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, false);
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, res.uid);
。。。。。。
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
extras, null, null, firstUsers);//发送ACTION_PACKAGE_ADDED广播
final boolean update = res.removedInfo.removedPackage != null;
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
extras, null, null, updateUsers);
if (update) {//如果是升级包,还会发送更多的广播
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
extras, null, null, updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
res.pkg.applicationInfo.packageName, null, updateUsers);
// treat asec-hosted packages like removable media on upgrade
if (isForwardLocked(res.pkg) || isExternal(res.pkg)) {//如果是forward lock应用,或安装在SD卡上的应用,发送广播
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + res.pkg
+ " is ASEC-hosted -> AVAILABLE");
}
int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
ArrayList<String> pkgList = new ArrayList<String>(1);
pkgList.add(res.pkg.applicationInfo.packageName);
sendResourcesChangedBroadcast(true, true,
pkgList,uidArray, null);
}
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
deleteOld = true;
}
// Log current value of "unknown sources" setting
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
getUnknownSourcesSettings());
}
// 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 {//调用参数中的回调接口,通知调用者安装的结果
Bundle extras = extrasForInstallResult(res);
args.observer.onPackageInstalled(res.name, res.returnCode,
res.returnMsg, extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
} else {
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
sLatestSecureContainerList = PackageHelper.getSecureContainerList();
}
POST_INSTALL消息的处理主要就是发送广播,应用安装完成后要通知系统中其他的应用开始处理,例如,Launcher中需要增加应用的图标等。发完广播安装也就结束了。
最后通过参数中的回调接口通知调用者安装的结果。
四、卸载应用
PMS还会监视应用目录的变化,做出相应的处理。
卸载应用的接口是deletePackage(),如下:
public void deletePackage(final String packageName,
final IPackageDeleteObserver2 observer, final int userId, final int flags) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);//检查执行权限
final int uid = Binder.getCallingUid();
if (UserHandle.getUserId(uid) != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"deletePackage for user " + userId);
}
if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {
try {//如果用户权限不够,发送错误信息
observer.onPackageDeleted(packageName,
PackageManager.DELETE_FAILED_USER_RESTRICTED, null);
} catch (RemoteException re) {
}
return;
}
boolean uninstallBlocked = false;
if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
int[] users = sUserManager.getUserIds();
for (int i = 0; i < users.length; ++i) {
if (getBlockUninstallForUser(packageName, users[i])) {
uninstallBlocked = true;
break;
}
}
} else {
uninstallBlocked = getBlockUninstallForUser(packageName, userId);
}
if (uninstallBlocked) {
try {
observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED,
null);
} catch (RemoteException re) {
}
return;
}
if (DEBUG_REMOVE) {
Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId);
}
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {//post消息,在消息处理方法中执行卸载应用的操作
public void run() {
mHandler.removeCallbacks(this);
final int returnCode = deletePackageX(packageName, userId, flags);
if (observer != null) {
try {
observer.onPackageDeleted(packageName, returnCode, null);//发送卸载应用的结果
} catch (RemoteException e) {
Log.i(TAG, "Observer no longer exists.");
} //end catch
} //end if
} //end run
});
}
deletePackage()方法检查调用者的权限后,post了一个消息。在消息的处理方法中继续执行卸载操作,这样就避免了Binder调用时间过长。实际的卸载是通过调用deletePackageX()方法完成的,如下:
private int deletePackageX(String packageName, int userId, int flags) {
final PackageRemovedInfo info = new PackageRemovedInfo();
final boolean res;
final UserHandle removeForUser = (flags & PackageManager.DELETE_ALL_USERS) != 0
? UserHandle.ALL : new UserHandle(userId);
//检查userId代表的用户是否有权限删除这个应用
if (isPackageDeviceAdmin(packageName, removeForUser.getIdentifier())) {
Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
}
boolean removedForAllUsers = false;
boolean systemUpdate = false;
// for the uninstall-updates case and restricted profiles, remember the per-
// userhandle installed state
int[] allUsers;
boolean[] perUserInstalled;
synchronized (mPackages) {
PackageSetting ps = mSettings.mPackages.get(packageName);//获取应用在mSettings中的包的信息
allUsers = sUserManager.getUserIds();
perUserInstalled = new boolean[allUsers.length];
for (int i = 0; i < allUsers.length; i++) {//检查并记录系统中所有用户是否都安装了这个应用
perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;
}
}
synchronized (mInstallLock) {
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
res = deletePackageLI(packageName, removeForUser,
true, allUsers, perUserInstalled,
flags | REMOVE_CHATTY, info, true);//调用deletePackageLI方法来卸载应用
systemUpdate = info.isRemovedPackageSystemUpdate;
if (res && !systemUpdate && mPackages.get(packageName) == null) {
removedForAllUsers = true;
}
if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate
+ " removedForAllUsers=" + removedForAllUsers);
}
if (res) {
info.sendBroadcast(true, systemUpdate, removedForAllUsers);
// If the removed package was a system update, the old system package
// was re-enabled; we need to broadcast this information
if (systemUpdate) {//如果卸载的应用是某个系统应用的升级包,卸载它将导致低版本的系统应用重新使用;因此需要发送广播通知这种变化
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
? info.removedAppId : info.uid);
extras.putBoolean(Intent.EXTRA_REPLACING, true);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, null, null, null);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
extras, null, null, null);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
null, packageName, null, null);
}
}
// Force a gc here.
Runtime.getRuntime().gc();
// Delete the resources here after sending the broadcast to let
// other processes clean up before deleting resources.
if (info.args != null) {
synchronized (mInstallLock) {
info.args.doPostDeleteLI(true);//调用FileInstallArgs的方法做清理工作
}
}
return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
deletePackageX()方法收集了应用在不同用户下的安装情况后,调用deletePackageLI方法继续卸载。
如果删除的是某个系统应用的升级包,这里还会发出广播通知以前的应用又重新使用了。deletePackageLI方法如下:
private boolean deletePackageLI(String packageName, UserHandle user,
boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,
int flags, PackageRemovedInfo outInfo,
boolean writeSettings) {
if (packageName == null) {
Slog.w(TAG, "Attempt to delete null packageName.");
return false;
}
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
PackageSetting ps;
boolean dataOnly = false;
int removeUser = -1;
int appId = -1;
synchronized (mPackages) {
ps = mSettings.mPackages.get(packageName);
if (ps == null) {
Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
}
if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
&& user.getIdentifier() != UserHandle.USER_ALL) {
// 如果调用者只要求卸载某个用户下的应用
if (DEBUG_REMOVE) Slog.d(TAG, "Only deleting for single user");
ps.setUserState(user.getIdentifier(),
COMPONENT_ENABLED_STATE_DEFAULT,
false, //installed
true, //stopped
true, //notLaunched
false, //hidden
null, null, null,
false // blockUninstall
);
if (!isSystemApp(ps)) {
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
// 如果还有其他用户在使用这个应用,删除应用在这个被删除用户下的数据
if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
removeUser = user.getIdentifier();
appId = ps.appId;
mSettings.writePackageRestrictionsLPr(removeUser);
} else {
// We need to set it back to 'installed' so the uninstall
// broadcasts will be sent correctly.没有用户使用了,全部删除应用
if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
ps.setInstalled(true, user.getIdentifier());
}
} else {
// 系统应用,可能别的用户还在使用它;因此,这里只清除应用在需要删除的用户目录下的数据目录;
if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
removeUser = user.getIdentifier();
appId = ps.appId;
mSettings.writePackageRestrictionsLPr(removeUser);
}
}
}
。。。。。。
if (dataOnly) {
// Delete application data first,先删除应用的数据
if (DEBUG_REMOVE) Slog.d(TAG, "Removing package data only");
removePackageDataLI(ps, null, null, outInfo, flags, writeSettings);
return true;
}
boolean ret = false;
if (isSystemApp(ps)) {//删除系统应用
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package:" + ps.name);
// When an updated system application is deleted we delete the existing resources as well and
// fall back to existing code in system partition
ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,
flags, outInfo, writeSettings);
} else {//停止应用进程,删除文件
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package:" + ps.name);
// Kill application pre-emptively especially for apps on sd.
killApplication(packageName, ps.appId, "uninstall pkg");
ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,
allUserHandles, perUserInstalled,
outInfo, writeSettings);
}
return ret;
}
deletePackageLI方法根据不同用户的安装情况来删除应用的apk文件和数据。
五、通过Intent查询组件
PMS在系统运行过程中,很重要的一项工作就是根据Intent来查询处理Intent的组件信息。处理Intent查询的接口如下:
- queryIntentActivities():查询处理Intent的Activity列表。
- queryIntentServices():查询处理Intent的Service列表。
- queryIntentReceivers():查询处理Intent的Receiver列表。
- queryIntentConentProviders():查询处理Intent的ContentProvider列表。
系统中响应某个Intent的组件可能有多个,因此,这些接口返回的是列表。查询到的组件信息都是用类ResolverInfo来表示的。Android定义了ResolveInfo类来表示所有组件。 以queryIntentActivities()为例分析查询过程。
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();//检查调用接口的用户的权限
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {//如果Intent中已经指定了模块和组件名称,则只会有一个匹配
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);//通过模块信息得到ActivityInfo
if (ai != null) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
// reader
synchronized (mPackages) {
final String pkgName = intent.getPackage();
if (pkgName == null) {//如果intent中没有包名,则在系统所有包中查找
List<CrossProfileIntentFilter> matchingFilters =
getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);//匹配所有的IntertFilter
// Check for results that need to skip the current profile.首先检查带有SKIP_CURRENT_PROFILE标志的filter
ResolveInfo resolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (resolveInfo != null) {
List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
result.add(resolveInfo);
return result;
}
// Check for cross profile results.
resolveInfo = queryCrossProfileIntents(
matchingFilters, intent, resolvedType, flags, userId);
// Check for results in the current profile.在当前的profile下查找
List<ResolveInfo> result = mActivities.queryIntent(
intent, resolvedType, flags, userId);
if (resolveInfo != null) {
result.add(resolveInfo);
Collections.sort(result, mResolvePrioritySorter);
}
return result;
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {//如果Intent中有包名,则在指定的包中查找
return mActivities.queryIntentForPackage(intent, resolvedType, flags,
pkg.activities, userId);
}
return new ArrayList<ResolveInfo>();
}
}
queryIntentActivities()方法会根据Intent中的信息来分别处理。如果Intent中有包名和Activity的信息,在调用getActivityInfo()直接返回ActivityInfo。如果只有包名,调用queryIntentForPackage方法在指定的包中查找Activity。如果包名也没有,调用queryIntent()方法来搜索所有安装包。
下面我们再看下installd进程的理解:
Android应用管理三 — APK包的安装、卸载和优化(PackageManagerService)