PackageManagerService之應(yīng)用安裝流程分析

PMS是Android系統(tǒng)服務(wù)中的包管理機(jī)制,服務(wù)于APK的完整生命周期,主要包括APK的解析,安裝,運(yùn)行,卸載。

PMS整個(gè)生命周期比較龐大,可以先從安裝和卸載入手,看PMS是如何工作。

APK的安裝

下面這段代碼估計(jì)大家都很熟悉,就是用做在應(yīng)用中做自更新或者安裝其他應(yīng)用使用

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  Uri apkUri = FileProvider.getUriForFile(context, UIUtils.getContext().getPackageName(), file);
  intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
  intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} else {
  intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}

InstallStart

application/vnd.android.package-archive
上面這段主要做隱試匹配,會(huì)匹配到 com.android.packageInstaller進(jìn)程中InstallStart(InstallStart是一個(gè)Activity)

<activity android:name=".InstallStart"
                android:theme="@android:style/Theme.Translucent.NoTitleBar"
                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="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="package" />
                <data android:scheme="content" />
            </intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.content.pm.action.CONFIRM_INSTALL" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
</activity>

InstallStart其實(shí)也沒(méi)干什么正事,一般來(lái)說(shuō)就三點(diǎn)

  1. 對(duì)TargetSDKVersion進(jìn)行校驗(yàn)
  2. 對(duì)Manifest.permission.REQUEST_INSTALL_PACKAGES進(jìn)行校驗(yàn)
  3. 對(duì)傳遞進(jìn)來(lái)的uri進(jìn)行校驗(yàn),如果以Schema以Content開(kāi)頭,就跳轉(zhuǎn)到InstallStaging中。
    斷點(diǎn)看一下吧
packageUri

我們傳遞進(jìn)來(lái)uri的值

content://com.android.externalstorage.documents/document/primary%3Aapp-release.apk

InstallStaging

先來(lái)看看這個(gè)Activity長(zhǎng)什么樣子


image

InstallStaging中主要邏輯在onResume()方法中開(kāi)啟異步任務(wù)

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

    // This is the first onResume in a single life of the activity
    if (mStagingTask == null) {
        // File does not exist, or became invalid
        if (mStagedFile == null) {
            // Create file delayed to be able to show error
            try {
                mStagedFile = TemporaryFileManager.getStagedFile(this);
            } catch (IOException e) {
                showError();
                return;
            }
        }

        mStagingTask = new StagingAsyncTask();
        mStagingTask.execute(getIntent().getData());
    }
}

具體看下StagingAsyncTask的任務(wù)

private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Uri... params) {
            if (params == null || params.length <= 0) {
                return false;
            }
            Uri packageUri = params[0];
            // 這里的packageUri 是  content://com.android.externalstorage.documents/document/primary%3Aapp-release.apk
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
                // Despite the comments in ContentResolver#openInputStream the returned stream can
                // be null.
                if (in == null) {
                    return false;
                }

                try (OutputStream out = new FileOutputStream(mStagedFile)) {
                    byte[] buffer = new byte[1024 * 1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        // Be nice and respond to a cancellation
                        if (isCancelled()) {
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException | IllegalStateException e) {
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean success) {
            if (success) {
                // Now start the installation again from a file
                Intent installIntent = new Intent(getIntent());
                installIntent.setClass(InstallStaging.this,    DeleteStagedFileOnResult.class);
              // 這里需要注意,已經(jīng)將content開(kāi)頭的地址轉(zhuǎn)換成了file開(kāi)頭的文件地址
                installIntent.setData(Uri.fromFile(mStagedFile));

                if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
                    installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                }

                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                startActivity(installIntent);

                InstallStaging.this.finish();
            } else {
                // 這里如果出錯(cuò)的話(huà)會(huì)出現(xiàn)一個(gè)比較熟悉的界面
                showError();
            }
        }
    }

其實(shí)就是將APK文件拷貝一份,具體拷貝到哪里呢

拷貝到這里 /data/user/com.android.packageInstaller/no_backup/

拷貝完了之后跳轉(zhuǎn)到DeleteStagedFileOnResult

ps 這里如果拷貝錯(cuò)誤的話(huà)會(huì)出現(xiàn)一個(gè)比較熟悉界面

image0

DeleteStagedFileOnResult

這個(gè)類(lèi)貌似是android 10新增的,在android9還沒(méi)看到這個(gè)類(lèi),內(nèi)容比較簡(jiǎn)單

public class DeleteStagedFileOnResult extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
            Intent installIntent = new Intent(getIntent());
            // 跳轉(zhuǎn)到PackageInstallerActivity,這個(gè)頁(yè)面很熟悉了
            installIntent.setClass(this, PackageInstallerActivity.class);

            installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
            startActivityForResult(installIntent, 0);
        }
    }

  // 這里是重點(diǎn),主要是用來(lái)接收安裝的成功或失敗,不管咋樣,都把剛復(fù)制的文件干掉
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        File sourceFile = new File(getIntent().getData().getPath());
        sourceFile.delete();

        setResult(resultCode, data);
        finish();
    }
}

PackageInstallerActivity

還是先看看這個(gè)頁(yè)面長(zhǎng)什么樣子

image1

PackageInstallerActivity中邏輯比較多

先看看在onCreate方法中初始化的主要變量

// 這個(gè)是向應(yīng)用的進(jìn)程提供功能,但是功能最終都有PMS提供,當(dāng)前的進(jìn)程還是處在com.android.packageInstaller中        
mPm = getPackageManager();

// AIDL接口,用于和PMS進(jìn)行通信
mIpm = AppGlobals.getPackageManager();

// 動(dòng)態(tài)權(quán)限檢測(cè)
mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);

// 提供安裝,卸載的功能
mInstaller = mPm.getPackageInstaller();

// 誰(shuí)喚起了這次安裝,如果是從應(yīng)用安裝,那么就是應(yīng)用相關(guān)的信息,如果是從文件管理器點(diǎn)擊安裝,那么都是空的
mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                                     PackageInstaller.SessionParams.UID_UNKNOWN);

看一下傳過(guò)來(lái)的apk文件地址


image3

file:///data/user_de/0/com.android.packageinstaller/no_backup/package5541502234249167159.apk

得到地址后,對(duì)APK文件開(kāi)始解析,具體實(shí)現(xiàn)在processPackageUri方法中

ApkAssets apkAssets = ApkAssets.loadFromPath("/data/user_de/0/com.android.packageinstaller/no_backup/package5541502234249167159.apk")

XmlResourceParser parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);

構(gòu)造資源路徑

 parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
// 這里的assets資源路徑總共有兩個(gè)
// ApkAssets{path=/system/framework/framework-res.apk},應(yīng)用中有很多使用系統(tǒng)資源
// ApkAssets{path=/data/user_de/0/com.android.packageinstaller/no_backup/package4780013443826073855.apk}
 final Resources res = new Resources(assets, mMetrics, null);

最后看下解析的結(jié)果

activitys  // 數(shù)量和類(lèi)名
applicationInfo 
baseCodePath
mCompileSdkVersion
mVersionName
mVersionCode
manifestPackageName
packageName
permissionGroups
permissions
use32bitAbi = false
codePath = /data/user_de/0/com.android.packageinstaller/no_backup/package4780013443826073855.apk
providers
receivers
.....

基本上得到在清單文件注冊(cè)的所有信息

點(diǎn)擊繼續(xù)之后會(huì)出現(xiàn)確認(rèn)安裝界面


image4

在看看點(diǎn)擊安裝之后的邏輯

private void startInstall() {
        // Start subactivity to actually install the application
        Intent newIntent = new Intent();
        // 應(yīng)用的ApplicationInfo
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                mPkgInfo.applicationInfo);
        // mPackageURI = file:///data/user_de/0/com.android.packageinstaller/no_backup/package479667155487808554.apk
        newIntent.setData(mPackageURI);
       
       // 跳轉(zhuǎn)到InstallInstalling中
        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();
    }

邏輯比較簡(jiǎn)單,就是將解析出來(lái)的ApplicationInfo和之前傳遞過(guò)來(lái)的PackageURI繼續(xù)傳遞到InstallInstalling中.

這里應(yīng)該會(huì)跟現(xiàn)在網(wǎng)上已經(jīng)有的描述不一樣,android 10在應(yīng)用安裝過(guò)程不會(huì)展示需要系統(tǒng)權(quán)限

InstallInstalling

這里差不多是在應(yīng)用安裝在com.android.packageinstaller進(jìn)程的最后一步。
首先注冊(cè)了注冊(cè)一個(gè)接受應(yīng)用安裝成功或者失敗的廣播

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

這里需要重點(diǎn)看一下這個(gè)廣播

 @NonNull private static EventResultPersister getReceiver(@NonNull Context context) {
        synchronized (sLock) {
            if (sReceiver == null) {
                sReceiver = new EventResultPersister(
                // 這里返回了一個(gè)File 具體路徑是
                   // /data/user_de/0/com.android.packageinstaller/no_backup/install_results.xml
// install_results.xml中記錄的每次安裝的installId,這次installId每次安裝的時(shí)候遞增
                        TemporaryFileManager.getInstallStateFile(context));
            }
        }
        return sReceiver;
    }

我們安裝應(yīng)用的時(shí)候通常會(huì)有進(jìn)度呢,到現(xiàn)在還沒(méi)看到進(jìn)度的回調(diào)呢?

進(jìn)度的回調(diào)肯定是PMS回調(diào)給InstallInstalling來(lái)展示的,接著往下看,果然在后面建立了于PMS通信

try {
  // 這種SessionID的系統(tǒng)應(yīng)用和SystemServer的服務(wù)中很常見(jiàn),因?yàn)橄到y(tǒng)服務(wù)要服務(wù)于所有應(yīng)用,所以每個(gè)應(yīng)用調(diào)用系統(tǒng)的服務(wù)的時(shí)候,在每個(gè)系統(tǒng)服務(wù)那里都會(huì)有一個(gè)sessionID的列表,標(biāo)識(shí)著與各種應(yīng)用的連接
              mSessionId =  getPackageManager().getPackageInstaller().createSession(params);
    } catch (IOException e) {
      launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
    }
mSessionCallback = new InstallSessionCallback();


// InstallSessionCallback這里就是PMS的進(jìn)度回調(diào)
@Override
public void onProgressChanged(int sessionId, float progress) {
  if (sessionId == mSessionId) {
    ProgressBar progressBar = requireViewById(R.id.progress);
    progressBar.setMax(Integer.MAX_VALUE);
    progressBar.setProgress((int) (Integer.MAX_VALUE * progress));
  }
}

接著就是將APK傳遞給PMS,怎么傳遞呢,直接傳遞路徑?NO

PackageInstaller.Session session;
            try {
              // 通過(guò)上一步創(chuàng)建的sessionID,創(chuàng)建一個(gè)安裝的會(huì)話(huà)
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
                return null;
            }

            session.setStagingProgress(0);

            try {
                File file = new File(mPackageURI.getPath());

                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    // 將字節(jié)流寫(xiě)入PMS中,具體寫(xiě)到什么位置了呢?下文有介紹
                    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();
                }
            }

APK傳輸給PMS之后下一步肯定是通知PMS我傳遞完了,你可以開(kāi)始安裝了,接著看下怎么通知了

Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());

// 還記得這個(gè)mInstallId之前在哪里創(chuàng)建的嗎,忘記了往前翻翻,這里將mInstallId也傳遞給了PMS,PMS拿到這個(gè)mInstallID,在應(yīng)用安裝完成后就可以發(fā)送一個(gè)廣播,在把這個(gè)mInstallID回寫(xiě),packageinstaller進(jìn)程就可以單獨(dú)去更新這個(gè)被安裝應(yīng)用的狀態(tài)
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

// PendingIntent 延時(shí)意圖,滿(mǎn)足某個(gè)條件的時(shí)候才觸發(fā)
PendingIntent pendingIntent = PendingIntent.getBroadcast(
  InstallInstalling.this,
  mInstallId,
  broadcastIntent,
  PendingIntent.FLAG_UPDATE_CURRENT);

session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);

說(shuō)了半天其實(shí)和PMS還沒(méi)有半毛錢(qián)關(guān)系,只是說(shuō)安裝前的準(zhǔn)備工作,收攏總結(jié)一下在com.android.packageinstaller中流程的工作

image5

PMS中的安裝流程

在介紹PMS之前,先介紹PNS

接著上面IPC調(diào)用開(kāi)始分析

session.commit(pendingIntent.getIntentSender());

對(duì)應(yīng)的遠(yuǎn)程調(diào)用在 PackageInstallerSession中的commit方法

 @Override
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        if (hasParentSessionId()) {
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + mParentSessionId +  " and may not be committed directly.");
        }
        if (!markAsCommitted(statusReceiver, forTransfer)) {
            return;
        }
        ... // 省略部分代碼
        ...
       // 發(fā)送了一個(gè)MSG_COMMIT
        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    }

這個(gè)Handler通過(guò)Callback的方式處理消息,在SystemServer進(jìn)程斷點(diǎn)看一下


image06

handleCommit方法中終于調(diào)用PMS中的installStage方法


// 安裝信息都在ActiveInstallSession中,ActiveInstallSession具體有哪些值呢
// mInstallPackageName = com.android.packageinstaller
// mInstallUid = 10048
// mPackageName = com.android.sourcecodedebug
// mStageDir = /data/app/vmdl1397850738.tmp
// .....

// 之前說(shuō)過(guò)跨進(jìn)程將APK寫(xiě)入到PMS中,其實(shí)就是寫(xiě)入到 /data/app/vmdl1397850738.tmp,它是個(gè)文件夾,里面有兩個(gè)文件,后面的數(shù)字是隨機(jī)生成的,不同的應(yīng)用不一樣
// /data/app/vmdl1397850738.tmp/lib   估計(jì)能猜出有啥作用
// /data/app/vmdl1397850738.tmp/base.apk
void installStage(ActiveInstallSession activeInstallSession) {
   
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final InstallParams params = new InstallParams(activeInstallSession);
        msg.obj = params;
       // 這個(gè)mHandler是PMS中的核心,下文會(huì)細(xì)說(shuō)
       // 發(fā)送了一個(gè)INIT_COPY消息
        mHandler.sendMessage(msg);
    }

PackageHandler是處理PMS中的所有邏輯的核心,應(yīng)用的安裝,卸載都會(huì)觸發(fā)到這個(gè)Handler,消息的種類(lèi)太多,先接著上文的INIC_COPY消息來(lái)看

// 看著好像比較簡(jiǎn)單,直接調(diào)用了InstallParams的startCopy方法,看名字像是做了一次拷貝,其實(shí)不完全是
case INIT_COPY: {
  HandlerParams params = (HandlerParams) msg.obj;
  if (params != null) {   
    params.startCopy();                  
  }
  break;
}

InstallParams.startCopy中就兩個(gè)方法調(diào)用

  1. handleStartCopy
  2. handleReturnCode

先看handleStartCopy

         /*
         *  
         * 方法很長(zhǎng),省略了部分代碼,關(guān)鍵其實(shí)就做了兩件事
           1 輕量解析 /data/app/vmdl1397850738.tmp/base.apk文件,將各種信息包裝成PackageInfoLite
           2 確定應(yīng)用的安裝位置,三方應(yīng)用,系統(tǒng)應(yīng)用,InstantApp都有對(duì)應(yīng)的規(guī)則
           * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal                    storage, 等于1
           * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external                 media,   等于2
           * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage                 errors,  等于-1
         */
        public void handleStartCopy() {
            int ret = PackageManager.INSTALL_SUCCEEDED;

            ....
            ....
              
              
            PackageInfoLite pkgLite = null;
            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                    origin.resolvedPath, installFlags, packageAbiOverride);

          
            /*
             * 判斷空間夠不夠,不光是APK文件,還有從APK解壓釋放的so文件
             * 
             */
            if (!origin.staged && pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                // TODO: focus freeing disk space on the target device
                final StorageManager storage = StorageManager.from(mContext);
                final long lowThreshold = storage.getStorageLowBytes(
                        Environment.getDataDirectory());

                final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
                        origin.resolvedPath, packageAbiOverride);
                if (sizeBytes >= 0) {
                    try {
                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
                        pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                                origin.resolvedPath, installFlags, packageAbiOverride);
                    } catch (InstallerException e) {
                        Slog.w(TAG, "Failed to free cache", e);
                    }
                }
            }
            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                // 解析完就可以知道應(yīng)用的類(lèi)型,獲取PMS推薦的安裝目錄,一般都是1 內(nèi)置存儲(chǔ)
                int loc = pkgLite.recommendedInstallLocation;
                .....
                .....
            }      
            mRet = ret;
        }

接著看handlerReturnCode

@Override
void handleReturnCode() {
  if (mVerificationCompleted && mEnableRollbackCompleted) {
    .....
      .....
      if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        // copyAPK并沒(méi)有開(kāi)始復(fù)制,
        mRet = mArgs.copyApk();
      }
    processPendingInstall(mArgs, mRet);
  }
}

copyApk()并沒(méi)有真正的復(fù)制,只是將FileInstallArgs對(duì)象中的兩個(gè)成員變量復(fù)制,兩個(gè)變量值都是一樣的

/data/app/vmdl424643763.tmp

image07

最終的實(shí)現(xiàn)在processPendingInstall方法中,通過(guò)PackageHandler post一個(gè)Runnable

 private void processInstallRequestsAsync(boolean success,
            List<InstallRequest> installRequests) {
        mHandler.post(() -> {
            if (success) {
                for (InstallRequest request : installRequests) {
                    // 對(duì)安裝目錄做一下清理
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                synchronized (mInstallLock) {
                    // 重點(diǎn),安裝過(guò)程中最核心的方法
                    installPackagesTracedLI(installRequests);
                }
              
                for (InstallRequest request : installRequests) {
                    // 安裝完成之后的工作
                    request.args.doPostInstall(
                            request.installResult.returnCode, request.installResult.uid);
                }
            }
      
        });
    }

installPackagesTracedLi方法代碼比較多,主要執(zhí)行了下面四個(gè)邏輯
1 全量解析APK文件
2 校驗(yàn)APK簽名,如果之前有安裝過(guò)會(huì)和之前的APK簽名做比對(duì)
3 釋放apk文件中的so庫(kù),釋放到/data/app/vmdl1397850738.tmp/lib**
4 對(duì)目錄和文件充新命名,將/data/app/vmdl903343092.tmp重新命名為/data/app/包名/,/data/app/vmdl1397850738.tmp/base.apk重新命名為/data/app/包名/base.apk**

5 重新命名后的地址賦值給PackageParser.Package
6 更新mPackages和mSettings中的應(yīng)用信息


最后一個(gè)目錄比較熟悉吧!

這里描述的安裝流程和現(xiàn)有的很多不一致的地方,主要是android 10的源代碼在PMS安裝這一塊改動(dòng)挺大,摒棄了PackageHandler中的MCS_BOUND消息。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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