Jimmy Chen

A Programmer

(原创)基于Android 8.1之PackageInstaller源码分析(二)

  上一篇讲到如果允许安装未知源程序,并且应用属于未知源程序,就会调用initiateInstall进行安装,而initiateInstall最后会调用startInstallConfirm进行安装确认。这一篇就从startInstallConfirm方法开始讲解。

PackageInstallerActivity.java

private void startInstallConfirm() {
    // We might need to show permissions, load layout with permissions
    if (mAppInfo != null) {
        // 如果程序已经安装过了,那么就显示install_confirm_perm_update界面
        bindUi(R.layout.install_confirm_perm_update, true);
    } else {
        // 如果程序没有安装,则显示install_confirm_perm界面
        bindUi(R.layout.install_confirm_perm, true);
    }

    ((TextView) findViewById(R.id.install_confirm_question))
            .setText(R.string.install_confirm_question);
    TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
    tabHost.setup();
    ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
    TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
    // Android M开始支持runtime permission,如果是runtime permission的话在这个界面不会显示
    boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
            >= Build.VERSION_CODES.M;
    boolean permVisible = false;
    mScrollView = null;
    mOkCanInstall = false;
    int msg = 0;

    // 这里创建AppSecurityPermissions,该能够将mPkgInfo中的Permission相关信息提出取来
    // 并且其内部有一个VIEW类PermissionItemView专门用于展示提出到的权限
    AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
    final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
    // 如果mAppInfo不为null,表示这是升级应用
    if (mAppInfo != null) {
        // 判断是否是system app
        msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                ? R.string.install_confirm_question_update_system
                : R.string.install_confirm_question_update;
        mScrollView = new CaffeinatedScrollView(this);
        mScrollView.setFillViewport(true);
        boolean newPermissionsFound = false;
        // 根据新添加Permission的情况以及系统版本情况显示不同的提示信息
        if (!supportsRuntimePermissions) {
            newPermissionsFound =
                    (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
            if (newPermissionsFound) {
                permVisible = true;
                mScrollView.addView(perms.getPermissionsView(
                        AppSecurityPermissions.WHICH_NEW));
            }
        }
        if (!supportsRuntimePermissions && !newPermissionsFound) {
            LayoutInflater inflater = (LayoutInflater)getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE);
            TextView label = (TextView)inflater.inflate(R.layout.label, null);
            label.setText(R.string.no_new_perms);
            mScrollView.addView(label);
        }
        // 将升级后的应用所需要的权限显示出来
        adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
                getText(R.string.newPerms)), mScrollView);
    }
    // 如果不支持运行时权限而且应用所需要的权限个数不为0
    if (!supportsRuntimePermissions && N > 0) {
        permVisible = true;
        LayoutInflater inflater = (LayoutInflater)getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        View root = inflater.inflate(R.layout.permissions_list, null);
        if (mScrollView == null) {
            mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
        }
        ((ViewGroup)root.findViewById(R.id.permission_list)).addView(
                    perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
        // 显示该应用所需要的全部权限
        adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
                getText(R.string.allPerms)), root);
    }
    if (!permVisible) {
        if (mAppInfo != null) {
            // 升级的应用,不需要更多额外的权限
            msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                    ? R.string.install_confirm_question_update_system_no_perms
                    : R.string.install_confirm_question_update_no_perms;
        } else {
            // 新安装的应用,不需要其他权限
            msg = R.string.install_confirm_question_no_perms;
        }

        // 加载显示应用不需要权限的界面
        bindUi(R.layout.install_confirm, true);
        mScrollView = null;
    }
    if (msg != 0) {
        ((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
    }
    if (mScrollView == null) {
        // 显示权限的scrollView中没有任何东西时,直接将安装界面的右下角的NEXT按钮修改为install
        mOk.setText(R.string.install);
        mOkCanInstall = true;
    } else {
        // 否则监听ScrollView事件
        mScrollView.setFullScrollAction(new Runnable() {
            @Override
            public void run() {
                mOk.setText(R.string.install);
                mOkCanInstall = true;
            }
        });
    }
}

  上面安装应用的界面大致如下,升级应用或者应用不需要额外权限的界面大致类似,只是提示信息不同而已:

《(原创)基于Android 8.1之PackageInstaller源码分析(二)》 《(原创)基于Android 8.1之PackageInstaller源码分析(二)》

  应用相关信息读取到这里,接下来就到了点击INSTALL确认安装的时候了。


PackageInstallerActivity.onClick

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开始安装程序
                    startInstall();
                }
            } else {
                // 这里表示此时permission页面显示的是上面左边的图
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        }
    // Cancel按钮的逻辑
    } else if (v == mCancel) {
        // Cancel and finish
        setResult(RESULT_CANCELED);
        if (mSessionId != -1) {
            mInstaller.setPermissionsResult(mSessionId, false);
        }
        finish();
    }
}

PackageInstallerActivity.startInstall

private void startInstall() {
    // 跳转到其他Activity进行实际安装过程
    Intent newIntent = new Intent();
    // 携带PackageInfo信息
    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
            mPkgInfo.applicationInfo);
    // 传递file协议的Urine
    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();
}

  这里并没有立刻开始进行实际的安装动作,而是将相关信息添加到一个Intent中,然后跳转到InstallInstalling这个Activity进行安装,下面我们去阅读这个类的代码来看实际安装所完成的操作。

InstallInstalling.java

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())) {
        .............
    } else {
        // 打开Uri指定的文件
        final File sourceFile = new File(mPackageURI.getPath());
        PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
                sourceFile), R.id.app_snippet);

        // 读取savedInstanceState的信息
        if (savedInstanceState != null) {
            mSessionId = savedInstanceState.getInt(SESSION_ID);
            mInstallId = savedInstanceState.getInt(INSTALL_ID);

            // 向InstallEventReceiver注册一个回调
            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 {
                // 调用parsePackageLite进行轻量级解析
                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 {
                // 和上面的一样,这里注册一个回调,launchFinishBasedOnResult会接收到安装事件的回调
                mInstallId = InstallEventReceiver
                        .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }

            try {
                // 生成安装该应用的sessionId
                mSessionId = getPackageManager().getPackageInstaller().createSession(params);
            } catch (IOException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        }

        // 监听cancel按钮事件
        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();
    }
}

  onCreate只是做一些基本的设置和注册动作,接着我们查看onResume的代码。


onResume

protected void onResume() {
    super.onResume();

    // 第一次调用的话,mInstallingTask为null,代表安装应用的task还没有创建
    if (mInstallingTask == null) {
        // 获取PackageInstaller对象
        PackageInstaller installer = getPackageManager().getPackageInstaller();
        // 根据sessionid获取SessionInfo信息
        PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

        // sessionInfo不为null并且不是活动的
        if (sessionInfo != null && !sessionInfo.isActive()) {
            // 创建安装的异步任务
            mInstallingTask = new InstallingAsyncTask();
            // 调用execute会触发AsyncTask的onPostExecute调用
            mInstallingTask.execute();
        } else {
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        }
    }
}

  上述SessionInfo代表的是一个安装绘画的详细信息,如果我们安装应用第一次到这里的话,那么这个sessionInfo是处于非活动状态的。接着看InstallingAsyncTask完成什么类型工作。


InstallingAsyncTask

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创建要给session
            session = getPackageManager().getPackageInstaller().openSession(mSessionId);
        } catch (IOException e) {
            return null;
        }

        // 设置安装进度条
        session.setStagingProgress(0);

        try {
            // 根据Uri打开文件
            File file = new File(mPackageURI.getPath());

            // 读取文件
            try (InputStream in = new FileInputStream(file)) {
                long sizeBytes = file.length();
                // 下面的操作是将文件中的内容写到PackageInstaller的session中
                try (OutputStream out = session
                        .openWrite("PackageInstaller", 0, sizeBytes)) {
                    byte[] buffer = new byte[1024 * 1024];
                    while (true) {
                        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,其中的Action为:
            // BROADCAST_ACTION = "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
            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 = PendingIntent.getBroadcast(
                    InstallInstalling.this,
                    mInstallId,
                    broadcastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);

            // 调用session的commit方法将intent发送出去
            session.commit(pendingIntent.getIntentSender());
            // 此时将cancel按钮设置为不可用
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        } else {
            getPackageManager().getPackageInstaller().abandonSession(mSessionId);

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

接下来分析PackageInstaller.Session的commit方法完成的事情。


commit

public void commit(@NonNull IntentSender statusReceiver) {
    try {
        mSession.commit(statusReceiver, false);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

  上述mSession定义如下为private IPackageInstallerSession mSession;,代表mSession是一个IPackageInstallSession代理对象,通过这个代理可以和PackageInstallerSession进行binder通信。


PackageInstallSession.java

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();

        // forTransfer为false
        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");
            }
        }

        // 初始化时mSealed为false,在sealAndValidateLocked中会将mSealed置为true
        wasSealed = mSealed;
        if (!mSealed) {
            try {
                // 封装好session,防止session信息被修改
                sealAndValidateLocked();
            } catch (IOException e) {
                throw new IllegalArgumentException(e);
            } catch (PackageManagerException e) {
                destroyInternal();
                mHandler.obtainMessage(MSG_SESSION_FINISHED_WITH_EXCEPTION, e).sendToTarget();

                return;
            }
        }

        mClientProgress = 1f;
        computeProgressLocked(true);

        mActiveCount.incrementAndGet();

        // 然后想mHandler发送MSG_COMMIT信息
        mCommitted = true;
        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    }

    if (!wasSealed) {
        mCallback.onSessionSealedBlocking(this);
    }
}

  上面的代码最主要的是在最后的部分通过mHandler发送MSG_COMMIT消息。mHandler是Handler对象,其通过mHandler = new Handler(looper, mHandlerCallback);进行初始化。


mHandlerCallback

private final Handler.Callback mHandlerCallback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_COMMIT:
                synchronized (mLock) {
                    try {
                        // MSG_COMMIT的主要处理函数
                        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:
                ................
                break;
        }

        return true;
    }
};

  上面的代码处理起来比较简单,主要调用commitLocked来完成。下面直接来看commitLocked方法的代码。


commitLocked

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

    // 检擦是否仍有权限需要确认
    if (needToAskForPermissionsLocked()) {
        // User needs to accept permissions; give installer an intent they
        // can use to involve user.
        final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
        intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
        intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
        try {
            mRemoteObserver.onUserActionRequired(intent);
        } catch (RemoteException ignored) {
        }

        // Commit was keeping session marked as active until now; release
        // that extra refcount so session appears idle.
        closeInternal(false);
        return;
    }

    if (stageCid != null) {
        // Figure out the final installed size and resize the container once
        // and for all. Internally the parser handles straddling between two
        // locations when inheriting.
        final long finalSize = calculateInstalledSize();
        resizeContainer(stageCid, finalSize);
    }

    // 继承现有安装包中没有被覆盖的native库
    if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
        try {
            final List<File> fromFiles = mResolvedInheritedFiles;
            final File toDir = resolveStageDirLocked();

            if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
            if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
                throw new IllegalStateException("mInheritedFilesBase == null");
            }

            if (isLinkPossible(fromFiles, toDir)) {
                if (!mResolvedInstructionSets.isEmpty()) {
                    final File oatDir = new File(toDir, "oat");
                    createOatDirs(mResolvedInstructionSets, oatDir);
                }
                linkFiles(fromFiles, toDir, mInheritedFilesBase);
            } else {
                // TODO: this should delegate to DCS so the system process
                // avoids holding open FDs into containers.
                copyFiles(fromFiles, toDir);
            }
        } catch (IOException e) {
            throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                    "Failed to inherit existing install", e);
        }
    }

    mInternalProgress = 0.5f;
    computeProgressLocked(true);

    // 解压native库
    extractNativeLibraries(mResolvedStageDir, params.abiOverride);

    if (stageCid != null) {
        finalizeAndFixContainer(stageCid);
    }

    // 获取IPackageInstallObserver2对象,用于binder通信
    final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
        @Override
        public void onUserActionRequired(Intent intent) {
            throw new IllegalStateException();
        }

        @Override
        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                Bundle extras) {
            destroyInternal();
            dispatchSessionFinished(returnCode, msg, extras);
        }
    };

    final UserHandle user;
    if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
        user = UserHandle.ALL;
    } else {
        user = new UserHandle(userId);
    }

    // 最最最重要的部分了,这里会调用PackageManager进行最后的安装动作
    mRelinquished = true;
    mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
            mInstallerPackageName, mInstallerUid, user, mCertificates);
}

  上面的代码到最后PackageInstallerSession的使命就到此结束了,最后会跳转到PackageManagerServic中执行。这一篇就先到这里了,之后PackageManagerServic的处理到下一篇再讲述。

小结

下面是这篇文章的简单总结:

  1. 在startInstallConfirm中会显示安装包内定义所需的权限信息
  2. 在点击Install后APK的内容会通过IO流的方式写到PackageInsaller.Session中
  3. 最后会调用PackageInstaller.Session的commit方法将请求到PKMS中

最后在PKMS中的处理我们留待下一篇再分析。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: