Android P PackageInstaller 學(xué)習(xí)

總體而言, Android P 和 Android O相比,PackageInstaller模塊變化不大。因?yàn)锳ndroid O的時(shí)候沒來及總結(jié),所以一些 Android O上就出現(xiàn)的特性也會(huì)在這里記錄學(xué)習(xí)一下。

界面跳轉(zhuǎn)流程

Android P PackageInstaller.png

??從界面跳轉(zhuǎn)流程上看,與 Android O相比,多了一個(gè) DeleteStagedFileOnResult 頁面,主要就是把原來 跳轉(zhuǎn)PackageInstallerActivity 和刪除 臨時(shí)文件的動(dòng)作 拎到這里來了。

一些重要的點(diǎn)

  • Android O開始,三方應(yīng)用如果想調(diào)起 系統(tǒng) PackageInstaller 安裝/卸載應(yīng)用,必須在AndroidManifest中聲明對應(yīng)的權(quán)限:
    <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
    <uses-permission android:name="android.permission.DELETE_PACKAGES"/>'

否則無法實(shí)現(xiàn)安裝會(huì)有類似 log打出來:

10-26 16:26:25.188  7457  7457 E InstallStart: Requesting uid 10106 needs to declare permission android.permission.REQUEST_INSTALL_PACKAGES

看一看這一部分的檢測邏輯,代碼位置在 InstallStart.javaoncreate方法中

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        .............
        .............
        final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
        final int originatingUid = getOriginatingUid(sourceInfo);
        /**
         * 下面是判定  未知來源  的邏輯
         */
        boolean isTrustedSource = false;
        //如果是 priv-app, 就去看Intent中 EXTRA_NOT_UNKNOWN_SOURCE 參數(shù),也就是說,如果調(diào)起安裝器的不是priv-app, 帶不帶這個(gè)參數(shù)都沒用
        //如果是 priv-app, 再看 EXTRA_NOT_UNKNOWN_SOURCE 參數(shù)的值
        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(this, 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(
                    //在 Android O 及以上版本中,調(diào)起安裝器需要聲明REQUEST_INSTALL_PACKAGES  權(quán)限,否則直接放棄安裝
                    originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
                Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
                        + Manifest.permission.REQUEST_INSTALL_PACKAGES);
                mAbortInstall = true;
            }
        }
        if (mAbortInstall) {
            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())) {
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
            Uri packageUri = intent.getData();

            /*
            幾種類型的Uri, file,content,package, 不同的Uri跳轉(zhuǎn)到不同的Activity
             */
            if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
                    || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
                // Copy file to prevent it from being changed underneath this process
                nextActivity.setClass(this, InstallStaging.class);
            } else if (packageUri != null && packageUri.getScheme().equals(
                    PackageInstallerActivity.SCHEME_PACKAGE)) {
                nextActivity.setClass(this, PackageInstallerActivity.class);
            } else {
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            }
        }

        if (nextActivity != null) {
            startActivity(nextActivity);
        }
        finish();
    }
  • Android O上去除了 未知來源 總開關(guān),改為單個(gè)渠道控制 未知來源的 安裝請求。


    9833687_5.png

    這一部分檢測邏輯如下,代碼位置在 PackageInstallerActivity.java

    private void handleUnknownSources() {
        if (mOriginatingPackage == null) {
            Log.i(TAG, "No source found for package " + mPkgInfo.packageName);
            showDialogInner(DLG_ANONYMOUS_SOURCE);
            return;
        }
        /**
         * 檢查callingPackage是否具有REQUEST_INSTALL_PACKAGES權(quán)限,如果 權(quán)限標(biāo)志位 是 AppOpsManager.MODE_ERRORED,
         * 就顯示 渠道未知來源對話框,可以進(jìn)入設(shè)置中設(shè)置開關(guān)
         */
        // Shouldn't use static constant directly, see b/65534401.
        final int appOpCode =
                AppOpsManager.permissionToOpCode(Manifest.permission.REQUEST_INSTALL_PACKAGES);
        final int appOpMode = mAppOpsManager.noteOpNoThrow(appOpCode,
                mOriginatingUid, mOriginatingPackage);
        switch (appOpMode) {
            case AppOpsManager.MODE_DEFAULT:
                try {
                    int result = mIpm.checkUidPermission(
                            Manifest.permission.REQUEST_INSTALL_PACKAGES, mOriginatingUid);
                    if (result == PackageManager.PERMISSION_GRANTED) {
                        initiateInstall();
                        break;
                    }
                } catch (RemoteException exc) {
                    Log.e(TAG, "Unable to talk to package manager");
                }
                mAppOpsManager.setMode(appOpCode, mOriginatingUid,
                        mOriginatingPackage, AppOpsManager.MODE_ERRORED);
                // fall through
                //渠道未知來源對話框
            case AppOpsManager.MODE_ERRORED:
                showDialogInner(DLG_EXTERNAL_SOURCE_BLOCKED);
                break;
            case AppOpsManager.MODE_ALLOWED:
                initiateInstall();
                break;
            default:
                Log.e(TAG, "Invalid app op mode " + appOpMode
                        + " for OP_REQUEST_INSTALL_PACKAGES found for uid " + mOriginatingUid);
                finish();
                break;
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,950評論 2 59
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,917評論 25 709
  • 你的臉上云淡風(fēng)輕,誰也不知道你的牙咬得有多緊。你走路帶著風(fēng),誰也不知道你膝蓋上仍有曾摔傷的淤青。你必須非常努力,才...
    智贏人生閱讀 2,766評論 0 1
  • 01. 傳說中,天上管理馬匹的神仙叫伯樂。一次,伯樂受楚王的委托,購買能日行干里的駿馬。他從齊國返回,看到...
    甘夢閱讀 1,454評論 3 14
  • 如果你很有趣,就做我朋友吧。不要做我男朋友,我怕分手后會(huì)少了一個(gè)那么有趣的朋友。男朋友的話,只要帥就可以了,有缺點(diǎn)...
    momitor閱讀 196評論 0 0

友情鏈接更多精彩內(nèi)容