国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

pms包管理服務分析-外部安裝流程(adb/packageInstaller)

2019-11-09 17:27:39
字體:
來源:轉載
供稿:網友

系統開機過程中掃描并安裝apk的過程是針對已有的apk文件。針對外部apk的安裝,pms提供了另外的接口。我們一般也就通過兩種方式去安裝外部apk,一種是通過adb的install命令安裝外部應用。另外一種是通過系統應用PackageInstaller,通過界面引導的方式安裝外部應用。下面分別來分析這兩種安裝方式的過程和相同的地方。

通過adb install命令安裝外部應用

adb install命令實際上調用的是/system/bin目錄下的pm可執行程序來實現安裝應用的功能。

下面來分析下pm的實現源碼

[/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java]

public static void main(String[] args) {	   int exitCode = 1;	   try {		exitCode = new Pm().run(args);	   } catch (Exception e) {			   }	   System.exit(exitCode);}     	

Pm程序的入口main函數中,創建一個Pm的實例并執行run方法,傳入args參數(含apk路徑等信息)。繼續分析run方法:

    public int run(String[] args) throws IOException, RemoteException {        boolean validCommand = false;        if (args.length < 1) {            return showUsage();        }        mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));        mPm = ipackageManager.Stub.asInterface(ServiceManager.getService("package"));		...        String op = args[0];        if ("install".equals(op)) {            return runInstall();        }		...	}

run方法中首先讀取第一個參數args[0],第一個參數是op(Operation),表示命令的操作是什么,這里我們來看op=”install”的情況,直接調用runInstall方法,下面來看看runInstall方法的實現:

PRivate int runInstall() {        int installFlags = 0;        int userId = UserHandle.USER_ALL;        String installerPackageName = null;		...        try {            VerificationParams verificationParams = new VerificationParams(verificationURI,                    originatingURI, referrerURI, VerificationParams.NO_UID, null);            mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,                    installerPackageName, verificationParams, abi, userId);			...        } catch (RemoteException e) {            System.err.println(e.toString());            System.err.println(PM_NOT_RUNNING_ERR);            return 1;        }

可以看到runInstall方法最終通過binder和pms通信,執行pms的installPackageAsUser方法,分別傳入apk的文件路徑,abi信息等。pms的installPackageAsUser方法在接下來會分析。

通過PackageInstaller應用安裝外部應用

查看PackageInstaller安裝界面代碼:

[/package/app/PackageInstaller/src/com/android/packageinstaller /InstallAppProgress.java]

    public void initView() {        setContentView(R.layout.op_progress);        int installFlags = 0;        PackageManager pm = getPackageManager();        ...        pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,                    installerPackageName, verificationParams, null);        ...    }

在PackageInstaller安裝界面初始化的時候就直接調用PackageManager提供的installPackageWithVerificationAndEncryption方法執行安裝操作。下面來分析下installPackageWithVerificationAndEncryption的具體實現:

[/frameworks/base/core/java/android/app/applicationPackageManager.java]

    public void installPackageWithVerificationAndEncryption(Uri packageURI,            PackageInstallObserver observer, int flags, String installerPackageName,            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {     installCommon(packageURI, observer, flags, installerPackageName, verificationParams,encryptionParams);    }

繼續分析installCommon方法:

    private void installCommon(Uri packageURI,            PackageInstallObserver observer, int flags, String installerPackageName,            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {        if (!"file".equals(packageURI.getScheme())) {            throw new UnsupportedOperationException("Only file:// URIs are supported");        }        final String originPath = packageURI.getPath();        try {            mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,                    verificationParams, null);        } catch (RemoteException ignored) {        }    }

installCommon方法首先判斷傳入的Uri是否屬于文件類型,如果不是,直接異常返回。如果是,則通過Uri獲得apk文件路徑,最后調用pms的installPackage方法(實際上同樣是調用installPackageAsUser)執行具體的安裝操作。

installPackageAsUser方法分析

上面兩種主要的外部安裝路徑最后都指向了pms的installPakcageAsUser方法。這個方法提供外部安裝功能,我們從應用安裝的第一個階段(復制文件)開始:

[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]

    public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,            int installFlags, String installerPackageName, VerificationParams verificationParams,            String packageAbiOverride, int userId) {        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);

第一步先檢查調用端是否具有INSTALL_PACKAGES權限,如果沒有該權限則拋出權限異常并終止運行。只有系統平臺簽名或者系統應用才會授予這個權限,這也是第三方應用無法實現靜默安裝的原因。

        final int callingUid = Binder.getCallingUid();	    if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {            installFlags |= PackageManager.INSTALL_FROM_ADB;        } else {            // Caller holds INSTALL_PACKAGES permission, so we're less strict            // about installerPackageName.            installFlags &= ~PackageManager.INSTALL_FROM_ADB;            installFlags &= ~PackageManager.INSTALL_ALL_USERS;        }

第二步檢查callingUid是否等于SHELL或者ROOT,如果是表示通過adb shell命令發起的安裝動作,給installFlags添加對應INSTALL_FROM_ADB標志,否則去掉。這個標志后面會用到。

	verificationParams.setInstallerUid(callingUid);        final File originFile = new File(originPath);        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);        final Message msg = mHandler.obtainMessage(INIT_COPY);        msg.obj = new InstallParams(origin, observer, installFlags,                installerPackageName, verificationParams, user, packageAbiOverride);        mHandler.sendMessage(msg);}

最后創建一個what是INIT_COPY的Message,把調用的參數保存在InstallParams對象中并發送到mHandler的消息隊列中。

下面分析下INIT_COPY類型消息的處理:

    class PackageHandler extends Handler {        private boolean mBound = false;        final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>();        void doHandleMessage(Message msg) {            switch (msg.what) {                case INIT_COPY: {                    HandlerParams params = (HandlerParams) msg.obj;                    int idx = mPendingInstalls.size();                    if (!mBound) {                        if (!connectToService()) {                            params.serviceError();                            return;                        } else {                            mPendingInstalls.add(idx, params);                        }                    } else {                        mPendingInstalls.add(idx, params);                        // Already bound to the service. Just make                        // sure we trigger off processing the first request.                        if (idx == 0) {                            mHandler.sendEmptyMessage(MCS_BOUND);                        }                    }                    break;                }

在INIT_COPY消息的處理中將綁定DefaultContainerService,因為這是一個異步的過程,要等待綁定的結果通過onServiceConnected()返回,所以,這里將安裝的參數信息放到mPendingInstalls列表中。如果這個service以前就綁定好了,現在不在需要再綁定,安裝信息也會先放到mPendingInstalls中。如果有多個安裝請求同時到達,通過mPendingInstalls列表可以對它們進行排隊。如果列表中只有一項,說明沒有更多的安裝請求,此時會立即發送MCS_BOUND消息,進入下一步的處理。而onServiceConnected()方法的處理同樣是發送MCS_BOUND消息,onServiceConnected方法如下:

    class DefaultContainerConnection implements ServiceConnection {        public void onServiceConnected(ComponentName name, IBinder service) {            IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);            mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));        }        public void onServiceDisconnected(ComponentName name) {        }    };

一旦DefaultContainerService(該服務用于外部存儲卡應用安裝拷貝用)服務綁定成功后,通過mHandler發MCS_BOUND消息,下面來分析下這個消息的處理過程:

                case MCS_BOUND: {                    if (msg.obj != null) {                        mContainerService = (IMediaContainerService) msg.obj;                    }                    if (mPendingInstalls.size() > 0) {                        HandlerParams params = mPendingInstalls.get(0);                        if (params != null) {                            if (params.startCopy()) {                                // Delete pending install                                if (mPendingInstalls.size() > 0) {                                    mPendingInstalls.remove(0);                                }                                if (mPendingInstalls.size() == 0) {                                    if (mBound) {                                        removeMessages(MCS_UNBIND);                                        Message ubmsg = obtainMessage(MCS_UNBIND);                                        sendMessageDelayed(ubmsg, 10000);                                    }                                } else {                                    mHandler.sendEmptyMessage(MCS_BOUND);                                }                            }                        }                    }                }

MCS_BOUND消息的處理過程就是調用InstallParams類的startCopy()方法來執行安裝操作,一次安裝完成后在待安裝列表中刪除。只要mPendingInstalls中還有安裝信息,就會重復發送MCS_BOUND消息,直到所有的應用都安裝完畢,最后發送一個延時10秒的MCS_UNBIND消息。MCS_UNBIND消息不細分析。

安裝的開始在InstallParams類的startCopy方法,該方法定義在InstallParams的父類HandlerParams中:

    private abstract class HandlerParams {        private static final int MAX_RETRIES = 4;        …        final boolean startCopy() {            boolean res;            try {                               handleStartCopy();                    res = true;                            }            handleReturnCode();            return res;        }

父類的startCopy方法最后調用handleStartCopy方法,其中抽象的父類只聲明了handleStartCopy抽象方法,具體實現在子類InstallParams中:

    class InstallParams extends HandlerParams {        final OriginInfo origin;        final IPackageInstallObserver2 observer;        int installFlags;        final String installerPackageName;        final VerificationParams verificationParams;        private InstallArgs mArgs;        private int mRet;        final String packageAbiOverride;       public void handleStartCopy() throws RemoteException {            int ret = PackageManager.INSTALL_SUCCEEDED;	        …            final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;            PackageInfoLite pkgLite = null;                    …            final InstallArgs args = createInstallArgs(this);            if (ret == PackageManager.INSTALL_SUCCEEDED) {            …                    /*                     * No package verification is enabled, so immediately start                     * the remote call to initiate copy using temporary file.                     */                    ret = args.copyApk(mContainerService, true);                            }            mRet = ret;        }

如果忽略handleStartCopy中一堆繁雜的檢驗判斷,最終調用InstallArgs的copyApk方法,傳入ContainerService的binder實例。另外真正的copyApk方法在其子類FileInstallArgs的copyApk方法中實現:

    class FileInstallArgs extends InstallArgs {        private File codeFile;        private File resourceFile;        private File legacyNativeLibraryPath;       int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {            …            int ret = PackageManager.INSTALL_SUCCEEDED;            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);            if (ret != PackageManager.INSTALL_SUCCEEDED) {                Slog.e(TAG, "Failed to copy package");                return ret;            }            final File libraryRoot = new File(codeFile, LIB_DIR_NAME);            NativeLibraryHelper.Handle handle = null;            try {                handle = NativeLibraryHelper.Handle.create(codeFile);                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,                        abiOverride);            } catch (IOException e) {                Slog.e(TAG, "Copying native libraries failed", e);                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;            } finally {                IoUtils.closeQuietly(handle);            }            return ret;        }

發現copyApk方法同樣是調用DefaultContainerService的copyPackage方法將應用的文件復制到/data/app下。如果應用中還有native的動態庫,也會把包在apk文件中的動態庫的文件提取出來。執行完copyApk后,安裝的第一階段工作就完成了,應用安裝到了/data/app目錄下。

第一階段(復制apk文件)完成后,繼續完成第二階段的掃描安裝工作:

在前面的startCopy()方法中已經看到,最后它會調用handleReturnCode()方法,其實現在InstallParams類中:

        void handleReturnCode() {            if (mArgs != null) {                processPendingInstall(mArgs, mRet);            }        }

接下來分析processPendingInstall方法的實現:

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {        mHandler.post(new Runnable() {            public void run() {                mHandler.removeCallbacks(this);                 // Result object to be returned                PackageInstalledInfo res = new PackageInstalledInfo();                res.returnCode = currentStatus;                res.uid = -1;                res.pkg = null;                res.removedInfo = new PackageRemovedInfo();                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {                    args.doPreInstall(res.returnCode);                    synchronized (mInstallLock) {                        installPackageLI(args, res);                    }                    args.doPostInstall(res.returnCode, res.uid);                }        });    }

processPendingInstall方法中post了一個消息,這樣安裝過程將以異步的方式繼續執行。在post消息的處理中,首先調用installPackageLI來裝載應用,接下來的一大段代碼都是執行備份操作,備份是通過BackupManagerService來完成的。備份完成后,通過發送POST_INSTALL消息來繼續處理。現在來分析下apk的裝載過程:

   private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {        final int installFlags = args.installFlags;        String installerPackageName = args.installerPackageName;        File tmpPackageFile = new File(args.getCodePath());        boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);        boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);        boolean replace = false;        final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;        // Result object to be returned        res.returnCode = PackageManager.INSTALL_SUCCEEDED;        PackageParser pp = new PackageParser();        pp.setSeparateProcesses(mSeparateProcesses);        pp.setDisplayMetrics(mMetrics);        final PackageParser.Package pkg;        pkg = pp.parsePackage(tmpPackageFile, parseFlags);        // Mark that we have an install time CPU ABI override.        pkg.cpuAbiOverride = args.abiOverride;        String pkgName = res.name = pkg.packageName;          pp.collectCertificates(pkg, parseFlags);        pp.collectManifestDigest(pkg);        pp = null;        String oldCodePath = null;        boolean systemApp = false;        synchronized (mPackages) {            // Check if installing already existing package            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {                String oldName = mSettings.mRenamedPackages.get(pkgName);                if (pkg.mOriginalPackages != null  && pkg.mOriginalPackages.contains(oldName)                        && mPackages.containsKey(oldName)) {                    pkg.setPackageName(oldName);                    pkgName = pkg.packageName;                    replace = true;                } else if (mPackages.containsKey(pkgName)) {                    // This package, under its official name, already exists                    // on the device; we should replace it.                    replace = true;                }            }            PackageSetting ps = mSettings.mPackages.get(pkgName);                if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {                    try {                        verifySignaturesLP(ps, pkg);                    } catch (PackageManagerException e) {                        res.setError(e.error, e.getMessage());                        return;                    }                } else {                    if (!checkUpgradeKeySetLP(ps, pkg)) {                        return;                    }                }            int N = pkg.permissions.size();            for (int i = N-1; i >= 0; i--) {                PackageParser.Permission perm = pkg.permissions.get(i);                BasePermission bp = mSettings.mPermissions.get(perm.info.name);                if (bp != null) {                    final boolean sigsOk;                    if (!bp.sourcePackage.equals(pkg.packageName)                            || !(bp.packageSetting instanceof PackageSetting)                            || !bp.packageSetting.keySetData.isUsingUpgradeKeySets()                            || ((PackageSetting) bp.packageSetting).sharedUser != null) {                        sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,                                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;                    } else {                        sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);                    }                }            }        }        if (replace) {            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,                    installerPackageName, res);        } else {            installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,                    args.user, installerPackageName, res);        }        synchronized (mPackages) {            final PackageSetting ps = mSettings.mPackages.get(pkgName);            if (ps != null) {                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);            }        }    }

installPackageLI方法首先解析了安裝的應用文件,得到解析的結果后,主要是判斷新安裝的應用是否和系統中已安裝的應用構成升級關系,如果是,調用replacePackageLI方法繼續處理,否則調用installNewPackageLI方法處理。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 修水县| 平遥县| 启东市| 资阳市| 寿宁县| 西峡县| 龙州县| 龙泉市| 得荣县| 舒兰市| 沂南县| 龙陵县| 山阴县| 五河县| 香港| 兰溪市| 安仁县| 宜良县| 应城市| 常山县| 湘潭县| 紫云| 玉溪市| 包头市| 富宁县| 玉门市| 南阳市| 宜州市| 阳山县| 茶陵县| 尉氏县| 金乡县| 花莲县| 佛教| 黔西| 广丰县| 乐昌市| 曲麻莱县| 土默特右旗| 博白县| 虞城县|