PackageInstaller源码分析(一)

本文详细分析了Android PackageInstaller的源码,探讨了Android权限在应用安装过程中的作用。通过追踪源码,从PackageInstallerActivity开始,经过Intent处理、文件解析、权限提取,到最终的权限验证和安装流程,揭示了用户同意安装后App如何获取权限的全过程。重点关注了证书校验、权限处理和文件拷贝等关键步骤。

  本篇博客分析PackageInstaller源码目的是分析Android权限机制,Android App的权限在应用被安装时,用户选择授予或者拒绝。所以,分析Android权限机制源码的第一步分析应用程序安装时的行为。
  此次阅读源码旨在解决的问题:Android权限是一次性授予的,即用户在同意安装后,App就获得了申请的权限。那这个过程是怎样的,即:用户点击同意——>App获得权限,经理了怎样的调用过程。

一、源码分析追踪过程

  Android源码中,负责应用程序安装的是PackageInstaller.apk。其源码路径/packages/apps/PackageInstaller。从Manifest文件可以看到,PackageInstallersActivity是其入口Activity,也是主要负责安装apk的Activity。所以首先分析PackageInstallersActivity。
  这次分析过程是基于源码调试的,便于追踪代码执行流程。首先利用一个Intent进行测试,am start -n com.android.packageinstaller/com.android.packageinstaller.PackageInstallerActivity -d file:///data/local/tmp/qqmusic.apk,是从手机中的文件来安装apk。

<activity android:name=".PackageInstallerActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:excludeFromRecents="true">
    <intent-filter>
        <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="file"/>
        <data android:mimeType="application/vnd.android.package-archive"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.INSTALL_PACKAGE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="file"/>
        <data android:scheme="package"/>
        </intent-filter>
    <intent-filter>
        <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

  以下就是源码分析过程。
  进入onCreate()函数后,首先从Intent获取相关数据,然后判断用户是否允许安装非官方应用。

protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        ``````
        final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
        final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();

        ```````
    }

  接着的一段代码也是关于从第三方应用市场(含本地)安装的一些安全设定工作。

protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        ``````

        final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
        final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();

        boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
        mInstallFlowAnalytics = new InstallFlowAnalytics();
        mInstallFlowAnalytics.setContext(this);
        mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
        mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(unknownSourcesAllowedByAdmin
                && unknownSourcesAllowedByUser);
        mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
        mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
        mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
        mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());

        ```````

}

  接着,从data中的取出scheme,并根据是都是file,package,和没有data进入不同的分支,测试程序是利用file安装程序,所以进入的是else分支。

protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        `````````

        if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
            Log.w(TAG, "Unsupported scheme " + scheme);
            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
            finish();
            return;
        }

        final PackageUtil.AppSnippet as;
        if ("package".equals(mPackageURI.getScheme())) {
            mInstallFlowAnalytics.setFileUri(false);
            try {
                mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
                        PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
            } catch (NameNotFoundException e) {
            }
            if (mPkgInfo == null) {
                Log.w(TAG, "Requested package " + mPackageURI.getScheme()
                        + " not available. Discontinuing installation");
                showDialogInner(DLG_PACKAGE_ERROR);
                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                mInstallFlowAnalytics.setPackageInfoObtained();
                mInstallFlowAnalytics.setFlowFinished(
                        InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);
                return;
            }
            as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
                    mPm.getApplicationIcon(mPkgInfo.applicationInfo));
        } else {
            mInstallFlowAnalytics.setFileUri(true);
            final File sourceFile = new File(mPackageURI.getPath());
            PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);

            // Check for parse errors
            if (parsed == null) {
                Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
                showDialogInner(DLG_PACKAGE_ERROR);
                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                mInstallFlowAnalytics.setPackageInfoObtained();
                mInstallFlowAnalytics.setFlowFinished(
                        InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);
                return;
            }
            mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                    PackageManager.GET_PERMISSIONS, 0, 0, null,
                    new PackageUserState());
            mPkgDigest = parsed.manifestDigest;
            as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
        }
        mInstallFlowAnalytics.setPackageInfoObtained();

        ```````
}

  在else分支里面,判断能否正确解析apk文件,变量parsed是Package实例,包含了解析apk的结果,解析过程的关键API是(这个解析过程的API值得具体分析):

 PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);

  Package包含了解析apk文件的结果,具体可以查看Package类文件,(在单步调试这一步时候需要等待很长时间)。这个API基本对apk文件的所有重要信息都做了分析,其实应该就是分析manifest文件,包括apk中的Activity,service等四大组件,以及自定义权限和申请的权限。
<img src="Package_class.jpg" width=1000></img>

===============
  else分支后,设置程序apk以获取,这个API不重要。接着就是设置Activity界面,注意下面的最后一行代码:

protected void onCreate(Bundle icicle) {

        ````````
        setContentView(R.layout.install_start);
        mInstallConfirm = findViewById(R.id.install_confirm_panel);
        mInstallConfirm.setVisibility(View.INVISIBLE);
        PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);

        mOriginatingUid = getOriginatingUid(intent);

        ```````

          final boolean isManagedProfile = mUserManager.isManagedProfile();
        if (!unknownSourcesAllowedByAdmin
                || (!unknownSourcesAllowedByUser && isManagedProfile)) {
            showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
        } else if (!unknownSourcesAllowedByUser) {
            // Ask user to enable setting first
            showDialogInner(DLG_UNKNOWN_SOURCES);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
        } else {
            initiateInstall();
        }
}

  最后进入initiateInstall()函数。

private void initiateInstall() {
        String pkgName = mPkgInfo.packageName;
        // Check if there is already a package on the device with this name
        // but it has been renamed to something else.
        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;
        }
        // Check if package is already installed. display confirmation dialog if replacing pkg
        try {
            // This is a little convoluted because we want to get all uninstalled
            // apps, but this may include apps with just data, and if it is just
            // data we still want to count it as "installed".
            mAppInfo = mPm.getApplicationInfo(pkgName,
                    PackageManager.GET_UNINSTALLED_PACKAGES);
            if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
                mAppInfo = null;
            }
        } catch (NameNotFoundException e) {
            mAppInfo = null;
        }

        mInstallFlowAnalytics.setReplace(mAppInfo != null);
        mInstallFlowAnalytics.setSystemApp(
                (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));

        startInstallConfirm();
    }

  这个函数内部就是对应用的包名做一些处理,以及判断是否是升级,是否是系统应用等,这些和权限机制基本没有关系。判断完毕之后,进入startInstallConfirm()函数。

private void startInstallConfirm() {
        TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
        tabHost.setup();
        ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
        TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
        adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
            @Override
            public void onTabChanged(String tabId) {
                if (TAB_ID_ALL.equals(tabId)) {
                    mInstallFlowAnalytics.setAllPermissionsDisplayed(true);
                } else if (TAB_ID_NEW.equals(tabId)) {
                    mInstallFlowAnalytics.setNewPermissionsDisplayed(true);
                }
            }
        });

        boolean permVisible = false;
        mScrollView = null;
        mOkCanInstall = false;
        int msg = 0;
        if (mPkgInfo != null) {
            AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
            final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
            final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
            if (mAppInfo != null) {
                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 =
                        (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
                mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
                if (newPermissionsFound) {
                    permVisible = true;
                    mScrollView.addView(perms.getPermissionsView(
                            AppSecurityPermissions.WHICH_NEW));
                } else {
                    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);
            } else  {
                findViewById(R.id.tabscontainer).setVisibility(View.GONE);
                findViewById(R.id.divider).setVisibility(View.VISIBLE);
            }
            if (NP > 0 || ND > 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);
                }
                if (NP > 0) {
                    ((ViewGroup)root.findViewById(R.id.privacylist)).addView(
                            perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
                } else {
                    root.findViewById(R.id.privacylist).setVisibility(View.GONE);
                }
                if (ND > 0) {
                    ((ViewGroup)root.findViewById(R.id.devicelist)).addView(
                            perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
                } else {
                    root.findViewById(R.id.devicelist).setVisibility(View.GONE);
                }
                adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
                        getText(R.string.allPerms)), root);
            }
        }
        mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
        if (!permVisible) {
            if (mAppInfo != null) {
                // This is an update to an application, but there are no
                // permissions at all.
                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 {
                // This is a new application with no permissions.
                msg = R.string.install_confirm_question_no_perms;
            }
            tabHost.setVisibility(View.GONE);
            mInstallFlowAnalytics.setAllPermissionsDisplayed(false);
            mInstallFlowAnalytics.setNewPermissionsDisplayed(false);
            findViewById(R.id.filler).setVisibility(View.VISIBLE);
            findViewById(R.id.divider).setVisibility(View.GONE);
            mScrollView = null;
        }
        if (msg != 0) {
            ((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
        }
        mInstallConfirm.setVisibility(View.VISIBLE);
        mOk = (Button)findViewById(R.id.ok_button);
        mCancel = (Button)findViewById(R.id.cancel_button);
        mOk.setOnClickListener(this);
        mCancel.setOnClickListener(this);
        if (mScrollView == null) {
            // There is nothing to scroll view, so the ok button is immediately
            // set to install.
            mOk.setText(R.string.install);
            mOkCanInstall = true;
        } else {
            mScrollView.setFullScrollAction(new Runnable() {
                @Override
                public void run() {
                    mOk.setText(R.string.install);
                    mOkCanInstall = true;
                }
            });
        }
    }

  从文件的134行起和权限有,进入AppSecurityPermissions(Context context, PackageInfo info)构造函数内部查看具体逻辑,AppSecurityPermissions(Context context, PackageInfo info)构造函数所在位置:/frameworks/base/core/java/android/widget/AppSecurityPermissions.java;

public AppSecurityPermissions(Context context, PackageInfo info) {
        this(context);
        Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
        if(info == null) {
            return;
        }
        mPackageName = info.packageName;

        // Convert to a PackageInfo
        PackageInfo installedPkgInfo = null;
        // Get requested permissions
        if (info.requestedPermissions != null) {
            try {
                installedPkgInfo = mPm.getPackageInfo(info.packageName,
                        PackageManager.GET_PERMISSIONS);
            } catch (NameNotFoundException e) {
            }
            extractPerms(info, permSet, installedPkgInfo);
        }
        // Get permissions related to  shared user if any
        if (info.sharedUserId != null) {
            int sharedUid;
            try {
                sharedUid = mPm.getUidForSharedUser(info.sharedUserId);
                getAllUsedPermissions(sharedUid, permSet);
            } catch (NameNotFoundException e) {
                Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName);
            }
        }
        // Retrieve list of permissions
        mPermsList.addAll(permSet);
        setPermissions(mPermsList);
    }

  此函数调用了extractPerms(info, permSet, installedPkgInfo)函数,这个函数也在AppSecurityPermissions.java文件内部,查看函数定义。

private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet, PackageInfo installedPkgInfo) {

    ``````
    myPerm.mNew = newPerm;
    permSet.add(myPerm);

    ````
}
private final Map<String, MyPermissionGroupInfo> mPermGroups
        = new HashMap<String, MyPermissionGroupInfo>();

  在AppSecurityPermissions类中定义了mPermGroups全局变量,以permissionGroup为键的键值对。extractPerms(PackageInfo info,Set《AppSecurityPermissions.MyPermissionInfo> permSet,PackageInfo installedPkgInfo)函数就是从第一个参数PackageInfo中获取的权限信息保存到第二个参数。

public AppSecurityPermissions(Context context, PackageInfo info) {

     ```` 

     if (info.sharedUserId != null) {                       
         int sharedUid;
         try {
             sharedUid = mPm.getUidForSharedUser(info.sharedUserId);
             getAllUsedPermissions(sharedUid, permSet);
         } catch (NameNotFoundException e) {
             Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName);
         }  

      ````
}

  在这个if分支里面,处理了shareUid的情况,即getAllUsedPermissions(sharedUid, permSet)函数,结果也存储在第二参数即permSet参数。AppSecurityPermissions类中有一个变量,即mPermsList变量,用于存储从apk中分析得到的permission信息。

  private final PermissionGroupInfoComparator mPermGroupComparator = new PermissionGroupInfoComparator();
  private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
  private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
  private final CharSequence mNewPermPrefix;
  private String mPackageName;    

  安装流程至此,所有和权限相关的就是从apk中提取权限,然后把提取出的权限存放在AppSecurityPermissions类的mPermsList变量变量中,最后显示出来。最后,界面上“下一步”和“取消”按钮分别对应于onClick()函数,我们关注用户同意以后的操作。

public void onClick(View v) {
    if (v == mOk) {
        if (mOkCanInstall || mScrollView == null) {
            mInstallFlowAnalytics.setInstallButtonClicked();
            if (mSessionId != -1) {
                mInstaller.setPermissionsResult(mSessionId, true);

                // We're only confirming permissions, so we don't really know how the
                // story ends; assume success.
                mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
                        PackageManager.INSTALL_SUCCEEDED);
            } else {
                // Start subactivity to actually install the application
                Intent newIntent = new Intent();
                newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                        mPkgInfo.applicationInfo);
                newIntent.setData(mPackageURI);
                newIntent.setClass(this, InstallAppProgress.class);
                newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest);
                newIntent.putExtra(
                        InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics);
                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 != VerificationParams.NO_UID) {
                    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();
        } else {
            mScrollView.pageScroll(View.FOCUS_DOWN);
        }
    } else if(v == mCancel) {
        // Cancel and finish
        setResult(RESULT_CANCELED);
        if (mSessionId != -1) {
            mInstaller.setPermissionsResult(mSessionId, false);
        }
        mInstallFlowAnalytics.setFlowFinished(
                InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
        finish();
    }
}

  继续分析前,把上面的函数调用流程总结一下,见下图。

Created with Raphaël 2.1.0 PackageInstallerActivity中onCreate() PackageInstallerActivity中initiateInstall() PackageInstallerActivity中startInstallConfirm() perms = new AppSecurityPermissions(this, mPkgInfo);perms结构中的mPermsList保存App权限信息 启动InstallAppProcess这个Activity

  把AppSecurityPermissions实例化过程也总结一下。

Created with Raphaël 2.1.0 AppSecurityPermissions() extractPerms(PackageInfo, Set<MyPermissionInfo> ,PackageInfo)permission存到第二个参数 回到AppSecurityPermissions将permission存到mPermsList成员。

  点击同意后,就是一个startActivity的操作,也就是打开InstallAppProcess.java这个Activity。在Oncreate()函数中调用了PackageManager的pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null)函数。PackageManager是抽象内,其方法实现在子类:/frameworks/base/core/java/android/app/ApplicationPackageManager.java

public void onCreate(Bundle icicle) {
    super.onCreate(icicle);

    ```
    initView();
}
public void initView() {
    setContentView(R.layout.op_progress);

    ```
    pm.installPackageWithVerificationAndEncryption(mPackageURI,observer, installFlags,installerPackageName, verificationParams, null);
}
    @Override
    public void installPackageWithVerificationAndEncryption(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
                encryptionParams);
    }

  这个函数调用了自身文件内定义的installCommon(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName,VerificationParams verificationParams, ContainerEncryptionParams encryptionParams)函数。

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");
    }
    if (encryptionParams != null) {
        throw new UnsupportedOperationException("ContainerEncryptionParams not supported");
    }

    final String originPath = packageURI.getPath();
    try {
        mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                verificationParams, null);
    } catch (RemoteException ignored) {
    }
}

  而这个函数的关键是调用了installPackage函数,继续追中installPackag()函数,发现并不能跟踪到。原来,ApplicationPackageManager中的mPM成员变量一个IPackageManager变量,而IPackageManager是通过AIDL定义的类,就是有程序生成的。IPackageManager.java文件在编译过程中是经aidl工具处理IPackageManager.aidl后得到,最终的文件位置在Android源码/out/target。IPackageManager.aidl文件所在路径:\frameworks\base\core\java\android\content\pm\IPackageManager.aidl。
所以实际调用了PackageManager.java的installPackage()函数,所在路径:\frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java。

public void installPackage(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, VerificationParams verificationParams,
        String packageAbiOverride) {
    installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,
            packageAbiOverride, UserHandle.getCallingUserId());
}

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

    final int callingUid = Binder.getCallingUid();
    enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");

    if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
        try {
            if (observer != null) {
                observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
            }
        } catch (RemoteException re) {
        }
        return;
    }

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

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

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

  installPackage()函数调用了installPackageAsUser()函数,在这个函数中,首先调用mContext.enforceCallingOrSelfPermission()检查调用者是否有安装apk的权限,即判断否申请了android.Manifest.permission.INSTALL_PACKAGES权限。接着判断调用者是否是受限制的app,即只有root,shell以及拥有系统签名的app才有执行权限。

  由于安装程序是一个耗时的过程,所以函数使用了Handler消息传递机制,PackageManagerService中定义了PackageHandler的Handler类。Handler首先处理installPackageAsUser()方法发出的INIT_COPY消息,接着跳转到MCS_BOUND消息的处理。在MCS_BOUND消息处理中调用了startCopy()函数,接着调用handleStartCopy()函数,最后调用copyApk()函数,这个函数完成apk的拷贝以及所需文件夹子的创建。handlerStartCopy()函数调用完成之后,调用了handleReturnCode()方法。

final boolean startCopy() {
    boolean res;
    try {
        if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

        if (++mRetries > MAX_RETRIES) {
            Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
            mHandler.sendEmptyMessage(MCS_GIVE_UP);
            handleServiceError();
            return false;
        } else {
            handleStartCopy();
            res = true;
        }
    } catch (RemoteException e) {
        if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
        mHandler.sendEmptyMessage(MCS_RECONNECT);
        res = false;
    }
    handleReturnCode();
    return res;
}
public void handleStartCopy() throws RemoteException {

    ``````    
    final InstallArgs args = createInstallArgs(this);

    `````
    ret = args.copyApk(mContainerService, true);
}  
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
    if (origin.staged) {
        Slog.d(TAG, origin.file + " already staged; skipping copy");
        codeFile = origin.file;
        resourceFile = origin.file;
        return PackageManager.INSTALL_SUCCEEDED;
    }

    try {
        final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
        codeFile = tempDir;
        resourceFile = tempDir;
    } catch (IOException e) {
        Slog.w(TAG, "Failed to create copy file: " + e);
        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
    }

    final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
        @Override
        public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
            if (!FileUtils.isValidExtFilename(name)) {
                throw new IllegalArgumentException("Invalid filename: " + name);
            }
            try {
                final File file = new File(codeFile, name);
                final FileDescriptor fd = Os.open(file.getAbsolutePath(),
                        O_RDWR | O_CREAT, 0644);
                Os.chmod(file.getAbsolutePath(), 0644);
                return new ParcelFileDescriptor(fd);
            } catch (ErrnoException e) {
                throw new RemoteException("Failed to open: " + e.getMessage());
            }
        }
    };

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

  这个函数中调用了copyPackage()函数,这个函数也是调用了系统服务,这个方法在/frameworks/base/core/java/com/android/internal/app/IMediaContainerService.aidl中定义,在/frameworks/base/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java中实现:

/**
 * Copy package to the target location.
 *
 * @param packagePath absolute path to the package to be copied. Can be
 *            a single monolithic APK file or a cluster directory
 *            containing one or more APKs.
 * @return returns status code according to those in
 *         {@link PackageManager}
 */
@Override
public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
    if (packagePath == null || target == null) {
        return PackageManager.INSTALL_FAILED_INVALID_URI;
    }

    PackageLite pkg = null;
    try {
        final File packageFile = new File(packagePath);
        pkg = PackageParser.parsePackageLite(packageFile, 0);
        return copyPackageInner(pkg, target);
    } catch (PackageParserException | IOException | RemoteException e) {
        Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
    }
}

  查看copyPackageInner()函数:

private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
        throws IOException, RemoteException {
    copyFile(pkg.baseCodePath, target, "base.apk");
    if (!ArrayUtils.isEmpty(pkg.splitNames)) {
        for (int i = 0; i < pkg.splitNames.length; i++) {
            copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");
        }
    }

    return PackageManager.INSTALL_SUCCEEDED;
}

  copyFile()函数:

private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)
       throws IOException, RemoteException {
   Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
   InputStream in = null;
   OutputStream out = null;
   try {
       in = new FileInputStream(sourcePath);
       out = new ParcelFileDescriptor.AutoCloseOutputStream(
               target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
       Streams.copy(in, out);
   } finally {
       IoUtils.closeQuietly(out);
       IoUtils.closeQuietly(in);
   }
}

  所以copyPackage()就是把apk文件拷贝到/data/app/packageName/base.apk.而NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,abiOverride);就是将apk中的so文件拷贝到/data/app/packageName/lib目录下。

  至此,startCopy()方法漫长的函数调用链追踪完毕,我们在回顾一下这个方法的调用流程。
startCopy()——>handleStartCopy()——>copyApk();copyApk()分别调用了copyPackage()就是把apk文件拷贝到/data/app/packageName/base.apk,以及NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,abiOverride);就是将apk中的so文件拷贝到/data/app/packageName/lib目录下,我们还是以流程图的形式总结一下。

Created with Raphaël 2.1.0 InstallAppProcess中的OnCreate() ApplicationPackageManager中的installPackageWithVerificationAndEncryption() ApplicationPackageManager中的installCommon() PackageManagerService中的installPackage() PackageManagerService中的installPackageAsUser() 启动Handler机制发送INIT_COPY消息 MCS_BOUND消息处理中调用startCopy() 调用handleStartCopy()和handleReturnCode()
Created with Raphaël 2.1.0 PackageManagerService中的handleStartCopy() PackageManagerService.FileInstallArgs中的copyApk() copyPackage()把apk拷贝到/data/app/pkgName-1/下 NativeLibraryHelper.copyNativeBinariesWithOverride()把so拷贝到/data/app/pkgName-1/lib/下

  回到startCopy()函数,在handleStartCopy()调用完成后,还调用了一个重要的方法handleReturnCode()。继续查看handleReturnCode()源码。

void handleReturnCode() {
  // If mArgs is null, then MCS couldn't be reached. When it
  // reconnects, it will try again to install. At that point, this
  // will succeed.
  if (mArgs != null) {
      processPendingInstall(mArgs, mRet);
  }
}
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
    // Queue up an async operation since the package installation may take a little while.
    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);
            }

            // A restore should be performed at this point if (a) the install
            // succeeded, (b) the operation is not an update, and (c) the new
            // package has not opted out of backup participation.
            final boolean update = res.removedInfo.removedPackage != null;
            final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
            boolean doRestore = !update
                    && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);

            // Set up the post-install work request bookkeeping.  This will be used
            // and cleaned up by the post-install event handling regardless of whether
            // there's a restore pass performed.  Token values are >= 1.
            int token;
            if (mNextInstallToken < 0) mNextInstallToken = 1;
            token = mNextInstallToken++;

            PostInstallData data = new PostInstallData(args, res);
            mRunningInstalls.put(token, data);
            if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);

            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
                // Pass responsibility to the Backup Manager.  It will perform a
                // restore if appropriate, then pass responsibility back to the
                // Package Manager to run the post-install observer callbacks
                // and broadcasts.
                IBackupManager bm = IBackupManager.Stub.asInterface(
                        ServiceManager.getService(Context.BACKUP_SERVICE));
                if (bm != null) {
                    if (DEBUG_INSTALL) Log.v(TAG, "token " + token
                            + " to BM for possible restore");
                    try {
                        if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
                            bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
                        } else {
                            doRestore = false;
                        }
                    } catch (RemoteException e) {
                        // can't happen; the backup manager is local
                    } catch (Exception e) {
                        Slog.e(TAG, "Exception trying to enqueue restore", e);
                        doRestore = false;
                    }
                } else {
                    Slog.e(TAG, "Backup Manager not found!");
                    doRestore = false;
                }
            }

            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);
                Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
                mHandler.sendMessage(msg);
            }
        }
    });
}
Created with Raphaël 2.1.0 handleReturnCode() processPendingInstall(final InstallArgs args, final int currentStatus) void installPackageLI(InstallArgs args, PackageInstalledInfo res)

  installPackageLI()方法的代码太长,所以还是分解分析。首先还是通过PackageParser来获得Package实例,collectCertificates()函数调用链中,完成了App安装过程中(非升级App)对App签名的检验工作,具体可参看另一篇博客Android App签名(证书)校验过程源码分析。collectManifestDigest()函数负责计算Manifest.xml的文件摘要,并且存在了Package的manifestDigest成员下。

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

    ````

    try {
      pp.collectCertificates(pkg, parseFlags);
      pp.collectManifestDigest(pkg);
    } catch (PackageParserException e) {
      res.setError("Failed collect during installPackageLI", e);
      return;
    }

    ````
}

  接着,安装程序比较apk中manifest.xml文件的摘要值:将从apk文件中提提取的manifest计算摘要和之前installer解析到的进行比较,若不匹配,则抛出异常。

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

    ````

     if (args.manifestDigest != null) {
        if (DEBUG_INSTALL) {
            final String parsedManifest = pkg.manifestDigest == null ? "null"
                    : pkg.manifestDigest.toString();
            Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
                    + parsedManifest);
        }

        if (!args.manifestDigest.equals(pkg.manifestDigest)) {
            res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");
            return;
        }
    } else if (DEBUG_INSTALL) {
        final String parsedManifest = pkg.manifestDigest == null
                ? "null" : pkg.manifestDigest.toString();
        Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);
    }

    ````
}

  接下来,是对App升级或者使用了shareUserId属性时的证书判断。

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

    ````
       if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {
           try {
               verifySignaturesLP(ps, pkg);
           } catch (PackageManagerException e) {
               res.setError(e.error, e.getMessage());
               return;
           }
       }

    ````
}

  上面if中的!ps.keySetData.isUsingUpgradeKeySets()给出了不是采用Upgrade keysets升级的情况,是不是采用Upgrade keysets升级时根据PackageKeySetData中的long[] mUpgradeKeySets成员是否空或者长度是否为零决定的。关于Upgrade keysets,后面可能会给出说明。对于不是Upgrade keysets升级或者shareUid情况的,调用了verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg)函数来检验签名,若不匹配,抛出PackageManagerException异常。

private void verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg)throws PackageManagerException {
    if (pkgSetting.signatures.mSignatures != null) {
        // Already existing package. Make sure signatures match
        boolean match = compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures)
                == PackageManager.SIGNATURE_MATCH;
        if (!match) {
            match = compareSignaturesCompat(pkgSetting.signatures, pkg)
                    == PackageManager.SIGNATURE_MATCH;
        }
        if (!match) {
            match = compareSignaturesRecover(pkgSetting.signatures, pkg)
                    == PackageManager.SIGNATURE_MATCH;
        }
        if (!match) {
            throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                    + pkg.packageName + " signatures do not match the "
                    + "previously installed version; ignoring!");
        }
    }

    // Check for shared user signatures
    if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
        // Already existing package. Make sure signatures match
        boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
        if (!match) {
            match = compareSignaturesCompat(pkgSetting.sharedUser.signatures, pkg)
                    == PackageManager.SIGNATURE_MATCH;
        }
        if (!match) {
            match = compareSignaturesRecover(pkgSetting.sharedUser.signatures, pkg)
                    == PackageManager.SIGNATURE_MATCH;
        }
        if (!match) {
            throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                    "Package " + pkg.packageName
                    + " has no signatures that match those in shared user "
                    + pkgSetting.sharedUser.name + "; ignoring!");
        }
    }
}

  校验过程主要调用了三个函数,其实这三个函数都是比较旧的apk中的Signatures[]和新的apk中的Signatures[]是否相同。但这里有几个不明确的点,就是如何从apk中或得Signatures[],这个以后单独分析。

  在installPackageLI()函数中,接着是对采用Upgrade keysets升级时检验签名的处理,即调用checkUpgradeKeySetLP()检验新的apk中是都全部包含老的apk中的公钥信息,若不是,则抛出异常。checkUpgradeKeySetLP()用到了证书公钥信息,这解决了一点点在PackageInstaller源码分析中提出疑问。

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

    ````
    else {
        if (!checkUpgradeKeySetLP(ps, pkg)) {
           res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                   + pkg.packageName + " upgrade keys do not match the "
                   + "previously installed version");
           return;
       }
   }
   ````
}
private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {
    // Upgrade keysets are being used.  Determine if new package has a superset of the
    // required keys.
    long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
    KeySetManagerService ksms = mSettings.mKeySetManagerService;
    for (int i = 0; i < upgradeKeySets.length; i++) {
        Set<PublicKey> upgradeSet = ksms.getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
        if (newPkg.mSigningKeys.containsAll(upgradeSet)) {
            return true;
        }
    }
    return false;
}

  接下来,处理安装的apk申明的权限,分别对apk为系统apk申明系统权限和非系统apk申明系统权限做了处理。

private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {

    `````

   // Check whether the newly-scanned package wants to define an already-defined perm
   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) {
           // If the defining package is signed with our cert, it's okay.  This
           // also includes the "updating the same package" case, of course.
           // "updating same package" could also involve key-rotation.
           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 (!sigsOk) {
               // If the owning package is the system itself, we log but allow
               // install to proceed; we fail the install on all other permission
               // redefinitions.
               if (!bp.sourcePackage.equals("android")) {
                   res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
                           + pkg.packageName + " attempting to redeclare permission "
                           + perm.info.name + " already owned by " + bp.sourcePackage);
                   res.origPermission = perm.info.name;
                   res.origPackage = bp.sourcePackage;
                   return;
               } else {
                   Slog.w(TAG, "Package " + pkg.packageName
                           + " attempting to redeclare system permission "
                           + perm.info.name + "; ignoring new declaration");
                   pkg.permissions.remove(i);
               }
           }
       }
   }

    ````
}

  最后,针对升级App还是安装新App分别交由不同的函数执行,升级App调用replacePackageLI()函数,安装新App调用installNewPackageLI()。

private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {

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

     ````
}

  在分析这两个函数之前,还是对installPackageLI()函数做个总结,我们看看在这个函数中都做了哪些事。

  • 首先,调用了collectCertificates()函数对apk的签名做了校验,确保apk没有被非法修改(修改其中的文件);
  • 接着,调用collectManifestDigest()函数计算了Manifest.xml的文件摘要,并且存在了Package的manifestDigest成员下;
  • 接着,将从apk文件中提提取的manifest计算摘要和之前installer解析到的进行比较,若不匹配,则抛出异常。
  • 然后,对App升级或者使用了共享属性sharedUid的情况校验待安装的App的证书是否匹配,并就升级方式做了具体处理,这里有两个问题,就是升级过程中的Upgrade keysets升级方式和Signature[]的获得不明朗。
  • 最后,就到了根据是升级App还是安装新App,调用不同的函数进行处理环节。

  鉴于不想本篇博文篇幅过长,对App升级还是安装新App过程分析放到PackageInstaller源码分析(二)下面总结一下。

二、总结

  PackageInstaller负责App的安装工作,根据既有知识,Android权限是在App安装时候一次性授予的,所以在App安装过程要对权限处理。原本分析PackageInstaller源码,旨在分析权限授予过程。由于App安装过程中牵扯很多的安全策略,就一并分析了,包括对apk证书的校验等。

  下面对前面的分析做个梳理。

  • PackageInstallerActivity的onCreate();
  • 根据从Intent中获取的Data的scheme的不同,调用不同的处理逻辑安装App,这篇是从File安装的;
  • 解析App文件到Package对象;
  • initInstall()函数获取apk中的权限信息;
  • onClick()监听用户点击同意安装按钮;
  • 启动InstallAppProcess Activity;
  • onCrete()函数调用了installPackageWithVerificationAndEncryption()函数;
  • 继续调用installPackage()函数;
  • 通过Handler机制启动了startCopy()函数;
  • startCopy()调用了启动了handlerStartCopy()将apk拷贝到/data/app、pkg-name/和把.so文件拷贝到/data/app/pkg-name/lib/的handlerReturnCode()函数;
  • handlerReturnCode调用了installPackageLI()函数;
  • 在installPackageLI()中,首先调用了collectCertificates()函数对apk的签名做了校验,确保apk没有被非法修改(修改其中的文件);
  • 接着,调用collectManifestDigest()函数计算了Manifest.xml的文件摘要,并且存在了Package的manifestDigest成员下;
  • 接着,将从apk文件中提提取的manifest计算摘要和之前installer解析到的进行比较,若不匹配,则抛出异常。
  • 然后,对App升级或者使用了共享属性sharedUid的情况校验待安装的App的证书是否匹配,并就升级方式做了具体处理,这里有两个问题,就是升级过程中的Upgrade keysets升级方式和Signature[]的获得不明朗。
  • 最后,就到了根据是升级App还是安装新App,调用不同的函数进行处理环节。

  PackageInstaller源码分析(一)暂时处理到这,这里还有几个模糊的地方,前面分析也提到了。一是升级过程中的Upgrade keysets升级方式,二是Signature[]的结构和怎么获得的不够明朗。后续分析即关于是升级App还是安装新App的处理见PackageInstaller源码分析(二)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值