Android8.1 APK安装过程源码解析

Android系统 同时被 2 个专栏收录
6 篇文章 1 订阅
3 篇文章 0 订阅

最近在学习Android8.0的Launcher源码遇到APP安装过程中监听APK安装过程,顺便就看了一遍APK源码安装的过程。相比较之前的版本APK安装源码有一点出处,由于才疏学浅,什么地方讲不到请多多谅解。文章末尾附上函数调用时序图

一、启动安装

1、InstallStart

在开发过程中有时候需要进行APP更新,或者下载其他APP到本地让后使用以下代码进行安装,我这里单单讲解在执行startActivity之后的运行过程。

Intent intent = new Intent(Intent.ACTION_VIEW);
//设置intent的数据类型是应用程序application  
intent.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
//为这个新apk开启一个新的activity栈  
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//开始安装  
startActivity(intent);

在执行完这几行代码后会启动系统的一个APP进行APK的安装,我们可以通过源码查看在系统中有一个packageinstaller的系统应用。然后首先查看看清单文件

<activity android:name=".InstallStart"
   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>
           <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>

从中可以发现首先启动的是名为InstallStart的Activity,由于整个源码有点多,因此这里只是贴出部分重要方法。在这里只需要看onCreate的方法


import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;

public class InstallStart extends Activity {
    private static final String LOG_TAG = InstallStart.class.getSimpleName();

    private static final String SCHEME_CONTENT = "content";
    private static final String DOWNLOADS_AUTHORITY = "downloads";
    private IActivityManager mIActivityManager;
    private IPackageManager mIPackageManager;
    private boolean mAbortInstall = false;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mIPackageManager = AppGlobals.getPackageManager();
        Intent intent = getIntent();
        String callingPackage = getCallingPackage();

        // If the activity was started via a PackageInstaller session, we retrieve the calling
        // package from that session
        int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
        if (callingPackage == null && sessionId != -1) {      //我们第三发安装sessionId为-1
            PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
            PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
            callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
        }

        final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
        final int originatingUid = getOriginatingUid(sourceInfo);
        boolean isTrustedSource = false;
        if (sourceInfo != null
                && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
            isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
        }

        if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
            final int targetSdkVersion = getMaxTargetSdkVersionForUid(originatingUid);
            if (targetSdkVersion < 0) {
                Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
                // Invalid originating uid supplied. Abort install.
                mAbortInstall = true;
            } else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission(
                    originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
                //在现版本第三方安装APK需要申明REQUEST_INSTALL_PACKAGES权限,declaresAppOpPermission检测
                //是否申请改权限
                Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
                        + Manifest.permission.REQUEST_INSTALL_PACKAGES);
                mAbortInstall = true;
            }
        }
        if (mAbortInstall) { //如果经过上述一系列操作没问题,当然是false
            setResult(RESULT_CANCELED);
            finish();
            return;
        }

        Intent nextActivity = new Intent(intent);
        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);

        // The the installation source as the nextActivity thinks this activity is the source, hence
        // set the originating UID and sourceInfo explicitly
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);

        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
            //在这里我们第三方app是不会跳转到这里的
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
            Uri packageUri = intent.getData();

            if (packageUri == null) {
                // if there's nothing to do, quietly slip into the ether
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            } else {
                if (packageUri.getScheme().equals(SCHEME_CONTENT)) {
                    nextActivity.setClass(this, InstallStaging.class);
                } else {
                    nextActivity.setClass(this, PackageInstallerActivity.class);
                }
            }
        }

        if (nextActivity != null) {
            //跳转到下一个Activity
            startActivity(nextActivity);
        }
        finish();
    }

   ......
}

经过一系列的解析操作之后会跳转到新的界面

2、PackageInstallerActivity

public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener {
    private static final String TAG = "PackageInstaller";
    .....
    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        if (icicle != null) {
            mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
        }

        mPm = getPackageManager(); //获取PackageManager
        mIpm = AppGlobals.getPackageManager();
        mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
        mInstaller = mPm.getPackageInstaller();//获取PackageInstaller
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);

        final Intent intent = getIntent();

        mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
        mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
        mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                PackageInstaller.SessionParams.UID_UNKNOWN);
        mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
                ? getPackageNameForUid(mOriginatingUid) : null;


        final Uri packageUri;

        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {//非Android的GooglePaly上的app可以直接忽略了
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId); //在PackageInstaller中创建一个Session
            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(); //获取安装APK安装包的路径
            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;
        }

        boolean wasSetUp = processPackageUri(packageUri);
        if (!wasSetUp) {
            return;
        }

        // load dummy layout with OK button disabled until we override this layout in
        // startInstallConfirm
        bindUi(R.layout.install_confirm, false); //安装界面绑定
        checkIfAllowedAndInitiateInstall(); //检测此APK是否允许,并且初始化安装前操作界面
    }
    
    .....
    
    
    public void onClick(View v) {
        if (v == mOk) {//点击确定安装按钮
            if (mOk.isEnabled()) {
                if (mOkCanInstall || mScrollView == null) {
                    if (mSessionId != -1) {
                        mInstaller.setPermissionsResult(mSessionId, true);
                        finish();
                    } else {
                        startInstall();
                    }
                } else {
                    mScrollView.pageScroll(View.FOCUS_DOWN);
                }
            }
        } else if (v == mCancel) {
            // Cancel and finish
            setResult(RESULT_CANCELED);
            if (mSessionId != -1) {
                mInstaller.setPermissionsResult(mSessionId, false);
            }
            finish();
        }
    }

    .....
    
    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, InstallInstalling.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 != PackageInstaller.SessionParams.UID_UNKNOWN) {
            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();
    }
.....
 
}

在上述界面中将APK解析,并分析其中权限信息等内容。此时,点击安装就会进入到下一个界面

3、InstallInstalling

public class InstallInstalling extends Activity {
    private static final String LOG_TAG = InstallInstalling.class.getSimpleName();

    private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID";
    private static final String INSTALL_ID = "com.android.packageinstaller.INSTALL_ID";


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.install_installing);

        ApplicationInfo appInfo = getIntent()
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        mPackageURI = getIntent().getData();

        if ("package".equals(mPackageURI.getScheme())) {//查看是否是package路径Uri,发现明显不是
            try {
                getPackageManager().installExistingPackage(appInfo.packageName);
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else {
            final File sourceFile = new File(mPackageURI.getPath());
            PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
                    sourceFile), R.id.app_snippet);

            if (savedInstanceState != null) { //为null
                mSessionId = savedInstanceState.getInt(SESSION_ID);
                mInstallId = savedInstanceState.getInt(INSTALL_ID);

                // Reregister for result; might instantly call back if result was delivered while
                // activity was destroyed
                try {
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else {
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
                params.originatingUri = getIntent()
                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
                params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                        UID_UNKNOWN);

                File file = new File(mPackageURI.getPath());
                try {
                    PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
                    params.setAppPackageName(pkg.packageName);
                    params.setInstallLocation(pkg.installLocation);
                    params.setSize(
                            PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
                } catch (PackageParser.PackageParserException e) {
                    Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                } catch (IOException e) {
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                }

                try {
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

                try {//在PackageInstaller创建用于通信的session,关于创建Seeion过程这里不再做叙述
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
            }

            mCancelButton = (Button) findViewById(R.id.cancel_button);

            mCancelButton.setOnClickListener(view -> {
                if (mInstallingTask != null) {
                    mInstallingTask.cancel(true);
                }

                if (mSessionId > 0) {
                    getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                    mSessionId = 0;
                }

                setResult(RESULT_CANCELED);
                finish();
            });

            mSessionCallback = new InstallSessionCallback();
        }
    }

    @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
            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);//根据在onCreate中创建的SeesionID获取相应session信息

            if (sessionInfo != null && !sessionInfo.isActive()) {
                mInstallingTask = new InstallingAsyncTask(); //创建异步任务
                mInstallingTask.execute(); //执行异步任务
            } else {
                // we will receive a broadcast when the install is finished
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            }
        }
    }

    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
                PackageInstaller.Session> {
        volatile boolean isDone;

        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {//根据SessionId打开PackageInstaller端Session
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
                return null;
            }

            session.setStagingProgress(0);

            try {
                File file = new File(mPackageURI.getPath());//获取APK安装包

                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) {//读取APK,并通过Session传递APK安装包
                            int numRead = in.read(buffer);

                            if (numRead == -1) {
                                session.fsync(out);
                                break;
                            }

                            if (isCancelled()) {
                                session.close();
                                break;
                            }

                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                float fraction = ((float) numRead / (float) sizeBytes);
                                session.addProgress(fraction);
                            }
                        }
                    }
                }

                return session;
            } catch (IOException | SecurityException e) {
                Log.e(LOG_TAG, "Could not write package", e);

                session.close();

                return null;
            } finally {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
            }
        }

        @Override
        protected void onPostExecute(PackageInstaller.Session session) {
            if (session != null) {
                Intent broadcastIntent = new Intent(BROADCAST_ACTION);
                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                broadcastIntent.setPackage(
                        getPackageManager().getPermissionControllerPackageName());
                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

                PendingIntent pendingIntent = PendingIntent.getBroadcast(
                        InstallInstalling.this,
                        mInstallId,
                        broadcastIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT);

                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);

                if (!isCancelled()) {
                    launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
                }
            }
        }
    }
}

在上述操作中,主要是完成以下步骤

  • 获取PackageManager,然后获取其中的PackageInstaller对象
  • 在PackageInstaller中创建用于传输APK安装包以及安装信息的Session,并返回SessionId
  • 创建异步任务,并在异步任务中根据之前返回的SessionId打开PackageInstaller端Session
  • 通过Session将APK安装包以及相关信息传递
  • 在异步任务中onPostExecute方法中执行session.commit(pendingIntent.getIntentSender())

二、PackageInstaller解析

在这一步操作中用到PackageInstaller,下面看一下这部分代码。由于代码太多,这里只做部分方法讲解。

1、构造函数
public PackageInstaller(IPackageInstaller installer,
                        String installerPackageName, int userId) {
    mInstaller = installer;
    mInstallerPackageName = installerPackageName;
    mUserId = userId;
}

此方法中主要是成员变量的赋值,在这里需要注意的主要是mInstaller成员变量。这里是远程PackageInstallerServices对象,在后面多处用到。

2、创建Session
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();
    }
}

看到此方法,发现也是通过远程对象创建的

3、打开Session
public @NonNull Session openSession(int sessionId) throws IOException {
    try {
        return new Session(mInstaller.openSession(sessionId));
    } catch (RuntimeException e) {
        ExceptionUtils.maybeUnwrapIOException(e);
        throw e;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

此方法中根据远程对象打开Session的返回值创建本地Session。

由上面最后安装界面可知,在最后创建异步任务中打开Session,打开之后就是调用openWrite方法开始进行APK安装包传输

4、打开Session后打开写
public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
                                       long lengthBytes) throws IOException {
    try {
        if (ENABLE_REVOCABLE_FD) {
            return new ParcelFileDescriptor.AutoCloseOutputStream(
                    mSession.openWrite(name, offsetBytes, lengthBytes));
        } else {
            final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
                    offsetBytes, lengthBytes);
            return new FileBridge.FileBridgeOutputStream(clientSocket);
        }
    } catch (RuntimeException e) {
        ExceptionUtils.maybeUnwrapIOException(e);
        throw e;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

由此方法可以看出最后还是调用远程Session的openWrite方法。

在安装最后一个界面的最后的异步任务中执行了session.fsync(out)方法,下面看看这个方法

public void fsync(@NonNull OutputStream out) throws IOException {
    if (ENABLE_REVOCABLE_FD) {
        if (out instanceof ParcelFileDescriptor.AutoCloseOutputStream) {
            try {
                Os.fsync(((ParcelFileDescriptor.AutoCloseOutputStream) out).getFD());
            } catch (ErrnoException e) {
                throw e.rethrowAsIOException();
            }
        } else {
            throw new IllegalArgumentException("Unrecognized stream");
        }
    } else {
        if (out instanceof FileBridge.FileBridgeOutputStream) {
            ((FileBridge.FileBridgeOutputStream) out).fsync();
        } else {
            throw new IllegalArgumentException("Unrecognized stream");
        }
    }
}
这个方法就是将读取到内存中的文件保存到本地。
5、最后页面的commit
public void commit(@NonNull IntentSender statusReceiver) {
    try {
        mSession.commit(statusReceiver, false);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

最后调用的还是远程的commit方法,下面看一看远程的PackageInstallerSession

三、PackageInstallerSession方法解析

1、PackageInstallerSession的commit方法
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    Preconditions.checkNotNull(statusReceiver);
    final boolean wasSealed;
    synchronized (mLock) {
        assertCallerIsOwnerOrRootLocked();
        assertPreparedAndNotDestroyedLocked("commit");
        final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
                mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);
        mRemoteObserver = adapter.getBinder();
        if (forTransfer) {
            mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
            if (mInstallerUid == mOriginalInstallerUid) {
                throw new IllegalArgumentException("Session has not been transferred");
            }
        } else {
            if (mInstallerUid != mOriginalInstallerUid) {
                throw new IllegalArgumentException("Session has been transferred");
            }
        }
        wasSealed = mSealed;
        if (!mSealed) {
            try {
                sealAndValidateLocked();
            } catch (IOException e) {
                throw new IllegalArgumentException(e);
            } catch (PackageManagerException e) {
                destroyInternal();
                // Cannot call dispatchFinal synchronous as this might be called from inside the
                // system server on the main thread. Hence the call back scheduled in
                // dispachFinal has to be scheduled on a different thread.
                mHandler.obtainMessage(MSG_SESSION_FINISHED_WITH_EXCEPTION, e).sendToTarget();
                return;
            }
        }
        // Client staging is fully done at this point
        mClientProgress = 1f;
        computeProgressLocked(true);
        // This ongoing commit should keep session active, even though client
        // will probably close their end.
        mActiveCount.incrementAndGet();
        mCommitted = true;
        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    }
    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);
    }
}

由此方法可以,最后发送了一个MSG_COMMIT方法。下面看一下定义的Handler。

2、PackageInstallerSession的Handler
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_COMMIT:
                synchronized (mLock) {
                    try {
                        commitLocked();
                    } catch (PackageManagerException e) {
                        final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                        Slog.e(TAG,
                                "Commit of session " + sessionId + " failed: " + completeMsg);
                        destroyInternal();
                        dispatchSessionFinished(e.error, completeMsg, null);
                    }
                }
                break;
            case MSG_SESSION_FINISHED_WITH_EXCEPTION:
                PackageManagerException e = (PackageManagerException) msg.obj;
                dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e),
                        null);
                break;
        }
        return true;
    }
};

此方法也是相当简单,只需要看commitLocker()方法。

3、PackageInstallerSession的commitLocker方法
private void commitLocked()
        throws PackageManagerException {
    if (mDestroyed) {
        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
    }
    if (!mSealed) {
        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
    }
    Preconditions.checkNotNull(mPackageName);
    Preconditions.checkNotNull(mSignatures);
    Preconditions.checkNotNull(mResolvedBaseFile);
   .....
    mRelinquished = true;
    mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
            mInstallerPackageName, mInstallerUid, user, mCertificates);
}
至此,PackageInstallerSession的工作就完成了。最后,将接力棒给了PackageManagerServices去执行。


四、PackageManagerService解析

1、APK拷贝初始化

1.1、installStage
void installStage(String packageName, File stagedDir, String stagedCid,
                  IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
                  String installerPackageName, int installerUid, UserHandle user,
                  Certificate[][] certificates) {
        .....
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    final int installReason = fixUpInstallReason(installerPackageName, installerUid,
            sessionParams.installReason);
    final InstallParams params = new InstallParams(origin, null, observer,
            sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
            verificationInfo, user, sessionParams.abiOverride,
            sessionParams.grantedRuntimePermissions, certificates, installReason);
    params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
    msg.obj = params;
        .....
    mHandler.sendMessage(msg);
}

从此方法中可以看出发送了一个INIT_COPY消息

1.2、INIT_COPY
void doHandleMessage(Message msg) {
    switch (msg.what) {
        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) {
                Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                        System.identityHashCode(mHandler));
                // If this is the only one pending we might
                // have to bind to the service again.
                if (!connectToService()) {
                    Slog.e(TAG, "Failed to bind to media container service");
                    params.serviceError();
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                            System.identityHashCode(mHandler));
                    if (params.traceMethod != null) {
                        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
                                params.traceCookie);
                    }
                    return;
                } else {
                    // Once we bind to the service, the first
                    // 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) {
                    mHandler.sendEmptyMessage(MCS_BOUND);
                }
            }
            break;
        }
        '' '' '
    }'
再次方法中可以看出首先需要执行连接服务操作,让后将参数添加到mPendingInstalls变量中。首先我们可以看连接服务的方法
private boolean connectToService() {
    if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
            " DefaultContainerService");
    Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
    Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
    if (mContext.bindServiceAsUser(service, mDefContainerConn,
            Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        mBound = true;
        return true;
    }
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    return false;
}

使用mDefaultContainerConn连接DefaultContainerServer服务,然后看一下这个变量定义处

final private DefaultContainerConnection mDefContainerConn =
        new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
    public void onServiceConnected(ComponentName name, IBinder service) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
        final IMediaContainerService imcs = IMediaContainerService.Stub
                .asInterface(Binder.allowBlocking(service));
        mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
    }
    public void onServiceDisconnected(ComponentName name) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
    }
}

可以看出连接成功之后会发送一个MCS_BOUND消息,后边看这个消息的处理过程

1.3、MCS_BOUND
void doHandleMessage(Message msg) {
    switch (msg.what) {
        .....
        case MCS_BOUND: {
            if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
            if (msg.obj != null) {
                mContainerService = (IMediaContainerService) msg.obj;
                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                        System.identityHashCode(mHandler));
            }
            if (mContainerService == null) {
                if (!mBound) {
                    // Something seriously wrong since we are not bound and we are not
                    // waiting for connection. Bail out.
                    Slog.e(TAG, "Cannot bind to media container service");
                    for (HandlerParams params : mPendingInstalls) {
                        // Indicate service bind error
                        params.serviceError();
                        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                System.identityHashCode(params));
                        if (params.traceMethod != null) {
                            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
                                    params.traceMethod, params.traceCookie);
                        }
                        return;
                    }
                    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) {
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                            System.identityHashCode(params));
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                    if (params.startCopy()) {  //进行APK文件的拷贝
                        // 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) {
                                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.
                            if (DEBUG_SD_INSTALL) Log.i(TAG,
                                    "Posting MCS_BOUND for next work");
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
            } else {
                // Should never happen ideally.
                Slog.w(TAG, "Empty queue");
            }
            break;
        }
        .....
    }

在这里面params.startCopy()这行代码就是要开始APK安装包的拷贝过程,可以查看InstallParams类中的startcopy方法。可以看到InstallParams继承HandlerParams,startCopy()方法的实现在HandlerParams类中。接下来看看HandlerParams中startCopy()方法

final boolean startCopy() {
    boolean res;
        .....
    handleStartCopy();//进行拷贝文件
        .....
    handleReturnCode();//处理拷贝操作返回的操作
    return res;
}

这个方法中主要有两个方法,一个是进行APK拷贝文件的操作,一个是处理拷贝文件后的操作。首先看拷贝文件的操作

2、apk拷贝操作

2.1.1、handleStartCopy

从源码中可以看出在HandleParams类中并没有实现,是一个抽象方法。再进入PackageManageService开始创建的是InstallParams对象,可以看InstallParams中handleStartCopy()方法实现

public void handleStartCopy() throws RemoteException {
    int ret = PackageManager.INSTALL_SUCCEEDED;
    ......
    if (ret == PackageManager.INSTALL_SUCCEEDED) {
     .....
    }
    final InstallArgs args = createInstallArgs(this); //创建用于实际拷贝APK安装包的对象
    mArgs = args;
    if (ret == PackageManager.INSTALL_SUCCEEDED) {
        ......
        if (!origin.existing && requiredUid != -1
                && isVerificationEnabled(
                verifierUser.getIdentifier(), installFlags, installerUid)) { //判断是否有权限
          ......
            }
        } else {
            ret = args.copyApk(mContainerService, true); //拷贝APK安装包
        }
    }
    mRet = ret;
}
从此方法可以看出APK的拷贝工作又交给另外的对象进行处理,看一下InstallArgs的创建时根据变量来创建不同的对象。这里不用看其他的,直接看FileInstallArgs中的copyApk()方法。
2.1.2、copyApk

int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
    try {
        return doCopyApk(imcs, temp);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
    if (origin.staged) { //在创建的时候设置为ture,因为在一开始已经通过Session拷贝APK
        if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
        codeFile = origin.file;
        resourceFile = origin.file;
        return PackageManager.INSTALL_SUCCEEDED;
    }
    .....
}
在copyApk()中又调用doCopyApk()方法进行拷贝工作。在新版本中主要是通过Session拷贝APK,因此在doCopyApk()方法中首先进行判断,而此状态变量在一开始创建的时候已经初始化为true。我感觉有必要看一下次变量初始化的地方

static OriginInfo fromStagedFile(File file) {
    return new OriginInfo(file, null, true, false);
}
static OriginInfo fromStagedContainer(String cid) {
    return new OriginInfo(null, cid, true, false);
}
private OriginInfo(File file, String cid, boolean staged, boolean existing) {
    this.file = file;
    this.cid = cid;
    this.staged = staged;
    this.existing = existing;
    ''''''
}

由此可见,拷贝APK的工作在doCopyApk()开始之初已经返回。接下来看拷贝APK操作之后操作。

2.2.1、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);
    }
}

擦,直接调用了另外一个方法

2.2.2processPendingInstall

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.setReturnCode(currentStatus);
            res.uid = -1;
            res.pkg = null;
            res.removedInfo = null;
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                args.doPreInstall(res.returnCode);//1、apk安装前操作,删除安装残留文件
                synchronized (mInstallLock) {
                    installPackageTracedLI(args, res); //2、apk安装
                }
                args.doPostInstall(res.returnCode, res.uid);//3、
            }
            // 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 != null
                    && res.removedInfo.removedPackage != null;
            final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
            boolean doRestore = !update
                    && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
                ......
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {//从前面可知update和doRestore都为false
               ......
            }
            if (!doRestore) {
                ......
                Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);//发送消息
                mHandler.sendMessage(msg);
            }
        }
    });
}

此方法中又四步操作

  • doPreInstall方法进行安装前操作,删除安装残留文件
  • installPackageTracedLI方法将apk进行解析,然后进行dex到opt文件操作
  • doPostInstall方法也是将一些残余文件的删除
  • 最后发送POST_INSTALL消息

从上述操作还是到handler中,查看doHandlerMessage消息

void doHandleMessage(Message msg) {
    switch (msg.what) {
            .....
        case POST_INSTALL: {
            if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
            PostInstallData data = mRunningInstalls.get(msg.arg1);
            final boolean didRestore = (msg.arg2 != 0);
            mRunningInstalls.delete(msg.arg1);
            if (data != null) {
               ......
                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;
            ......
    }

在此处可以看到执行handlePackagePosetInstall()方法

2.2.3、handlePackagePosetInstall

private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
                                      boolean killApp, boolean virtualPreload, String[] grantedPermissions,
                                      boolean launchedForRestore, String installerPackage,
                                      IPackageInstallObserver2 installObserver) {
    if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
        // Send the removed broadcasts
        if (res.removedInfo != null) {
            res.removedInfo.sendPackageRemovedBroadcasts(killApp);
        }
        // Now that we successfully installed the package, grant runtime
        // permissions if requested before broadcasting the install. Also
        // for legacy apps in permission review mode we clear the permission
        // review flag which is used to emulate runtime permissions for
        // legacy apps.
        if (grantPermissions) {//此处表示已经安装成功,现在在发送广播之前赋予运行权限
            grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
        }
            ......
        // Send installed broadcasts if the package is not a static shared lib.
        if (res.pkg.staticSharedLibName == null) {
            mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);
            // Send added for users that see the package for the first time
            // sendPackageAddedForNewUsers also deals with system apps
            int appId = UserHandle.getAppId(res.uid);
            boolean isSystem = res.pkg.applicationInfo.isSystemApp();
            sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
                    virtualPreload /*startReceiver*/, appId, firstUsers); //发送APK安装成功的消息
            // Send added for users that don't see the package for the first time
            Bundle extras = new Bundle(1);
            extras.putInt(Intent.EXTRA_UID, res.uid);
            if (update) {
                extras.putBoolean(Intent.EXTRA_REPLACING, true);
            }
            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                    extras, 0 /*flags*/,
                    null /*targetPackage*/, null /*finishedReceiver*/, updateUsers);//发送APK安装成功的消息
          ......
        for (int userId : firstUsers) {
            PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
            // There's a race currently where some install events may interleave with an uninstall.
            // This can lead to package info being null (b/36642664).
            if (info != null) {
                mDexManager.notifyPackageInstalled(info, userId);
            }
        }
    }
    // If someone is watching installs - notify them
    if (installObserver != null) {
        try {
            Bundle extras = extrasForInstallResult(res);
            installObserver.onPackageInstalled(res.name, res.returnCode,
                    res.returnMsg, extras);
        } catch (RemoteException e) {
            Slog.i(TAG, "Observer no longer exists.");
        }
    }
}

此函数主要是受运行权限,并且发送已经安装成功广播。至此,此安装过程完成。附上函数调用时序图


  • 2
    点赞
  • 3
    评论
  • 3
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 3 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页

打赏作者

唯依心动

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值