研究应用的安装过程,老样子,我们还是先从使用入手。
在Android
中,通过发送Intent
就可以启动应用的安装过程,比如:
Uri uri = Uri.fromFile(new File(apkFilePath));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri,"application/vnd.android.package-archive");
startActivity(intent);
而在Android
的系统应用PackageInstaller
中有一个InstallStart
会响应这个Intent
,
<activity android:name=".InstallStart"
android:theme="@style/Installer"
android:exported="true"
android:excludeFromRecents="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
......
</activity>
在InstallStart
中会进行各种Uri
的判断,最终会跳转到一个叫做PackageInstallerActivity
的界面。
国内的厂商基本上会在
InstallStart
这里进行修改,替换为自己的安装界面
对于PackageInstallerActivity
来说,它的主要作用是
- 解析
Uri
协议进行解析,包括file
和package
两种- 如果是
file
协议会解析APK
文件得到包信息PackageInfo
- 如果是
- 对未知来源进行处理
- 如果允许安装未知来源或者根据
Intent
判断得出该APK
不是未知来源,就会初始化安装确认界面 - 如果管理员限制来自未知源的安装, 就弹出提示
Dialog
或者跳转到设置界面
- 如果允许安装未知来源或者根据
- 提取并在界面展示应用对应的权限
当点击确认安装后,其实会执行到startInstall()
方法,相关内容如下:
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
......
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallInstalling.class);
......
startActivity(newIntent);
finish();
}
上面的逻辑是跳转到InstallInstalling
界面进行安装,我们看看这个Activity
:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
......
if ("package".equals(mPackageURI.getScheme())) {
// 直接安装 package
try {
getPackageManager().installExistingPackage(appInfo.packageName);
launchSuccess();
} catch (PackageManager.NameNotFoundException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
// 对于 file 安装,按照session的逻辑去走
// 这部分在onResume中开始
final File sourceFile = new File(mPackageURI.getPath());
......
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getInt(SESSION_ID);
mInstallId = savedInstanceState.getInt(INSTALL_ID);
......
} else {
......
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
......
// 创建应用安装状态的Observer
// 真正注册是在后面 Session 的 commitLocked 中
// InstallEventReceiver 这里是做了二次封装,方便进行持久化操作
InstallEventReceiver.addObserver(this, mInstallId,
this::launchFinishBasedOnResult);
}
......
}
}
@Override
protected void onResume() {
super.onResume();
// This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
// 启动异步安装任务 InstallingAsyncTask 进行一系列的session操作
if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
}
......
}
}
从上面的方法我们可以看到安装操作主要分为两个接口:
-
getPackageManager().installExistingPackage()
:主要是对已经并扫描到Settings
的mPackages
集合中的应用进行操作- 根据当前的
userID
从集合中查询应用状态,如果存在并且应用没有安装,就进行一些简单的配置工作 - 最后通过
prepareAppDataAfterInstallLIF
方法,创建特定userID
下的应用数据
- 根据当前的
-
InstallingAsyncTask
任务中的Session
系列操作:一个应用完整的安装过程从这里开始private final class InstallingAsyncTask extends AsyncTask<Void, Void, PackageInstaller.Session> { volatile boolean isDone; @Override protected PackageInstaller.Session doInBackground(Void... params) { PackageInstaller.Session session; ...... session = getPackageManager().getPackageInstaller().openSession(mSessionId); try { File file = new File(mPackageURI.getPath()); try (InputStream in = new FileInputStream(file)) { long sizeBytes = file.length(); try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) { byte[] buffer = new byte[1024 * 1024]; while (true) { ...... out.write(buffer, 0, numRead); ...... } } } return session; } ...... } @Override protected void onPostExecute(PackageInstaller.Session session) { if (session != null) { ...... session.commit(pendingIntent.getIntentSender()); ...... } else { getPackageManager().getPackageInstaller().abandonSession(mSessionId); ...... } }
- 任务中做的第一个事情是:根据
APK
的Uri
,将APK
的信息通过IO
流的形式写入到PackageInstaller.Session
中 - 第二个事情就是
PackageInstallerSession.commit()
函数
- 任务中做的第一个事情是:根据
session
这个东西看来很重要,那么我们先来看看PackageInstallerSession
吧
PackageInstallerSession
Android
通过PackageInstallerSession
来表示一次安装过程,一个PackageInstallerSession
包含一个系统中唯一的一个SessionId
,如果一个应用的安装必须分几个阶段来完成,即使设备重启了,也可以通过这个ID
来继续安装过程
PackageInstallerSession
中保存了应用安装的相关数据,例如,安装包的路径、安装的进度、中间数据保存的目录等。
PackageInstallerService
提供了接口createSession
来创建一个Session
对象:
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
createSession()
方法将返回一个系统唯一值作为Session ID
。如果希望再次使用这个Session
,可以通过接口openSession()
打开它。openSession()
方法的代码如下所示:
@Override
public IPackageInstallerSession openSession(int sessionId) {
try {
return openSessionInternal(sessionId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
openSession()
方法将返回一个IPackageInstallerSession
对象,它是Binder
服务PackageInstallerSession
的IBinder
对象。在PackageInstallerService
中mSessions
数组保存了所有PackageInstallerSession
对象,定义如下:
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
我们知道,当PackageManagerService
初始化时会创建PackageInstallerService
服务,在这个服务的初始化函数中会读取/data/system
目录下的install_sessions.xml
文件,这个文件中保存了系统中未完成的Install Session
。然后PackageInstallerService
会根据文件的内容创建PackageInstallerSession
对象并插入到mSessions
中。
而对于上面提到的commit()
函数,是真正触发PMS
安装的函数,定义如下:
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
......
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
......
}
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
......
case MSG_COMMIT:
synchronized (mLock) {
......
commitLocked();
......
}
break;
......
}
return true;
}
};
private void commitLocked()
throws PackageManagerException {
......
mRemoteObserver.onUserActionRequired(intent);
......
mPm.installStage(mPackageName, stageDir, localObserver, params,
mInstallerPackageName, mInstallerUid, user, mSigningDetails);
}
commit()
函数最后会调用到PMS
的installStage()
方法,到这里就触发了安装的第一阶段:文件复制
安装第一阶段:复制文件
我们已经知道PackageInstallerSession
通过调用PMS
的installStage()
方法开启了安装第一阶段,不过整个安装过程比较复杂,我们先看看这个过程的序列图:
installStage()
我们继续从installStage()
来分析,代码如下:
void installStage(String packageName, File stagedDir,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
PackageParser.SigningDetails signingDetails) {
......
final Message msg = mHandler.obtainMessage(INIT_COPY);
......
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, signingDetails, installReason);
......
msg.obj = params;
......
mHandler.sendMessage(msg);
}
installStage()
方法主要进行了:
- 创建了一个
InstallParams
对象-
InstallParams
是安装过程中的主要数据结构 - 一般情况,安装应用的时间通常比较长,因此
Android
把安装过程拆分,把调用过程的参数数据保存到InstallParams
中
-
- 创建并发送了一个
Message
消息-
msg.what
为INIT_COPY
-
msg.obj
为InstallParams
对象
-
先来简单了解下InstallParams
的类:
上图中的类都是PMS的内部类
-
InstallParams
继承自HandlerParams
,用来记录安装应用的参数。 -
InstallParams
中有一个成员变量mArgs
,是一个抽象类型InstallArgs
,主要是用来执行APK
的复制,真正的实现类包括:-
FileInstallArgs
:用来完成非ASEC
应用的安装-
asec
全称是Android Secure External Cache
-
-
MoveInstallArgs
:用来完成已安装应用的移动安装
-
简单了解完InstallParams
,我们继续看INIT_COPY
消息的处理过程:
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
......
if (!mBound) {
// 绑定 DefaultContainerService
if (!connectToService()) {
params.serviceError();
......
return;
} else {
// 连接成功,将安装信息保存到 mPendingInstalls 的尾部
mPendingInstalls.add(idx, params);
}
} else {
// 如果已经绑定服务,直接插入到集合尾部
mPendingInstalls.add(idx, params);
if (idx == 0) {
// 如果插入前的集合为空
// 直接发送 MCS_BOUND 消息
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
整个过程比较简洁:
- 首先是
connectToService()
去绑定和启动DefaultContainerService
服务。- 服务绑定是一个异步的过程,需要等待绑定结果通过
onServiceConnected()
返回 - 从
DefaultContainerConnection
中可以看到,当服务连接成功后,会发送MSC_BOUNF
消息
class DefaultContainerConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { final IMediaContainerService imcs = IMediaContainerService.Stub .asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); } }
- 服务绑定是一个异步的过程,需要等待绑定结果通过
- 然后将安装的参数信息放到了
mPendingInstalls
列表中。- 当有多个安装请求到达时,可以通过
mPendingInstalls
进行排队
- 当有多个安装请求到达时,可以通过
- 如果添加前的列表为空,说明没有其他的安装请求,此时直接发送
MSC_BOUND
消息
那么,重点又转移到了MCS_BOUND
的消息处理,代码如下:
case MCS_BOUND: {
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
if (!mBound) {
// DefaultContainerService bind失败的情况
for (HandlerParams params : mPendingInstalls) {
params.serviceError();
......
}
mPendingInstalls.clear();
} else {
Slog.w(TAG, "Waiting to connect to media container service");
}
} else if (mPendingInstalls.size() > 0) {
// 取出头部安装参数
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
......
if (params.startCopy()) {// 执行安装
// 已执行成功,移除头部元素
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
......
// 如果集合中没有需要安装的信息了,发送MCS_UNBIND延时消息进行解绑
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);
}
} else {
// 如果集合中还存在安装信息,发送 MCS_BOUND 消息,继续处理
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
}
break;
}
MCS_BOUND
消息的处理过程就是:
- 调用
InstallParams
类的startCopy()
方法来执行安装- 处理逻辑肯定在这个方法里面,下面细讲
- 只要
mPendingInstalls
中还有安装信息,就会重复发送MCS_BOUND
消息 - 直到所有应用都处理完毕,然后发送一个延时
10s
的MCS_UNBIND
消息-
MCS_UNBIND
消息的处理很简单,收到消息后如果发现mPendingInstalls
中有数据了,则发送MCS_BOUND
消息继续安装。 - 否则断开
DefaultContainerService
连接,安装过程到此结束
case MCS_UNBIND: { if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) { if (mBound) { // 断开 DefaultContainerService 连接 disconnectService(); } } else if (mPendingInstalls.size() > 0) { // 继续发送执行安装操作的消息 mHandler.sendEmptyMessage(MCS_BOUND); } break; }
-
针对startCopy()
方法的处理过程,代码如下:
private static final int MAX_RETRIES = 4;
final boolean startCopy() {
boolean res;
try {
// 重试超过4次,则推出
if (++mRetries > MAX_RETRIES) {
// 发送 MCS_GIVE_UP 消息取消安装
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
// 核心方法,执行 copy
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
// 出现异常,发送 MCS_RECONNECT 消息,进行重连
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
// 开始进入第二阶段
handleReturnCode();
return res;
}
startCopy()
方法通过调用handleStartCopy()
来完成安装过程。考虑到安装过程的不确定性,startCopy()
主要是进行错误处理:
- 在
MCS_RECONNECT
消息处理中,会重新绑定DefaultContainerService
- 如果绑定成功,安装过程就会重新开始,
startCopy()
会被再次调用。 - 重试的次数记录在变量
mRetries
中,超过4次,安装失败
- 如果绑定成功,安装过程就会重新开始,
-
handleStartCopy()
执行成功,startCopy()
会调用handleReturnCode()
来继续处理
handleStartCopy()
方法比较长,简要代码如下:
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
......
if (onInt && onSd) {
// 设置安装路径异常标志
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (onSd && ephemeral) {
// 设置安装路径异常标志
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
// 获取安装包中的 PackageInfoLite 对象
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride);
......
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
// 如果安装空间不够,尝试释放cache空间
......
try {
mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
......
} catch (InstallerException e) {
Slog.w(TAG, "Failed to free cache", e);
}
......
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) {
......
if (!origin.existing && requiredUid != -1 && isVerificationEnabled(verifierUser.getIdentifier(), installFlags, installerUid)) {
// 此部分是在执行应用的校验
// 主要通过向待校验功能的组件发送Intent来完成
......
/*
* We don't want the copy to proceed until verification
* succeeds, so null out this field.
*/
mArgs = null;
} else {
// 无需校验,调用 InstallArgs 的 copyAPK 方法继续处理
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
上面handleStartCopy()
方法的主要逻辑是:
- 首先通过
getMinimalPackageInfo()
方法确认是否还有足够的安装空间,如果安装空间不够,会通过mInstaller.freeCache()
来释放一部分 - 接下来通过
createInstallArgs()
创建InstallArgs
对象,createInstallArgs()
方法中会对InstallParams
的move
成员变量进行判断-
move
不为空使用MoveInstallArgs
-
move
为空使用FileInstallArgs
,我们重点来看这个
-
- 再接下来对
apk
进行校验,这个校验过程是通过发送Intent.ACTION_PACKAGE_NEEDS_VERIFICATION)
广播给系统中所有可以接收该Intent
的应用来完成的 - 无需校验,执行
InstallArgs
的copyApk()
方法
InstallArgs
的copyApk()
方法如下:
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
......
return doCopyApk(imcs, temp);
}
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (origin.staged) {
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
// 在/data/app路径下生成临时文件
final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
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());
}
}
};
int ret = PackageManager.INSTALL_SUCCEEDED;
// 使用服务 DefaultContainerService 的 copyPackage 方法复制文件
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
目录下。我们接下来看看handleReturnCode()
的内容,也就是第二阶段。
安装第二阶段:装载应用
第二阶段的工作主要是
- 把应用的格式转换成
oat
格式 - 为应用创建数据目录
- 把应用的信息装载进
PMS
的数据结构中
handleReturnCode()
方法代码如下:
void handleReturnCode() {
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);// 防止重复调用
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageTracedLI(args, res);
}
args.doPostInstall(res.returnCode, res.uid);
}
......
// 将相关数据记录到 mRunningInstalls 集合中
PostInstallData data = new PostInstallData(args, res);
mRunningInstalls.put(token, data);
...... // 省略备份部分
if (!doRestore) {
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
});
}
processPendingInstall()
方法中post
了一个任务:
- 任务中的核心是通过
installPackageTracedLI()
来装载应用 - 接下来的部分是通过
IBackupManager
服务执行备份相关的操作,这部分就不先深入了,篇幅已经过大。。。 - 备份完成后发送
POST_INSTALL
消息继续处理
我们还是先看下核心方法installPackageTracedLI()
的内容,代码如下:
private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
installPackageLI(args, res);
}
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
......
// Result object to be returned
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
res.installerPackageName = installerPackageName;
......
// 创建 APK 解析对象 PackageParser
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
......
final PackageParser.Package pkg;
try {
// 解析 APK
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
}
......
// 收集应用的签名信息
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
pkg.setSigningDetails(args.signingDetails);
} else {
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
......
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.getRenamedPackageLPr(pkgName);
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
replace = true;
}
......// 省略部分新旧应用的属性比对
}
// 检查已安装的应用集合中是否存在该应用的记录
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;
}
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
// 检查应用中定义的Permission是否被重复定义
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
final PackageParser.Permission perm = pkg.permissions.get(i);
final BasePermission bp =
(BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
......
// 省略部分只要是对系统定义权限和非系统定义权限的区分
// 如果是系统应用定义的权限,则忽略本应用中的定义,然后继续
// 如果是非系统应用定义的权限,则本次安装失败
......
}
}
......
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {
......
replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
installerPackageName, res, args.installReason);
} else {
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
...... // 省略 dexopt 相关操作
}
}
这部分的代码逻辑和前面介绍的scanDirLI()
很像,不详细介绍了,整体流程是:
- 先通过
PackageParser
解析了安装的应用文件- 解析过程和
scanDirLI()
中的parsePackage()
一样
- 解析过程和
- 得到解析结果后,主要是判断新安装的应用是否和已安装的应用构成升级关系
- 如果是,则调用
replacePackageLIF()
继续处理 - 否则调用
installNewPackageLIF()
处理
- 如果是,则调用
这里不深入进行分析了哈,跟踪完调用过程后其实都会走到addForInitLI()
中
再次吐槽一下
PMS
,package
解析部分的调用是真滴复杂啊,各种条件判断
我们继续看下POST_INSTALL
消息的处理过程:
case POST_INSTALL: {
PostInstallData data = mRunningInstalls.get(msg.arg1);
final boolean didRestore = (msg.arg2 != 0);
mRunningInstalls.delete(msg.arg1);
if (data != null) {
InstallArgs args = data.args;
PackageInstalledInfo parentRes = data.res;
......
// Handle the parent package
handlePackagePostInstall(parentRes, grantPermissions, killApp,
virtualPreload, grantedPermissions, didRestore,
args.installerPackageName, args.observer);
// Handle the child packages
final int childCount = (parentRes.addedChildPackages != null)
? parentRes.addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
handlePackagePostInstall(childRes, grantPermissions, killApp,
virtualPreload, grantedPermissions, false /*didRestore*/,
args.installerPackageName, args.observer);
}
......
}
......
} break;
POST_INSTALL
的消息处理基本上都是在执行handlePackagePostInstall()
方法,handlePackagePostInstall()
方法主要工作包括两个:
- 发广播,广播包括:
-
Intent.ACTION_PACKAGE_ADDED
:新应用安装成功 -
Intent.ACTION_PACKAGE_REPLACED
:应用更新成功 - 发广播的目的是通知系统中的其他应用
APK
安装完成。例如,Launcher
中需要增加应用的图标等
-
- 调用
IPackageInstallObserver2
的onPackageInstalled()
方法通知观察者- 我们最初介绍的
PackageInstaller
在安装应用时就是通过注册观察者来实现结果监听的
- 我们最初介绍的
关于
PMS
的软件卸载流程,大家同样可以从PackageInstaller
入手,关键类是UninstallUninstalling
。这里就先跳过了,PMS
篇幅占用时间太久了。。。。。