Android12分析(更新內容、Android apk安裝、適配)

Android 12 新功能分析
https://developer.android.com/about/versions/12/features
https://blog.csdn.net/weixin_40611659/article/details/119645712
關于存儲:
/**
* Allow apps to create the requests to manage the media files without user confirmation.
*
* @see android.Manifest.permission#MANAGE_MEDIA
* @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)
* @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)
* @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)
*
* @hide
*/

https://juejin.cn/post/6932363591689437192

關于項目中的適配
android 12 適配 (kdocs.cn)

apk的大體流程如下:

· 第一步:拷貝文件到指定的目錄: 在Android系統中,apk安裝文件是會被保存起來的,默認情況下,用戶安裝的apk首先會被拷貝到/data/app目錄下,/data/app目錄是用戶有權限訪問的目錄,在安裝apk的時候會自動選擇該目錄存放用戶安裝的文件,而系統出場的apk文件則被放到了/system分區下,包括/system/app,/system/vendor/app,以及/system/priv-app等等,該分區只有ROOT權限的用戶才能訪問,這也就是為什么在沒有Root手機之前,我們沒法刪除系統出場的app的原因了。

· 第二步:解壓縮apk,寶貝文件,創建應用的數據目錄 為了加快app的啟動速度,apk在安裝的時候,會首先將app的可執行文件dex拷貝到/data/dalvik-cache目錄,緩存起來。然后,在/data/data/目錄下創建應用程序的數據目錄(以應用的包名命名),存放在應用的相關數據,如數據庫、xml文件、cache、二進制的so動態庫等。

· 第三步:解析apk的AndroidManifest.xml文件

Android系統中,也有一個類似注冊表的東西,用來記錄當前所有安裝的應用的基本信息,每次系統安裝或者卸載了任何apk文件,都會更新這個文件。這個文件位于如下目錄:/data/system/packages.xml。系統在安裝這個apk的過程中,會解析apk的AndroidManifest.xml文件,提取出這個apk的重要信息寫入到packages.xml文件中,這些信息包括:權限、應用包名、APK的安裝位置、版本、userID等等。由此,我們就知道了為什么一些應用市場和軟件管理類的app能夠很清楚地知道當前手機所安裝的所有app,以及這些app的詳細信息了。另外一件事就是Linux的用戶Id和用戶組Id,以便他們可以獲得合適的運行權限。以上都是由PackageServcieManager完成的,后面我們會重點介紹PackageServiceManager。

第四步:顯示快捷方式 如果這些應用程序在PackageManagerService服務注冊好了,如果我們想要在Android桌米上看到這些應用程序,還需要有一個Home應用程序,負責從PackageManagerService服務中把這些安裝好的應用程序取出來,并以友好的方式在桌面上展現出來,例如以快捷圖標的形式。在Android系統中,負責把系統中已經安裝的應用程序在桌面中展現出來的Home應用就是Launcher了。
安裝 APK 主要分為以下三種場景

  • 安裝系統應用:系統啟動后調用 PackageManagerService.main() 初始化注冊解析安裝工作
public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    // Self-check for initial settings.
    PackageManagerServiceCompilerMapping.checkProperties();

    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    m.enableSystemUserPackages();
    ServiceManager.addService("package", m);
    final PackageManagerNative pmn = m.new PackageManagerNative();
    ServiceManager.addService("package_native", pmn);
    return m;
}

  • 通過 adb 安裝:通過 pm 參數,調用 PM 的 runInstall 方法,進入 PackageManagerService 安裝安裝工作
  • 通過系統安裝器 PackageInstaller 進行安裝:先調用 InstallStart 進行權限檢查之后啟動 PackageInstallActivity,調用 PackageInstallActivity 的 startInstall 方法,點擊 OK 按鈕后進入 PackageManagerService 完成拷貝解析安裝工作

所有安裝方式大致相同,最終就是回到 PackageManagerService 中,安裝一個 APK 的大致流程如下:

  • 拷貝到 APK 文件到指定目錄
  • 解壓縮 APK,拷貝文件,創建應用的數據目錄
  • 解析 APK 的 AndroidManifest.xml 文件
  • 向 Launcher 應用申請添加創建快捷方式

本文主要來分析通過安裝器 PackageInstaller 安裝 APK,這是用戶最常用的一種方式

7.0以前安裝的入口是PackageInstallerActivity 7.0以后是InstallStart

3.1 InstallStart

主要工作:

判斷是否勾選“未知來源”選項,若未勾選跳轉到設置安裝未知來源界面
對于大于等于 Android 8.0 版本,會先檢查是否申請安裝權限,若沒有則中斷安裝
判斷 Uri 的 Scheme 協議,若是 content 則調用 InstallStaging, 若是 package 則調用 PackageInstallerActivity,但是實際上 installStaging中的 StagingAsyncTask 會將content協議的Uri轉換為File協議,然后跳轉到PackageInstallerActivity,所以最終安裝的開始還是PackageInstallerActivity

3.2 PackageInstallerActivity

主要工作:

  1. 顯示安裝界面
  2. 初始化安裝需要用的各種對象,比如 PackageManager、IPackageManager、AppOpsManager、UserManager、PackageInstaller 等等
  3. 根據傳遞過來的 Scheme 協議做不同的處理
  4. 檢查是否允許、初始化安裝
  5. 在準備安裝的之前,檢查應用列表判斷該應用是否已安裝,若已安裝則提示該應用已安裝,由用戶決定是否替換
  6. 在安裝界面,提取出 APK 中權限信息并展示出來
  7. 點擊 OK 按鈕確認安裝后,會調用 startInstall 開始安裝工作
protected void onCreate(Bundle icicle) {
    getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
    super.onCreate(null);
    // 初始化安裝需要用到的對象
    mPm = getPackageManager();
    mIpm = AppGlobals.getPackageManager();
    mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    mInstaller = mPm.getPackageInstaller();
    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);

    // 根據Uri的Scheme做不同的處理
    boolean wasSetUp = processPackageUri(packageUri);
    if (!wasSetUp) {
        return;
    }
    // 顯示安裝界面
    bindUi();
    // 檢查是否允許安裝包,如果允許則啟動安裝。如果不允許顯示適當的對話框
    checkIfAllowedAndInitiateInstall();
}

主要做了對象的初始化,解析 Uri 的 Scheme,初始化界面,安裝包檢查等等工作,接著查看一下



processPackageUri 方法

private boolean processPackageUri(final Uri packageUri) {
    mPackageURI = packageUri;
    final String scheme = packageUri.getScheme();
    // 根據這個Scheme協議分別對package協議和file協議進行處理
    switch (scheme) {
        case SCHEME_PACKAGE: {
            try {
                // 通過PackageManager對象獲取指定包名的包信息
                mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
                        PackageManager.GET_PERMISSIONS
                                | PackageManager.MATCH_UNINSTALLED_PACKAGES);
            } catch (NameNotFoundException e) {
            }
            if (mPkgInfo == null) {
                Log.w(TAG, "Requested package " + packageUri.getScheme()
                        + " not available. Discontinuing installation");
                showDialogInner(DLG_PACKAGE_ERROR);
                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                return false;
            }
            mAppSnippet = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
                    mPm.getApplicationIcon(mPkgInfo.applicationInfo));
        } break;

        case ContentResolver.SCHEME_FILE: {
            // 根據packageUri創建一個新的File
            File sourceFile = new File(packageUri.getPath());
            // 解析APK得到APK的信息,PackageParser.Package存儲了APK的所有信息
            PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);

            if (parsed == null) {
                Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
                showDialogInner(DLG_PACKAGE_ERROR);
                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                return false;
            }
            // 根據PackageParser.Package得到的APK信息,生成PackageInfo
            mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                    PackageManager.GET_PERMISSIONS, 0, 0, null,
                    new PackageUserState());
            mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
        } break;

        default: {
            throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);
        }
    }

    return true;
}

主要對 Scheme 協議分別對 package 協議和 file 協議進行處理
SCHEME_PACKAGE:

在 package 協議中調用了 PackageManager.getPackageInfo 方法生成 PackageInfo,PackageInfo 是跨進程傳遞的包數據(activities、receivers、services、providers、permissions等等)包含 APK 的所有信息

SCHEME_FILE:

在 file 協議的處理中調用了 PackageUtil.getPackageInfo 方法,方法內部調用了 PackageParser.parsePackage() 把 APK 文件的 manifest 和簽名信息都解析完成并保存在了 Package,Package 包含了該 APK 的所有信息
調用 PackageParser.generatePackageInfo 生成 PackageInfo

接著往下走,都解析完成之后,回到 onCreate 方法,繼續調用 checkIfAllowedAndInitiateInstall 方法

private void checkIfAllowedAndInitiateInstall() {
    ## 首先檢查安裝應用程序的用戶限制,如果有限制并彈出彈出提示Dialog或者跳轉到設置界面
    final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
            UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
    if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
        showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
        return;
    } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
        startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
        finish();
        return;
    }

    // 判斷如果允許安裝未知來源或者根據Intent判斷得出該APK不是未知來源
    if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
        initiateInstall();
    } else {
        // 檢查未知安裝源限制,如果有限制彈出Dialog,顯示相應的信息
        final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
        final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
        final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
                & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
        if (systemRestriction != 0) {
            showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
        } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
            startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
        } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
            startAdminSupportDetailsActivity(
                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
        } else {
            // 處理未知來源的APK
            handleUnknownSources();
        }
    }
}

主要檢查安裝應用程序的用戶限制,當 APK 文件不對或者安裝有限制則調用 showDialogInner 方法,彈出 dialog 提示用戶,顯示相應的錯誤信息,來看一下都有那些錯誤信息

// Dialog identifiers used in showDialog
private static final int DLG_BASE = 0;
// package信息錯誤
private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
// 存儲空間不夠
private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
// 安裝錯誤
private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
// 用戶限制的未知來源
private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = DLG_BASE + 5;
private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 6;
// 在wear上不支持
private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 8;
// 安裝限制用戶使用的應用程序
private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = DLG_BASE + 9;

如果用戶允許安裝未知來源,會調用 initiateInstall 方法

private void initiateInstall() {
    String pkgName = mPkgInfo.packageName;
    // 檢查設備上是否存在相同包名的APK
    String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
    if (oldName != null && oldName.length > 0 && oldName[0] != null) {
        pkgName = oldName[0];
        mPkgInfo.packageName = pkgName;
        mPkgInfo.applicationInfo.packageName = pkgName;
    }
    // 檢查package是否已安裝, 如果已經安裝則顯示對話框提示用戶是否替換。
    try {
        mAppInfo = mPm.getApplicationInfo(pkgName,
                PackageManager.MATCH_UNINSTALLED_PACKAGES);
        if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
            mAppInfo = null;
        }
    } catch (NameNotFoundException e) {
        mAppInfo = null;
    }
    // 初始化確認安裝界面
    startInstallConfirm();
}

根據包名獲取應用程序的信息,調用 startInstallConfirm 方法初始化安裝確認界面后,當用戶點擊確認按鈕之后發生了什么,接著查看確認按鈕點擊事件

private void bindUi() {
   ...
    // 點擊確認按鈕,安裝APK
    mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
            (ignored, ignored2) -> {
                if (mOk.isEnabled()) {
                    if (mSessionId != -1) {
                        mInstaller.setPermissionsResult(mSessionId, true);
                        finish();
                    } else {
                        // 啟動Activity來完成應用的安裝
                        startInstall();
                    }
                }
            }, null);
   // 點擊取消按鈕,取消此次安裝
    mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
            (ignored, ignored2) -> {
                // Cancel and finish
                setResult(RESULT_CANCELED);
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, false);
                }
                finish();
            }, null);
    setupAlert();
    mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
    mOk.setEnabled(false);
}

當用戶點擊確認按鈕調用了 startInstall 方法,啟動子 Activity 完成 APK 的安裝

private void startInstall() {
    // 啟動子Activity來完成應用的安
    Intent newIntent = new Intent();
    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
            mPkgInfo.applicationInfo);
    newIntent.setData(mPackageURI);
    newIntent.setClass(this, InstallInstalling.class);
    ...
    if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
    startActivity(newIntent);
    finish();
}

startInstall 方法用來跳轉到 InstallInstalling,并關閉掉當前的 PackageInstallerActivity

3.3 InstallInstalling

主要工作:

向包管理器發送包的信息,然后等待包管理器處理結果
注冊一個觀察者 InstallEventReceiver,并接受安裝成功和失敗的回調
在方法 onResume 中創建同步棧,打開安裝 session,設置安裝進度條

InstallInstalling 首先向包管理器發送包的信息,然后等待包管理器處理結果,并在方法 InstallSuccess 和方法 InstallFailed 進行成功和失敗的處理,查看 InstallInstalling 的 onCreate 方法:

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

    // 判斷安裝的應用是否已經存在
    if ("package".equals(mPackageURI.getScheme())) {
        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.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
        ...

        if (savedInstanceState != null) {
            // 如果savedInstanceState 不為空,獲取已經存在mSessionId 和mInstallId 重新注冊
            mSessionId = savedInstanceState.getInt(SESSION_ID);
            mInstallId = savedInstanceState.getInt(INSTALL_ID);
            try {
                // 根據mInstallId向InstallEventReceiver注冊一個觀察者,launchFinishBasedOnResult會接收到安裝事件的回調
                InstallEventReceiver.addObserver(this, mInstallId,
                        this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
            }
        } else {
            // 如果為空創建SessionParams,代表安裝會話的參數
            // 解析APK, 并將解析的參數賦值給SessionParams
            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
            ...

            try {
                // 注冊InstallEventReceiver,并在launchFinishBasedOnResult會接收到安裝事件的回調
                mInstallId = InstallEventReceiver
                        .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }

            try {
                // createSession 內部通過IPackageInstaller與PackageInstallerService進行進程間通信,
                // 最終調用的是PackageInstallerService的createSession方法來創建并返回mSessionId
                mSessionId = getPackageManager().getPackageInstaller().createSession(params);
            } catch (IOException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        }
            ...
    }
}

最終都會注冊一個觀察者 InstallEventReceiver,并在 launchFinishBasedOnResult 會接收到安裝事件的回調,其中 InstallEventReceiver 繼承自 BroadcastReceiver,用于接收安裝事件并回調給 EventResultPersister
createSession 內部通過 IPackageInstaller 與 PackageInstallerService 進行進程間通信,最終調用的是 PackageInstallerService的createSession 方法來創建并返回 mSessionId
接下來在 onResume 方法創建 InstallingAsyncTask 用來執行 APK 的安裝,接著查看 onResume 方法

protected void onResume() {
    super.onResume();
    if (mInstallingTask == null) {
        PackageInstaller installer = getPackageManager().getPackageInstaller();
        // 根據mSessionId 獲取SessionInfo, 代表安裝會話的詳細信息
        PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
        if (sessionInfo != null && !sessionInfo.isActive()) {
            mInstallingTask = new InstallingAsyncTask();
            mInstallingTask.execute();
        } else {
            // 安裝完成后會收到廣播
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        }
    }
}

得到 SessionInfo 創建并創建 InstallingAsyncTask,InstallingAsyncTask 的 doInBackground 方法設置安裝進度條,并將 APK 信息寫入 PackageInstaller.Session,寫入完成之后,在 InstallingAsyncTask 的 onPostExecute 進行成功與失敗的處理,接著查看 onPostExecute 方法

protected void onPostExecute(PackageInstaller.Session session) {
    if (session != null) {
        Intent broadcastIntent = new Intent(BROADCAST_ACTION);
        broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        broadcastIntent.setPackage(getPackageName());
        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);
        }
    }
}

創建了 broadcastIntent,并通過 PackageInstaller.Session 的 commit 方法發送出去,通過 broadcastIntent 構造方法指定的 Intent 的 Action 為 BROADCAST_ACTION,而 BROADCAST_ACTION 是一個常量值

 private static final String BROADCAST_ACTION =
            "com.android.packageinstaller.ACTION_INSTALL_COMMIT";

回到 InstallInstalling.OnCreate 方法,在 OnCreate 方法注冊 InstallEventReceiver,而 InstallEventReceiver 繼承自 BroadcastReceiver,而使用 BroadcastReceiver 需要在 AndroidManifest.xml注冊,接著查看 AndroidManifest.xml:
/frameworks/base/packages/PackageInstaller/AndroidManifest.xml

<receiver android:name=".InstallEventReceiver"
        android:permission="android.permission.INSTALL_PACKAGES"
        android:exported="true">
    <intent-filter android:priority="1">
        <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
    </intent-filter>
</receiver>

安裝結束之后,會在觀察者 InstallEventReceiver 注冊的回調方法 launchFinishBasedOnResult 處理安裝事件的結果,接著查看 launchFinishBasedOnResult

private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
    if (statusCode == PackageInstaller.STATUS_SUCCESS) {
        launchSuccess();
    } else {
        launchFailure(legacyStatus, statusMessage);
    }
}

private void launchSuccess() {
    Intent successIntent = new Intent(getIntent());
    successIntent.setClass(this, InstallSuccess.class);
    successIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    startActivity(successIntent);
    finish();
}
    
private void launchFailure(int legacyStatus, String statusMessage) {
    Intent failureIntent = new Intent(getIntent());
    failureIntent.setClass(this, InstallFailed.class);
    failureIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    failureIntent.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, legacyStatus);
    failureIntent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
    startActivity(failureIntent);
    finish();
}

安裝成功和失敗,都會啟動一個新的 Activity(InstallSuccess、InstallFailed)將結果展示給用戶,然后 finish 掉 InstallInstalling
總結一下 PackageInstaller 安裝APK的過程:
現在來總結下PackageInstaller初始化的過程:

根據Uri的Scheme協議不同,跳轉到不同的界面,content協議跳轉到InstallStart,其他的跳轉到PackageInstallerActivity。本文應用場景中,如果是Android7.0以及更高版本會跳轉到InstallStart。

InstallStart將content協議的Uri轉換為File協議,然后跳轉到PackageInstallerActivity。

PackageInstallerActivity會分別對package協議和file協議的Uri進行處理,如果是file協議會解析APK文件得到包信息PackageInfo。
點擊 OK 按鈕確認安裝后,會調用 startInstall 開始安裝工作
如果用戶允許安裝,然后跳轉到 InstallInstalling,進行 APK 的安裝工作
在 InstallInstalling 中,向包管理器發送包的信息,然后注冊一個觀察者 InstallEventReceiver,并接受安裝成功和失敗的回調

PackageInstallSession

Android通過PackageInstallerSession來表示一次安裝過程,一個PackageInstallerSession包含一個系統中唯一的一個SessionId,如果一個應用的安裝必須分幾個階段來完成,即使設備重啟了,也可以通過這個ID來繼續安裝過程

PackageInstallerService

安裝的遠程服務類,用于返回sessionid



 @Override
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        if (hasParentSessionId()) {
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + getParentSessionId() +  " and may not be committed directly.");
        }

        if (!markAsSealed(statusReceiver, forTransfer)) {
            return;
        }
        if (isMultiPackage()) {
            synchronized (mLock) {
                final IntentSender childIntentSender =
                        new ChildStatusIntentReceiver(mChildSessions.clone(), statusReceiver)
                                .getIntentSender();
                boolean sealFailed = false;
                for (int i = mChildSessions.size() - 1; i >= 0; --i) {
                    // seal all children, regardless if any of them fail; we'll throw/return
                    // as appropriate once all children have been processed
                    if (!mChildSessions.valueAt(i)
                            .markAsSealed(childIntentSender, forTransfer)) {
                        sealFailed = true;
                    }
                }
                if (sealFailed) {
                    return;
                }
            }
        }

        dispatchSessionSealed();
    }

   /**
     * Kicks off the install flow. The first step is to persist 'sealed' flags
     * to prevent mutations of hard links created later.
     */
    private void dispatchSessionSealed() {
        mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
    }
 private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ON_SESSION_SEALED:
                    handleSessionSealed();
                    break;
                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit();
                    break;
                case MSG_INSTALL:
                    handleInstall();
                    break;
                case MSG_ON_PACKAGE_INSTALLED:
                    final SomeArgs args = (SomeArgs) msg.obj;
                    final String packageName = (String) args.arg1;
                    final String message = (String) args.arg2;
                    final Bundle extras = (Bundle) args.arg3;
                    final IntentSender statusReceiver = (IntentSender) args.arg4;
                    final int returnCode = args.argi1;
                    args.recycle();

                    sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                            isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
                            packageName, returnCode, message, extras);

                    break;
                case MSG_SESSION_VALIDATION_FAILURE:
                    final int error = msg.arg1;
                    final String detailMessage = (String) msg.obj;
                    onSessionValidationFailure(error, detailMessage);
                    break;
            }

            return true;
        }
    };

經過一系列處理最終是調用的還是Pms中的安裝方法

 private void installNonStaged()
            throws PackageManagerException {
        final PackageManagerService.InstallParams installingSession = makeInstallParams();
        if (installingSession == null) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                    "Session should contain at least one apk session for installation");
        }
        if (isMultiPackage()) {
            final List<PackageInstallerSession> childSessions;
            synchronized (mLock) {
                childSessions = getChildSessionsLocked();
            }
            List<PackageManagerService.InstallParams> installingChildSessions =
                    new ArrayList<>(childSessions.size());
            boolean success = true;
            PackageManagerException failure = null;
            for (int i = 0; i < childSessions.size(); ++i) {
                final PackageInstallerSession session = childSessions.get(i);
                try {
                    final PackageManagerService.InstallParams installingChildSession =
                            session.makeInstallParams();
                    if (installingChildSession != null) {
                        installingChildSessions.add(installingChildSession);
                    }
                } catch (PackageManagerException e) {
                    failure = e;
                    success = false;
                }
            }
            if (!success) {
                final IntentSender statusReceiver;
                synchronized (mLock) {
                    statusReceiver = mRemoteStatusReceiver;
                }
                sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                        isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
                        failure.error, failure.getLocalizedMessage(), null);
                return;
            }
            mPm.installStage(installingSession, installingChildSessions);
        } else {
            mPm.installStage(installingSession);
        }
    }

packageManagerService

 final void startCopy() {
            if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
            handleStartCopy();
            handleReturnCode();
        }

 @Override
        void handleReturnCode() {
            processPendingInstall();
        }

        private void processPendingInstall() {
            InstallArgs args = createInstallArgs(this);
            if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                mRet = args.copyApk();
            }
            if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                F2fsUtils.releaseCompressedBlocks(
                        mContext.getContentResolver(), new File(args.getCodePath()));
            }
            if (mParentInstallParams != null) {
                mParentInstallParams.tryProcessInstallRequest(args, mRet);
            } else {
                PackageInstalledInfo res = createPackageInstalledInfo(mRet);
                processInstallRequestsAsync(
                        res.returnCode == PackageManager.INSTALL_SUCCEEDED,
                        Collections.singletonList(new InstallRequest(args, res)));
            }
        }
  // Queue up an async operation since the package installation may take a little while.
    private void processInstallRequestsAsync(boolean success,
            List<InstallRequest> installRequests) {
        mHandler.post(() -> {
            List<InstallRequest> apexInstallRequests = new ArrayList<>();
            List<InstallRequest> apkInstallRequests = new ArrayList<>();
            for (InstallRequest request : installRequests) {
                if ((request.args.installFlags & PackageManager.INSTALL_APEX) != 0) {
                    apexInstallRequests.add(request);
                } else {
                    apkInstallRequests.add(request);
                }
            }
            // Note: supporting multi package install of both APEXes and APKs might requir some
            // thinking to ensure atomicity of the install.
            if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
                // This should've been caught at the validation step, but for some reason wasn't.
                throw new IllegalStateException(
                        "Attempted to do a multi package install of both APEXes and APKs");
            }
            if (!apexInstallRequests.isEmpty()) {
                if (success) {
                    // Since installApexPackages requires talking to external service (apexd), we
                    // schedule to run it async. Once it finishes, it will resume the install.
                    Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
                            "installApexPackages");
                    t.start();
                } else {
                    // Non-staged APEX installation failed somewhere before
                    // processInstallRequestAsync. In that case just notify the observer about the
                    // failure.
                    InstallRequest request = apexInstallRequests.get(0);
                    notifyInstallObserver(request.installResult, request.args.observer);
                }
                return;
            }
            if (success) {
                for (InstallRequest request : apkInstallRequests) {
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                synchronized (mInstallLock) {
                    installPackagesTracedLI(apkInstallRequests);
                }
                for (InstallRequest request : apkInstallRequests) {
                    request.args.doPostInstall(
                            request.installResult.returnCode, request.installResult.uid);
                }
            }
            for (InstallRequest request : apkInstallRequests) {
                restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
                        new PostInstallData(request.args, request.installResult, null));
            }
        });
    }
  if (!doRestore) {
            // No restore possible, or the Backup Manager was mysteriously not
            // available -- just fire the post-install work request directly.
            if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);

            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);

            Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
            mHandler.sendMessage(msg);
        }

  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 && data.res.freezer != null) {
                        data.res.freezer.close();
                    }

                    if (data != null && data.mPostInstallRunnable != null) {
                        data.mPostInstallRunnable.run();
                    } else if (data != null && data.args != null) {
                        InstallArgs args = data.args;
                        PackageInstalledInfo parentRes = data.res;

                        final boolean killApp = (args.installFlags
                                & PackageManager.INSTALL_DONT_KILL_APP) == 0;
                        final boolean virtualPreload = ((args.installFlags
                                & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);

                        handlePackagePostInstall(parentRes, killApp, virtualPreload,
                                didRestore, args.installSource.installerPackageName, args.observer,
                                args.mDataLoaderType);

https://www.edrawmax.cn/online/share.html?code=98e50f7242b611ec802fefb61da1a18d

Android 12 針對使用 PackageInstaller API 的應用引入了 setRequireUserAction() 方法。此方法可讓安裝程序應用執行應用更新而無需用戶確認操作。

   @UserActionRequirement
    private int computeUserActionRequirement() {
        final String packageName;
        synchronized (mLock) {
            if (mPermissionsManuallyAccepted) {
                return USER_ACTION_NOT_NEEDED;
            }
            packageName = mPackageName;
        }

        final boolean forcePermissionPrompt =
                (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0
                        || params.requireUserAction == SessionParams.USER_ACTION_REQUIRED;
        if (forcePermissionPrompt) {
            return USER_ACTION_REQUIRED;
        }
        // It is safe to access mInstallerUid and mInstallSource without lock
        // because they are immutable after sealing.
        final boolean isInstallPermissionGranted =
                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
        final boolean isSelfUpdatePermissionGranted =
                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
        final boolean isUpdatePermissionGranted =
                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
        final boolean isUpdateWithoutUserActionPermissionGranted = (mPm.checkUidPermission(
                android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid)
                == PackageManager.PERMISSION_GRANTED);
        final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
        final boolean isUpdate = targetPackageUid != -1 || isApexSession();
        final InstallSourceInfo existingInstallSourceInfo = isUpdate
                ? mPm.getInstallSourceInfo(packageName)
                : null;
        final String existingInstallerPackageName = existingInstallSourceInfo != null
                ? existingInstallSourceInfo.getInstallingPackageName()
                : null;
        final boolean isInstallerOfRecord = isUpdate
                && Objects.equals(existingInstallerPackageName, getInstallerPackageName());
        final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
        final boolean isPermissionGranted = isInstallPermissionGranted
                || (isUpdatePermissionGranted && isUpdate)
                || (isSelfUpdatePermissionGranted && isSelfUpdate);
        final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
        final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);

        // Device owners and affiliated profile owners  are allowed to silently install packages, so
        // the permission check is waived if the installer is the device owner.
        final boolean noUserActionNecessary = isPermissionGranted || isInstallerRoot
                || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner();

        if (noUserActionNecessary) {
            return USER_ACTION_NOT_NEEDED;
        }

        if (mPm.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, userId)) {
            // show the installer to account for device poslicy or unknown sources use cases
            return USER_ACTION_REQUIRED;
        }

        if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED
                && isUpdateWithoutUserActionPermissionGranted
                && (isInstallerOfRecord || isSelfUpdate)) {
            return USER_ACTION_PENDING_APK_PARSING;
        }

        return USER_ACTION_REQUIRED;
    }
 private PackageManagerService.VerificationParams prepareForVerification()
            throws PackageManagerException {
        assertNotLocked("makeSessionActive");

        @UserActionRequirement
        int userActionRequirement = USER_ACTION_NOT_NEEDED;
        // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
        if (!params.isMultiPackage) {
            userActionRequirement = computeUserActionRequirement();
            if (userActionRequirement == USER_ACTION_REQUIRED) {
                sendPendingUserActionIntent();
                return null;
            } // else, we'll wait until we parse to determine if we need to
        }

        boolean silentUpdatePolicyEnforceable = false;
        synchronized (mLock) {
            if (mRelinquished) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session relinquished");
            }
            if (mDestroyed) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session destroyed");
            }
            if (!mSealed) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session not sealed");
            }
            PackageLite result = parseApkLite();
            if (result != null) {
                mPackageLite = result;
                synchronized (mProgressLock) {
                    mInternalProgress = 0.5f;
                    computeProgressLocked(true);
                }

                extractNativeLibraries(
                        mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());

                    1 安裝程序聲明了 UPDATE_PACKAGES_WITHOUT_USER_ACTION 權限。
                    2  正在安裝的應用程序的目標是 API 級別 29(Android 10)或更高

                if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
                    if (result.getTargetSdk() < Build.VERSION_CODES.Q) {
                        sendPendingUserActionIntent();
                        return null;
                    }

                    3 安裝器選擇了新的行為 不需要操作的行為

                    if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
                        silentUpdatePolicyEnforceable = true;
                    }
                }
            }
        }
        if (silentUpdatePolicyEnforceable) {
            if (!mSilentUpdatePolicy.isSilentUpdateAllowed(
                    getInstallerPackageName(), getPackageName())) {
                // Fall back to the non-silent update if a repeated installation is invoked within
                // the throttle time.
                sendPendingUserActionIntent();
                return null;
            }
            記錄靜默更新程序
            mSilentUpdatePolicy.track(getInstallerPackageName(), getPackageName());
        }
        synchronized (mLock) {
            return makeVerificationParamsLocked();
        }
    }
 private PackageManagerService.VerificationParams makeVerificationParamsLocked() {
        final IPackageInstallObserver2 localObserver;
        if (!hasParentSessionId()) {
            // Avoid attaching this observer to child session since they won't use it.
            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) {
                    if (returnCode == INSTALL_SUCCEEDED) {
                        onVerificationComplete();
                    } else {
                        onSessionVerificationFailure(returnCode, msg);
                    }
                }
            };
        } else {
            localObserver = null;
        }

  private void onVerificationComplete() {
        // Staged sessions will be installed later during boot
        if (isStaged()) {
            // TODO(b/136257624): Remove this once all verification logic has been transferred out
            //  of StagingManager.
            mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
            // TODO(b/136257624): We also need to destroy internals for verified staged session,
            //  otherwise file descriptors are never closed for verified staged session until reboot
            return;
        }

        install();
    }

直接執行安裝

https://new.qq.com/omn/20210520/20210520A0689300.html

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容