Android Apk安装深入探索
Apk的三种安装方式
安卓apk的安装大致分为三种方式,一种是调用安装器安装,也就是普通用户见的最多的应用安装,比如应用市场或者网络下载的apk文件,会调用出一个安装界面,这个安装界面就是安装器的界面,这个安装器本质也是一个apk,只是系统预置权限比较高,拥有安装和部分目录的访问权限。一种是adb install安装,这种方式不会调用界面安装流程,会在后台进行安装,会调用pm命令去启动PackageManangerService(后面简称PKMS,贼长就不全打了)的安装服务,进行应用的安装命令使用举例adb install ***.apk注意安装的apk后缀一定要为apk否则无法安装,其实中间还可以添加各种参数,比如-t -s此处不做详细讲解。最后一种是使用adb push方式进行安装,当然这也是adb命令的操作,但是原理和走的流程完全不同,使用adb push命令推的apk只有系统重启后才能生效,而且推送的目录必须为指定目录比如system/app等几个目录,其实可以系统定制。这时有些人可能疑问为什么重启才会生效,这是因为adb push命令原理是向系统推送文件,其他动作没有进行,就相当于复制粘贴,并没有启动安装服务和apk文件扫描,那么为什么重启就会生效,这是因为重启的时候,我们在zygote启动后会去启动PackageManagerService这个服务,这个服务在构造函数中会每次开机都去扫描系统指定的apk安装目录,其实这个过程有点类似于安装,所以有些安卓机预置apk过多以后会导致系统开机异常慢,就其是没有开起odex编译优化的机器。到此大致介绍完毕,接下来比较详细的介绍一下各种安装。本文以安卓7.1代码为例。
一、安装器安装
当我们使用安卓手机时,在网上下了一个apk文件,通常你使用的应用市场会在下载完之后出现一个安装按钮,这时候他是怎么实现的呢,其实他是通过发送一个intent的隐式调用去启动了安装器apk,并且同时将apk源文件的路径传递过去,供安装器使用。示例代码如下:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
Uri uri = FileProvider.getUriForFile(context,"com.readboy.lee.AppUpdate.fileprovider",new File(path));
intent.setDataAndType(uri,"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.setDataAndType(Uri.parse("file://" + path),
"application/vnd.android.package-archive");
}
context.startActivity(intent);
上面是一个普通的调用方式,来源于网络。可以看到是一个很明显的隐式调用,而且添加了FileProvider因为
由于调用系统的安装,需要传递一个Uri对象,导致包含文件URL的intent离开了本应用,所以导致报错,安装APK的代码无法在Android7.0上正常使用,所以要进行修改获取Uri的方式。
接下来这个隐式调用会根据传递的data来选择安装器,那么我们的安装器究竟在什么位置,那就是安卓源码目录的 packages/apps/PackageInstaller这个目录下的代码,这里就是安装器apk的源码,我们先关注一下AndroidManifest.xml其中PackageInstallerActivity的代码如下:
<activity android:name=".PackageInstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
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>
<intent-filter android:priority="1">
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="package" />
<data android:scheme="content" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
这里我们可以看到刚才我们隐式调用对应的type在intent-filter中声明,在PKMS对应用扫描的时候这些信息全被加载到PKMS中,当我们隐式调用的时候会根据这些信息进行相应的调用启动对应的apk,如果同时含有多个相同属性的apk那么会拉起选择弹窗。选择你想用的那个apk。接下来我们去看一下PackageInstallerActivity都做了些什么,那么第一步肯定是去看onCreate了
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
mOriginatingUid = getOriginatingUid(intent);
final Uri packageUri;
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
mSessionId = sessionId;
packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
packageUri = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
// if there's nothing to do, quietly slip into the ether
if (packageUri == null) {
Log.w(TAG, "Unspecified source");
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
finish();
return;
}
if (DeviceUtils.isWear(this)) {
showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
return;
}
//set view
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
mOk = (Button)findViewById(R.id.ok_button);
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
boolean wasSetUp = processPackageUri(packageUri);
if (!wasSetUp) {
return;
}
checkIfAllowedAndInitiateInstall(false);
}
从这里我们可以看到首先获取到系统的PackageManager mPm用来调用实现包的管理,获取安装包的一些信息,比如签名gid等信息,以及创建一个PackageInstaller mInstaller,用来创建session与PKMS进行通讯,实现安装包的拷贝安装等动作,可以把两个一个看成动态一个看成静态,mPm作为静态的数据,mInstaller作为动态的执行,一个厨师一个食材,准备做菜。然后就是处理intent传过来的一些信息了,然后创建我们安装器最熟悉的界面,让用户确定是否安装了,一个mOk键位一个mCanel键位,就是确认和取消按钮的事件监听。然后会去检查是否允许初始化安装checkIfAllowedAndInitiateInstall,当然还有准备安装进度条以及检查权限等的动作,这些不再赘述,我们主要追踪安装过程,我们继续去追点击确认安装之后的事件。
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
clearCachedApkIfNeededAndFinish();
} else {
startInstall();
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
}
可以看到当ok被按下以后检查过准备动作以后就可以调用starInstall去进行安装,那么去看看那startInstall都做了些啥。代码如下:
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
if (mOriginatingUid != VerificationParams.NO_UID) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
installerPackageName);
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
}
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
finish();
}
看到注释说去启动真正的安装activity了,跳转到安装进度界面,叫做InstallAppProgress的一个类他的集成关系如下:
InstallAppProgress extends Activity implements View.OnClickListener, OnCancelListener
这个类的create里面一开始就进行intent的数据接收和解析,为下面的真正安装作准备,而且还创建了一个HandlerThread线程,并注册了一个广播接受器,来监听安装权限,代码如下:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = intent.getData();
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
throw new IllegalArgumentException("unexpected scheme " + scheme);
}
mInstallThread = new HandlerThread("InstallThread");
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(
mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
initView();
}
这里还有一个initView把一些显示初始化动作放到里面,估计怕太长了难看吧。这样又要跳到initView去看看,其实里面就是加载界面,然后初始化一些参数解析出安装包的路径和安装参数,并让mInstallHandler发送一个runnalble去执行doPackageStage(pm, params);这里才是重头戏,前面都是花架子给前台用户看的,渲染界面,到这里才真的要安装了。
initView中相关代码如下:
mInstallHandler.post(new Runnable() {
@Override
public void run() {
doPackageStage(pm, params);
}
});
进入doPackageStage之前需要介绍一下他的两个参数,第一个pm定义为final PackageManager pm = getPackageManager();这个getPackageManager实现是在ContextImpl中实现的代码如下:
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
转到ActivityThread.getPackageManager
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
这里与package服务建立一个链接作为一个binder客户端,可见最终的操作还是要PackageManangerService来完成的。这里最终返回了ApplicationPackageManager,并且传入了PackageManagerService的服务。
上面的关系可以用下图所示:
回到另一个参数params,其定义为
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL)
其主要作用为初始化参数,保存一些类似与apk安装包地址等信息,因为接下来创建的session会使用这些参数.并且这里指定安装参数为全面安装。
接下来看一下doPackageStage相关代码:
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
/*这里的pm刚才的分析代码中得出就是ApplicationPackageManager。
pm.getPackageInstaller最终会返回PackageInstaller的一个实例。
return mInstaller = new PackageInstaller(mContext, this, mPM.getPackageInstaller(),
mContext.getPackageName(), mContext.getUserId());
*/
final PackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.Session session = null;
try {
//接下来的路径信息为intent传入
final String packageLocation = mPackageURI.getPath();
final File file = new File(packageLocation);
final int sessionId = packageInstaller.createSession(params);
final byte[] buffer = new byte[65536];
session = packageInstaller.openSession(sessionId);
final InputStream in = new FileInputStream(file);
final long sizeBytes = file.length();
final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
try {
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
if (sizeBytes > 0) {
final float fraction = ((float) c / (float) sizeBytes);
session.addProgress(fraction);
}
}
session.fsync(out);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
}
// Create a PendingIntent and use it to generate the IntentSender
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallAppProgress.this /*context*/,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE);
} finally {
IoUtils.closeQuietly(session);
}
}
关注一下PackageInstall.createSession,其代码如下:
public int createSession(@NonNull SessionParams params) throws IOException {
try {
return mInstaller.createSession(params, mInstallerPackageName, mUserId);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
这里的mInstaller为ApplicationPackageManager中传入的
mPM.getPackageInstaller()
mPM既是传入的构造函数中的第二个参数,即我们上面关系图中与PackageManagerService通讯的接口。
这里的getPackageInstaller既是PKMS中的那么我们去看一下是如何获得的
@Override
public IPackageInstaller getPackageInstaller() {
return mInstallerService;
}
这里的mInstallerService又是哪里来的,它是在PKMS构造函数中就 创建的如下
mInstallerService = new PackageInstallerService(context, this);
那么回到createSession也就是PKMS里面的mInstallerService的createSession.既是来到
PackageInstallerService中代码如下:
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
转到createSessionInternal
private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
throws IOException {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
throw new SecurityException("User restriction prevents installing");
}
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
mAppOps.checkPackage(callingUid, installerPackageName);
params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
}
// Only system components can circumvent runtime permissions when installing.
if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException("You need the "
+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
Context.ACTIVITY_SERVICE);
final int iconSize = am.getLauncherLargeIconSize();
if ((params.appIcon.getWidth() > iconSize * 2)
|| (params.appIcon.getHeight() > iconSize * 2)) {
params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
true);
}
}
switch (params.mode) {
case SessionParams.MODE_FULL_INSTALL:
case SessionParams.MODE_INHERIT_EXISTING:
break;
default:
throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
// If caller requested explicit location, sanity check it, otherwise
// resolve the best internal or adopted location.
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
throw new IOException("No suitable internal storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
throw new IOException("No suitable external storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
params.setInstallFlagsInternal();
} else {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
params.setInstallFlagsInternal();
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
params.appPackageName, params.installLocation, params.sizeBytes);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
// Sanity check that installer isn't going crazy
final int activeCount = getSessionCount(mSessions, callingUid);
if (activeCount >= MAX_ACTIVE_SESSIONS) {
throw new IllegalStateException(
"Too many active sessions for UID " + callingUid);
}
final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
throw new IllegalStateException(
"Too many historical sessions for UID " + callingUid);
}
sessionId = allocateSessionIdLocked();
}
final long createdMillis = System.currentTimeMillis();
// We're staging to exactly one location
File stageDir = null;
String stageCid = null;
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
final boolean isEphemeral =
(params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
} else {
stageCid = buildExternalStageCid(sessionId);
}
session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
params, createdMillis, stageDir, stageCid, false, false);
synchronized (mSessions) {
mSessions.put(sessionId, session);
}
mCallbacks.notifySessionCreated(session.sessionId, session.userId);
writeSessionsAsync();
return sessionId;
}
这里面首先校验一下参数,判断安装方式以及安装路径等信息,确认完以后创建了我们需要的session
session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
params, createdMillis, stageDir, stageCid, false, false);
创建完成以后回到InstalleAppProgress发现紧接着就打开了这个session
session = packageInstaller.openSession(sessionId);
open的过程中调用关系如下PackageManangerService.openSession -> PackageInstallerSession.open -> PackageInstallerService.InternalCallback.onSessionPrepared -> PackageInstallerService.writeSessionAsync -> PackageInstallerService.writeSessionsLocked
这一系列的过程实际上是在data/app/目录下创建一个临时文件,格式为vmdl****.tmp此时文件为空,并且修改/data/system/install_sessions.xml文件里的内容,添加我们需要安装的APP的一些信息比如包名权限等,并且处理persist app的图标。在安装的过程中同时会判断安装参数中的installFlags如果为INSTALL_INTERNAL才会是上述目录(stageDir)创建临时文件,否则则使用smdl***.tmp的临时文件,也就是代码中的stageCid。
继续回到doPackageStage发现开始执行写的动作了,将我们刚才准备的路径内写入了需要安装apk路径里的内容,既将我们的apk源文件通过socket拷贝到刚才open时准备的临时文件中。写操作使用的socket形式进行写入,代码如下:
try {
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
if (sizeBytes > 0) {
final float fraction = ((float) c / (float) sizeBytes);
session.addProgress(fraction);
}
}
session.fsync(out);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
}
拷贝完成后(重命名为临时文件)继续传递了一组intent(作为广播使用)给了session如下:
// Create a PendingIntent and use it to generate the IntentSender
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallAppProgress.this /*context*/,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
那么接下来我们看看seesion的commit是如何处理这个传递的intent,来到PackageInstallerSession的commit函数代码如下:
@Override
public void commit(IntentSender statusReceiver) {
Preconditions.checkNotNull(statusReceiver);
final boolean wasSealed;
synchronized (mLock) {
wasSealed = mSealed;
if (!mSealed) {
// Verify that all writers are hands-off
for (FileBridge bridge : mBridges) {
if (!bridge.isClosed()) {
throw new SecurityException("Files still open");
}
}
mSealed = true;
}
// Client staging is fully done at this point
mClientProgress = 1f;
computeProgressLocked(true);
}
if (!wasSealed) {
// Persist the fact that we've sealed ourselves to prevent
// mutations of any hard links we create. We do this without holding
// the session lock, since otherwise it's a lock inversion.
mCallback.onSessionSealedBlocking(this);
}
// This ongoing commit should keep session active, even though client
// will probably close their end.
mActiveCount.incrementAndGet();
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
检查一下进度然后发送handler消息MSG_COMMIT交给handler处理这个handler是这样定义的:
mHandler = new Handler(looper, mHandlerCallback);
使用了mHandlerCallback,那么这个mHandlerCallback又是怎么定义的呢,如下:
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// Cache package manager data without the lock held
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
final ApplicationInfo appInfo = mPm.getApplicationInfo(
params.appPackageName, 0, userId);
synchronized (mLock) {
if (msg.obj != null) {
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
try {
commitLocked(pkgInfo, appInfo);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
destroyInternal();
dispatchSessionFinished(e.error, completeMsg, null);
}
return true;
}
}
};
这里定义了pkInfo和appInfo最终又交给了commitLocked函数来处理,这个函数代码比较长,他主要的功能就是检查一下路径,并对apk做个简单的检验保证其完整性(sane),然后检查权限,提取本地库文件,创建了一个本地观察着localObserver,确保当apk安装完成后销毁掉session.做完这一切以后开始通过mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);转移工作到PKMS。
进入到PKMS的安装舞台,开始PKMS真正的表演。代码如下
void installStage(String packageName, File stagedDir, String stagedCid,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
Certificate[][] certificates) {
if (DEBUG_EPHEMERAL) {
if ((sessionParams.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {
Slog.d(TAG, "Ephemeral install of " + packageName);
}
}
final VerificationInfo verificationInfo = new VerificationInfo(
sessionParams.originatingUri, sessionParams.referrerUri,
sessionParams.originatingUid, installerUid);
final OriginInfo origin;
if (stagedDir != null) {
origin = OriginInfo.fromStagedFile(stagedDir);
} else {
origin = OriginInfo.fromStagedContainer(stagedCid);
}
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, certificates);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
System.identityHashCode(msg.obj));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(msg.obj));
mHandler.sendMessage(msg);
}
到这里发现创建了InstallParams然后将其交于mHandler进行处理。这里就跳转到PackageHandler里面的INIT_COPY消息处理中了。
INIT_COPY中确认了mPendingInstalls中的安装任务数量,并且检查是否绑定服务IMediaContainerService否则返回错误。然后发送MCS_BOUND消息,跳转到MCS_BOUND中。MCS_BOUND中会首先获取到mContainerService,然后获取到params,这个就是前面传过来的InstallParams。然后开始最主要的工作params.startCopy(),这个函数包含主要的安装信息,这也是在InstallParams这个类中实现的,这个类继承自HandlerParams与MoveParams,MeasureParams并行,分别负责安装转移和测量的动作,关系类图如下:
然后进入到InstallParams的startCopy这个函数是在父类中实现的代码如下:
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(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);
res = false;
}
handleReturnCode();
return res;
}
父类函数又调用了抽象函数handleRetrunCode,由子类来实现此函数,不同的子类可以定制自己的copy操作,此处我们需要关注的是InstallParams的实现,代码如下:
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; final boolean ephemeral = (installFlags & PackageManager.INSTALL_EPHEMERAL) != 0; PackageInfoLite pkgLite = null; if (onInt && onSd) { // 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 if (onSd && ephemeral) { Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); if (DEBUG_EPHEMERAL && ephemeral) { Slog.v(TAG, "pkgLite for install: " + pkgLite); } /* * If we have too little free space, try to free cache * before giving up. */ 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); final long lowThreshold = storage.getStorageLowBytes( Environment.getDataDirectory()); final long sizeBytes = mContainerService.calculateInstalledSize( origin.resolvedPath, isForwardLocked(), packageAbiOverride); try { mInstaller.freeCache(null, sizeBytes + lowThreshold); pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); } catch (InstallerException e) { Slog.w(TAG, "Failed to free cache", e); } /* * 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; } } } if (ret == PackageManager.INSTALL_SUCCEEDED) { int loc = pkgLite.recommendedInstallLocation; if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) { ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) { ret = PackageManager.INSTALL_FAILED_INVALID_APK; } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { ret = PackageManager.INSTALL_FAILED_INVALID_URI; } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) { ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; } else { // Override with defaults if needed. loc = installLocationPolicy(pkgLite); if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) { ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; } else if (!onSd && !onInt) { // Override install location with flags if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { // Set the flag to install on external media. installFlags |= PackageManager.INSTALL_EXTERNAL; installFlags &= ~PackageManager.INSTALL_INTERNAL; } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) { if (DEBUG_EPHEMERAL) { Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag"); } installFlags |= PackageManager.INSTALL_EPHEMERAL; installFlags &= ~(PackageManager.INSTALL_EXTERNAL |PackageManager.INSTALL_INTERNAL); } else { // Make sure the flag for installing on external // media is unset installFlags |= PackageManager.INSTALL_INTERNAL; installFlags &= ~PackageManager.INSTALL_EXTERNAL; } } } } final InstallArgs args = createInstallArgs(this); mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { // TODO: http://b/22976637 // Apps installed for "all" users use the device owner to verify the app UserHandle verifierUser = getUser(); if (verifierUser == UserHandle.ALL) { verifierUser = UserHandle.SYSTEM; } /* * 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, MATCH_DEBUG_TRIAGED_MISSING, verifierUser.getIdentifier()); if (!origin.existing && requiredUid != -1 && isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // Query all live verifiers based on current user state final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification, PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier()); if (DEBUG_VERIFY) { Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent " + verification.toString() + " with " + pkgLite.verifiers.length + " optional verifiers"); } final int verificationId = mPendingVerificationToken++; verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId); verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE, installerPackageName); verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, installFlags); verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, pkgLite.packageName); verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE, pkgLite.versionCode); if (verificationInfo != null) { if (verificationInfo.originatingUri != null) { verification.putExtra(Intent.EXTRA_ORIGINATING_URI, verificationInfo.originatingUri); } if (verificationInfo.referrer != null) { verification.putExtra(Intent.EXTRA_REFERRER, verificationInfo.referrer); } if (verificationInfo.originatingUid >= 0) { verification.putExtra(Intent.EXTRA_ORIGINATING_UID, verificationInfo.originatingUid); } if (verificationInfo.installerUid >= 0) { verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, verificationInfo.installerUid); } } final PackageVerificationState verificationState = new PackageVerificationState( requiredUid, args); mPendingVerification.append(verificationId, verificationState); final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite, receivers, verificationState); /* * If any sufficient verifiers were listed in the package * manifest, attempt to ask them. */ if (sufficientVerifiers != null) { final int N = sufficientVerifiers.size(); if (N == 0) { Slog.i(TAG, "Additional verifiers required, but none installed."); ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; } else { for (int i = 0; i < N; i++) { final ComponentName verifierComponent = sufficientVerifiers.get(i); final Intent sufficientIntent = new Intent(verification); sufficientIntent.setComponent(verifierComponent); mContext.sendBroadcastAsUser(sufficientIntent, verifierUser); } } } final ComponentName requiredVerifierComponent = matchComponentForVerifier( mRequiredVerifierPackage, receivers); if (ret == PackageManager.INSTALL_SUCCEEDED && mRequiredVerifierPackage != null) { Trace.asyncTraceBegin( TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); /* * Send the intent to the required verification agent, * but only start the verification timeout after the * target BroadcastReceivers have run. */ verification.setComponent(requiredVerifierComponent); mContext.sendOrderedBroadcastAsUser(verification, verifierUser, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final Message msg = mHandler .obtainMessage(CHECK_PENDING_VERIFICATION); msg.arg1 = verificationId; mHandler.sendMessageDelayed(msg, getVerificationTimeout()); } }, null, 0, null, null); /* * We don't want the copy to proceed until verification * succeeds, so null out this field. */ mArgs = null; } } else { /* * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. */ ret = args.copyApk(mContainerService, true); } } mRet = ret; }