Android Apk安装深入探索

                                     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;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值