Android的包管理机制浅析(一)

在前面分析Service和Broadcast的时候经常提到PackageManagerService,今天就系统的对他进行分析一下。

Android的包管理是典型的代理模式,也就是分为客户端和服务端的实现,下面来看看源码中的代码列表

客户端(本地端)在\platform\frameworks\base\core\java\android\content\pm路径下:

从这个图中可以看到ActivityInfo,ApplicationInfo,ComponentInfo以及等等一些info.java都是一些用来记录当前应用信息的数据结构类(这些数据基本都是从manifest文件中解析出来的),对应的aidl文件是为了使得这些数据结构可以在AIDL方式的跨进程通信方式中轻松传递。

还有一些数据结构在后面分析时用到了再详细阐述。

接下来的IPackageDataObserver、IPackageDeleteObserver、IPackageInstallObserver、IPackageMoveObserver和IPackageStatsObserver的aidl文件都是表示的一种跨进程回调机制,用来执行一系列的操作完成之后的回调操作。

注意一下这里有两个关键的文件IPackageManager.aidl和PackageManager.java,他们和下面要说的PackageManagerService一起是包管理机制的核心,分别代表了包管理对外提供的方法、本地客户端和远程服务端。

最后XmlSerializerAndParser.java定义了一个用于解析xml文件的接口。

还有服务端的实现:platform\frameworks\base\services\java\com\android\server\pm路径下:

这个包里面文件就少得多了,

BackgroundDexOptService.java主要用于classes文件的odex优化。

Installer是用来实现Package的install、dexopt和remove等操作的实体,它通过socket与底层installd交互实现基本功能。

PackageManagerService是应用包管理机制的核心实现类,里面包含了几乎所有的操作。

PackageSignatures主要用于解析当前应用的签名信息。

PreferredActivity和PreferredIntentResolver用于处理同一Intent多个应用可以接收时的默认启动项。

SELinuxMMAC类用于管理一些安全策略。

Settings是存储一些包管理过程中的状态,感觉应该设计为单例的,但是这个是在PackageManagerService中统一管理的也就一个对象并有与手机运行相同的生命周期。

SharedUserSetting是GrantedPermissions的子类它们是用来处理APP的共享用户id的,与PackageSignatures签名是息息相关的。

UserManagerService是IUserManager的Stub,看来也是一个跨进程的东西,它是在多用户的系统中用来管理用户及其各种信息的。

这两部分所涉及到的类都很多,分析源码也不能一个文件一个文件的分析,所以我们还是从几个主要的操作场景来切入,主要是APK的安装,升级,卸载,启用和禁用。然后再从框架上对其进行总结分析。

系统中用于安装应用有一个专门的APP叫PackageInstaller,在源码的这个位置android-4.4.3\platform\packages\apps\PackageInstaller。启动它是发送了一个广播,告诉系统我有意向要安装一个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>
        </activity>

这是PackageInstaller的PackageInstallerActivity的Manifest注册信息,可以看到它可以接收Action为android.intent.action.VIEW和android.intent.action.INSTALL_PACKAGE的广播。然后就是由这个应用来执行应用的安装操作,这之前还会对所安装的apk的信息进行一个解析并展示给用户。

解析,这里第一次要提到PackageParser这个类,顾名思义就是包解析器,我们这里用到了parsePackage的接口,顺便我们会把这个类的其他重要接口也进行说明:(需要指出的是,这个类只是一个工具类,归根到底也只是一个XML文件解析而已,还没有涉及到包管理机制本身。)下面会贴一些系统的源码,注意一下我源码中的中文注释。

    public Package parsePackage(File sourceFile, String destCodePath,
            DisplayMetrics metrics, int flags, boolean trustedOverlay) {
        mParseError = PackageManager.INSTALL_SUCCEEDED;

        mArchiveSourcePath = sourceFile.getPath();
        //这里省略了对是否是APK文件的判断
        XmlResourceParser parser = null;
        AssetManager assmgr = null;
        Resources res = null;
        boolean assetError = true;
        try {
            assmgr = new AssetManager();
            int cookie = assmgr.addAssetPath(mArchiveSourcePath);
            if (cookie != 0) {
                res = new Resources(assmgr, metrics, null);
                assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                        Build.VERSION.RESOURCES_SDK_INT);
                //这里是重点,对一个APK的解析内容主要来自于其AndroidManifest.xml的文件,所以这里创建了一个xml文件的解析器
                parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
                assetError = false;
            } else {
                Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
            }
        } catch (Exception e) {
            Slog.w(TAG, "Unable to read AndroidManifest.xml of "
                    + mArchiveSourcePath, e);
        }
        if (assetError) {
            if (assmgr != null) assmgr.close();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
            return null;
        }
        String[] errorText = new String[1];
        Package pkg = null;
        Exception errorException = null;
        try {
            // XXXX todo: need to figure out correct configuration.
            // 真正的工作从这里才开始,对xml文件所包含的信息进行全解析,包括Application、Activity等等信息
            pkg = parsePackage(res, parser, flags, trustedOverlay, errorText);
        } catch (Exception e) {
            errorException = e;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
        }

        if (pkg == null) {
            // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
            // just means to skip this app so don't make a fuss about it.
            if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
                if (errorException != null) {
                    Slog.w(TAG, mArchiveSourcePath, errorException);
                } else {
                    Slog.w(TAG, mArchiveSourcePath + " (at "
                            + parser.getPositionDescription()
                            + "): " + errorText[0]);
                }
                if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                }
            }
            parser.close();
            assmgr.close();
            return null;
        }
        //不要忘记在文件处理之后需要关闭一些资源
        parser.close();
        assmgr.close();

        // Set code and resource paths
        pkg.mPath = destCodePath;
        pkg.mScanPath = mArchiveSourcePath;
        //pkg.applicationInfo.sourceDir = destCodePath;
        //pkg.applicationInfo.publicSourceDir = destRes;
        pkg.mSignatures = null;

        return pkg;
    }
来看看真正的解析方法吧,这个方法很长,可以细看这个方法能加深对AndroidManifest.xml文件的理解。

    private Package parsePackage(
        Resources res, XmlResourceParser parser, int flags, boolean trustedOverlay,
        String[] outError) throws XmlPullParserException, IOException {
        AttributeSet attrs = parser;

        mParseInstrumentationArgs = null;
        mParseActivityArgs = null;
        mParseServiceArgs = null;
        mParseProviderArgs = null;
        //解析包名
        String pkgName = parsePackageName(parser, attrs, flags, outError);
        if (pkgName == null) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
            return null;
        }
        int type;

        if (mOnlyCoreApps) {
            boolean core = attrs.getAttributeBooleanValue(null, "coreApp", false);
            if (!core) {
                mParseError = PackageManager.INSTALL_SUCCEEDED;
                return null;
            }
        }

        final Package pkg = new Package(pkgName);
        boolean foundApp = false;
        //下面是解析版本以及共享ID等信息
        TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifest);
        pkg.mVersionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
        pkg.mVersionName = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_versionName, 0);
        if (pkg.mVersionName != null) {
            pkg.mVersionName = pkg.mVersionName.intern();
        }
        String str = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
        if (str != null && str.length() > 0) {
            <span style="color:#FF0000;">//检测共享ID的合法性</span>
            String nameError = validateName(str, true);
            if (nameError != null && !"android".equals(pkgName)) {
                outError[0] = "<manifest> specifies bad sharedUserId name \""
                    + str + "\": " + nameError;
                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
                return null;
            }
            pkg.mSharedUserId = str.intern();
            pkg.mSharedUserLabel = sa.getResourceId(
                    com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
        }
        sa.recycle();
        //检测APK指定安装位置,主要是SD卡和内置
        pkg.installLocation = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_installLocation,
                PARSE_DEFAULT_INSTALL_LOCATION);
        pkg.applicationInfo.installLocation = pkg.installLocation;

        /* Set the global "forward lock" flag */
        if ((flags & PARSE_FORWARD_LOCK) != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FORWARD_LOCK;
        }

        /* Set the global "on SD card" flag */
        if ((flags & PARSE_ON_SDCARD) != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
        }

        // Resource boolean are -1, so 1 means we don't know the value.
        int supportsSmallScreens = 1;
        int supportsNormalScreens = 1;
        int supportsLargeScreens = 1;
        int supportsXLargeScreens = 1;
        int resizeable = 1;
        int anyDensity = 1;
        
        int outerDepth = parser.getDepth();
        //进入xml文件各个标签的处理了
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }
            String tagName = parser.getName();
            //对标签名进行判断,以此对各种标签进行解析,下面的内容可以细看
            if (tagName.equals("application")) {
                .................
            } else if (tagName.equals("overlay")) {
                .................
            } else if (tagName.equals("keys")) {
                if (!parseKeys(pkg, res, parser, attrs, outError)) {
                    return null;
                }
            } else if (tagName.equals("permission-group")) {
                if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("permission")) {
                if (parsePermission(pkg, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("permission-tree")) {
                if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("uses-permission")) {
                if (!parseUsesPermission(pkg, res, parser, attrs, outError)) {
                    return null;
                }

            } else if (tagName.equals("uses-configuration")) {
                ......................

            } else if (tagName.equals("uses-feature")) {
                ......................

            } else if (tagName.equals("uses-sdk")) {
                ......................

            } else if (tagName.equals("supports-screens")) {
                ......................
                
            } else if (tagName.equals("protected-broadcast")) {
                ......................
                
            } else if (tagName.equals("instrumentation")) {
                if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
                    return null;
                }
                
            } else if (tagName.equals("original-package")) {
                .....................
                
            } else if (tagName.equals("adopt-permissions")) {
                .....................
                
            } else if (tagName.equals("uses-gl-texture")) {
                // Just skip this tag
                XmlUtils.skipCurrentTag(parser);
                continue;
                
            } else if (tagName.equals("compatible-screens")) {
                // Just skip this tag
                XmlUtils.skipCurrentTag(parser);
                continue;
            } else if (tagName.equals("supports-input")) {
                XmlUtils.skipCurrentTag(parser);
                continue;
                
            } else if (tagName.equals("eat-comment")) {
                // Just skip this tag
                XmlUtils.skipCurrentTag(parser);
                continue;
                
            } else if (RIGID_PARSER) {
                outError[0] = "Bad element under <manifest>: "
                    + parser.getName();
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return null;

            } else {
                Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
                        + " at " + mArchiveSourcePath + " "
                        + parser.getPositionDescription());
                XmlUtils.skipCurrentTag(parser);
                continue;
            }
        }
        if (!foundApp && pkg.instrumentation.size() == 0) {
            outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
        }
        <span style="color:#FF0000;">//这下面有一段对新增权限的兼容处理,包括最后对较低版本上的APK在18以上运行的问题</span>
        final int NP = PackageParser.NEW_PERMISSIONS.length;
        StringBuilder implicitPerms = null;
        for (int ip=0; ip<NP; ip++) {
            final PackageParser.NewPermissionInfo npi
                    = PackageParser.NEW_PERMISSIONS[ip];
            if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
                break;
            }
            if (!pkg.requestedPermissions.contains(npi.name)) {
                if (implicitPerms == null) {
                    implicitPerms = new StringBuilder(128);
                    implicitPerms.append(pkg.packageName);
                    implicitPerms.append(": compat added ");
                } else {
                    implicitPerms.append(' ');
                }
                implicitPerms.append(npi.name);
                pkg.requestedPermissions.add(npi.name);
                pkg.requestedPermissionsRequired.add(Boolean.TRUE);
            }
        }
        if (implicitPerms != null) {
            Slog.i(TAG, implicitPerms.toString());
        }

        final int NS = PackageParser.SPLIT_PERMISSIONS.length;
        for (int is=0; is<NS; is++) {
            final PackageParser.SplitPermissionInfo spi
                    = PackageParser.SPLIT_PERMISSIONS[is];
            if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
                    || !pkg.requestedPermissions.contains(spi.rootPerm)) {
                continue;
            }
            for (int in=0; in<spi.newPerms.length; in++) {
                final String perm = spi.newPerms[in];
                if (!pkg.requestedPermissions.contains(perm)) {
                    pkg.requestedPermissions.add(perm);
                    pkg.requestedPermissionsRequired.add(Boolean.TRUE);
                }
            }
        }
        
        if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
        }
        if (supportsNormalScreens != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
        }
        if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
        }
        if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.GINGERBREAD)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
        }
        if (resizeable < 0 || (resizeable > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
        }
        if (anyDensity < 0 || (anyDensity > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
        }

        /*
         * b/8528162: Ignore the <uses-permission android:required> attribute if
         * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild
         * which are improperly using this attribute, even though it never worked.
         */
        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) {
                pkg.requestedPermissionsRequired.set(i, Boolean.TRUE);
            }
        }

        return pkg;
    }
继续分析流程,解析了APK信息之后,就是安装操作,我们来看看InstallAppProgress这个类的initView方法
    public void initView() {
        setContentView(R.layout.op_progress);
        int installFlags = 0;
        PackageManager pm = getPackageManager();
        try {
            <span style="color:#FF0000;">//判断当前需要安装的apk信息是否已经存在,如果已经存在则设置当前flags中替换安装的属性</span>
            PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName, 
                    PackageManager.GET_UNINSTALLED_PACKAGES);
            if(pi != null) {
                installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
            }
        } catch (NameNotFoundException e) {
        }
        if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
            Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
        }

        final PackageUtil.AppSnippet as;
        if ("package".equals(mPackageURI.getScheme())) {
            as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo),
                    pm.getApplicationIcon(mAppInfo));
        } else {
            final File sourceFile = new File(mPackageURI.getPath());
            as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile);
        }
        ......................

        PackageInstallObserver observer = new PackageInstallObserver();

        if ("package".equals(mPackageURI.getScheme())) {
            try {
                pm.installExistingPackage(mAppInfo.packageName);
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_SUCCEEDED);
            } catch (PackageManager.NameNotFoundException e) {
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_FAILED_INVALID_APK);
            }
        } else {
             <span style="color:#FF0000;">//重点来了,这里就是安装方法。</span>
             pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
                    installerPackageName, verificationParams, null);
        }
    }
这里通过getPackageManager()获取到的是PackageManager的一个直接子类ApplicationPackageManager。我们来看看它的installPackageWithVerificationAndEncryption()方法。

    @Override
	  public void installPackageWithVerificationAndEncryption(Uri packageURI,
            IPackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        try {
            mPM.installPackageWithVerificationAndEncryption(packageURI, observer, flags,
                    installerPackageName, verificationParams, encryptionParams);
        } catch (RemoteException e) {
            // Should never happen!
        }
    }

无处不在的跨进程来了,实际上它也是在调用远程服务PackageManagerService来完成的安装功能(这里还有一个跨进程的回调IPackageInstallObserver),这个方法名字中包含了两个关键字Verifivation和Encryption,也就是说它包含了验证和加密。

    @Override
    public void installPackageWithVerificationAndEncryption(Uri packageURI,
            IPackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
                null);

        final int uid = Binder.getCallingUid();
        <span style="color:#FF0000;">//做当前Uid是否被限制的检查</span>
        if (isUserRestricted(UserHandle.getUserId(uid), UserManager.DISALLOW_INSTALL_APPS)) {
            try {
                observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);
            } catch (RemoteException re) {
            }
            return;
        }

        UserHandle user;
        if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {
            user = UserHandle.ALL;
        } else {
            user = new UserHandle(UserHandle.getUserId(uid));
        }

        final int filteredFlags;

        if (uid == Process.SHELL_UID || uid == 0) {
            if (DEBUG_INSTALL) {
                Slog.v(TAG, "Install from ADB");
            }
            filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;
        } else {
            filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;
        }

        verificationParams.setInstallerUid(uid);

        final Message msg = mHandler.obtainMessage(INIT_COPY);
        <span style="color:#FF0000;">//将所有信息组成一个installParams的参数传递到子线程中指导下一步的COPY工作</span>
        msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
                verificationParams, encryptionParams, user);
        mHandler.sendMessage(msg);
    }
这段代码很简单,告诉我们实际上安装APK对系统来说先是一个文件的拷贝工作,这里有一个mHandler,可以想象拷贝文件这么耗时的工作肯定是在子线程中进行的,有handler肯定还有一个HandlerThread,没错在PackageManagerService构造方法中就已经准备好了。几经周折最终是调用了InstallParams的handleStartCopy()方法:

        public void handleStartCopy() throws RemoteException {
            int ret = PackageManager.INSTALL_SUCCEEDED;
            final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
            final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
            PackageInfoLite pkgLite = null;

            if (onInt && onSd) {
                // Check if both bits are set.
                Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            } else {
                final long lowThreshold;

                final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager
                        .getService(DeviceStorageMonitorService.SERVICE);
                if (dsm == null) {
                    Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
                    lowThreshold = 0L;
                } else {
                    lowThreshold = dsm.getMemoryLowThreshold();
                }

                try {
                    mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, mPackageURI,
                            Intent.FLAG_GRANT_READ_URI_PERMISSION);

                    final File packageFile;
                    //下面这一段代码主要是加密安装文件的,创建一个临时文件来实现加解密
                    if (encryptionParams != null || !"file".equals(mPackageURI.getScheme())) {
                        mTempPackage = createTempPackageFile(mDrmAppPrivateInstallDir);
                        if (mTempPackage != null) {
                            ParcelFileDescriptor out;
                            try {
                                out = ParcelFileDescriptor.open(mTempPackage,
                                        ParcelFileDescriptor.MODE_READ_WRITE);
                            } catch (FileNotFoundException e) {
                                out = null;
                                Slog.e(TAG, "Failed to create temporary file for : " + mPackageURI);
                            }

                            // Make a temporary file for decryption.
                            ret = mContainerService
                                    .copyResource(mPackageURI, encryptionParams, out);
                            IoUtils.closeQuietly(out);

                            packageFile = mTempPackage;

                            FileUtils.setPermissions(packageFile.getAbsolutePath(),
                                    FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP
                                            | FileUtils.S_IROTH,
                                    -1, -1);
                        } else {
                            packageFile = null;
                        }
                    } else {
                        packageFile = new File(mPackageURI.getPath());
                    }

                    if (packageFile != null) {
                        // Remote call to find out default install location
                        final String packageFilePath = packageFile.getAbsolutePath();
                        pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags,
                                lowThreshold);

                       <span style="color:#FF0000;"> /*
                         * If we have too little free space, try to free cache
                         * before giving up.
                         */</span>
                        if (pkgLite.recommendedInstallLocation
                                == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                            final long size = mContainerService.calculateInstalledSize(
                                    packageFilePath, isForwardLocked());
                            if (mInstaller.freeCache(size + lowThreshold) >= 0) {
                                pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath,
                                        flags, lowThreshold);
                            }
                            /*
                             * The cache free must have deleted the file we
                             * downloaded to install.
                             *
                             * TODO: fix the "freeCache" call to not delete
                             *       the file we care about.
                             */
                            if (pkgLite.recommendedInstallLocation
                                    == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                                pkgLite.recommendedInstallLocation
                                    = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                            }
                        }
                    }
                } finally {
                    mContext.revokeUriPermission(mPackageURI,
                            Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }
            }
            <span style="color:#FF0000;">//下面所有的Failed字符串都是代表了一种安装时的错误。从pkgLite中解析出来的都是文件系统一类的错误,比如路径错误,空间不足等等。</span>
            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                int loc = pkgLite.recommendedInstallLocation;
                if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
                    ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                    ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_APK;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_URI;
                } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
                    ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
                } else {
                    // Override with defaults if needed.
                    <span style="color:#FF0000;">// 设置安装的位置</span>
                    loc = installLocationPolicy(pkgLite, flags);
                    if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                        ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
                    } else if (!onSd && !onInt) {
                        // Override install location with flags
                        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                            // Set the flag to install on external media.
                            flags |= PackageManager.INSTALL_EXTERNAL;
                            flags &= ~PackageManager.INSTALL_INTERNAL;
                        } else {
                            // Make sure the flag for installing on external
                            // media is unset
                            flags |= PackageManager.INSTALL_INTERNAL;
                            flags &= ~PackageManager.INSTALL_EXTERNAL;
                        }
                    }
                }
            }

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

            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                 /*
                 * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
                 * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
                 */
                int userIdentifier = getUser().getIdentifier();
                if (userIdentifier == UserHandle.USER_ALL
                        && ((flags & PackageManager.INSTALL_FROM_ADB) != 0)) {
                    userIdentifier = UserHandle.USER_OWNER;
                }

                /*
                 * Determine if we have any installed package verifiers. If we
                 * do, then we'll defer to them to verify the packages.
                 */
                final int requiredUid = mRequiredVerifierPackage == null ? -1
                        : getPackageUid(mRequiredVerifierPackage, userIdentifier);
                // 以下分为两种情况,if中是需要验证的向外发送广播并接收结果然后到handler中去处理
                // 我没有看到这个Intent.ACTION_PACKAGE_NEEDS_VERIFICATION这个广播的接收者就先不分析这一块了,有知道的可以告诉我,谢谢
                // else就直接进行了文件的拷贝
                if (requiredUid != -1 && isVerificationEnabled(flags)) {
                    final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); 
                    verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE); 
                    verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 
                    ....................... 
                    if (ret == PackageManager.INSTALL_SUCCEEDED && mRequiredVerifierPackage != null) { 
                        /* * Send the intent to the required verification agent, 
                           * but only start the verification timeout after the 
                           * target BroadcastReceivers have run. 
                           */ verification.setComponent(requiredVerifierComponent);      
                       mContext.sendOrderedBroadcastAsUser(verification, getUser(), 
                                 android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, 
                                 new BroadcastReceiver() { 
                                      @Override 
                                      public void onReceive(Context context, Intent intent) {
                                            final Message msg = mHandler .obtainMessage(CHECK_PENDING_VERIFICATION); 
                                            msg.arg1 = verificationId; mHandler.sendMessageDelayed(msg, getVerificationTimeout()); 
                                      } 
                       }, null, 0, null, null); 
                       /* * We don't want the copy to proceed until verification 
                          * succeeds, so null out this field. 
                          */ mArgs = null;
                       }
                   } else { 
                       /* * No package verification is enabled, so immediately start 
                          * the remote call to initiate copy using temporary file. 
                          */ ret = args.copyApk(mContainerService, true); 
                   }
                } 
           mRet = ret; 
     }
 

紧接着应该是handleReturnCode()的时候了,文件拷贝完成之后才是对系统来说的安装过程,在这里需要将APK的所有信息采集注册和管理。调用的是processPendingInstall方法里面向mHandler中post了一个Runnable来异步执行installPackageLI:

    private void installPackageLI(InstallArgs args,
            boolean newInstall, PackageInstalledInfo res) {
        int pFlags = args.flags;
        String installerPackageName = args.installerPackageName;
        File tmpPackageFile = new File(args.getCodePath());
        boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
        boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0);
        boolean replace = false;
        int scanMode = (onSd ? 0 : SCAN_MONITOR) | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE
                | (newInstall ? SCAN_NEW_INSTALL : 0);
        // Result object to be returned
        res.returnCode = PackageManager.INSTALL_SUCCEEDED;

        if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
        // Retrieve PackageSettings and parse package
        <span style="color:#FF0000;">// 以下就是对当前所安装的APK信息进行解析</span>
        int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
        PackageParser pp = new PackageParser(tmpPackageFile.getPath());
        pp.setSeparateProcesses(mSeparateProcesses);
        final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
                null, mMetrics, parseFlags);
        if (pkg == null) {
            res.returnCode = pp.getParseError();
            return;
        }
        String pkgName = res.name = pkg.packageName;
        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
            if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
                res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
                return;
            }
        }
        <span style="color:#FF0000;">// 签名检查,这是Android安全性的重要组成部分,下面将着重讲解</span>
        if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {
            res.returnCode = pp.getParseError();
            return;
        }
        <span style="color:#FF0000;">// 下面是manifest摘要检查,判断之前验证的manifestDigest与当前解析的内存是否一致,这里对验证内容的过程有点没太明白,忘能人指点</span>
        /* If the installer passed in a manifest digest, compare it now. */
        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.returnCode = PackageManager.INSTALL_FAILED_PACKAGE_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);
        }

        // Get rid of all references to package scan path via parser.
        pp = null;
        String oldCodePath = null;
        boolean systemApp = false;
        synchronized (mPackages) {
            <span style="color:#FF0000;">// Check if installing already existing package</span>
            <span style="color:#FF0000;">// 流程是,如果是替换安装,有两种情况一种是包名有变化(看来替换安装包名是可以不一样的,也就是在AndroidManifest文件中申明original-package标签),
            // 还有一种是包名一致的。</span>
            if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                String oldName = mSettings.mRenamedPackages.get(pkgName);
                if (pkg.mOriginalPackages != null
                        && pkg.mOriginalPackages.contains(oldName)
                        && mPackages.containsKey(oldName)) {
                    // This package is derived from an original package,
                    // and this device has been updating from that original
                    // name.  We must continue using the original name, so
                    // rename the new package here.
                    pkg.setPackageName(oldName);
                    pkgName = pkg.packageName;
                    replace = true;
                    if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
                            + oldName + " pkgName=" + pkgName);
                } else if (mPackages.containsKey(pkgName)) {
                    // This package, under its official name, already exists
                    // on the device; we should replace it.
                    replace = true;
                    if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
                }
            }
            PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
                oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
                if (ps.pkg != null && ps.pkg.applicationInfo != null) {
                    systemApp = (ps.pkg.applicationInfo.flags &
                            ApplicationInfo.FLAG_SYSTEM) != 0;
                }
                res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
            }
        }
        // 系统APK不允许安装到SD卡上
        if (systemApp && onSd) {
            // Disable updates to system apps on sdcard
            Slog.w(TAG, "Cannot install updates to system apps on sdcard");
            res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            return;
        }
        //重命名
        if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
            res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            return;
        }
        // Set application objects path explicitly after the rename
        setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
        pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
        if (replace) {
            // 更新安装
            replacePackageLI(pkg, parseFlags, scanMode, args.user,
                    installerPackageName, res);
        } else {
            // 安装新APK
            installNewPackageLI(pkg, parseFlags, scanMode, args.user,
                    installerPackageName, res);
        }
        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
            }
        }
    }

我们先来看看签名检查那一块,Android的签名机制属于安全机制的范畴,主要用于检验程序的来源,当然它也与系统的权限管理相关联。主要是两个方法:

    public boolean collectCertificates(Package pkg, int flags) {
        pkg.mSignatures = null;

        WeakReference<byte[]> readBufferRef;
        byte[] readBuffer = null;
        synchronized (mSync) {
            readBufferRef = mReadBuffer;
            if (readBufferRef != null) {
                mReadBuffer = null;
                readBuffer = readBufferRef.get();
            }
            if (readBuffer == null) {
                readBuffer = new byte[8192];
                readBufferRef = new WeakReference<byte[]>(readBuffer);
            }
        }

        try {
            StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);

            Certificate[] certs = null;

            if ((flags&PARSE_IS_SYSTEM) != 0) {
                // If this package comes from the system image, then we
                // can trust it...  we'll just use the AndroidManifest.xml
                // to retrieve its signatures, not validating all of the
                // files.
                // 针对系统预置APK的处理,只需要检查其Manifest文件即可
                ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
                certs = loadCertificates(jarFile, jarEntry, readBuffer);
                if (certs == null) {
                    Slog.e(TAG, "Package " + pkg.packageName
                            + " has no certificates at entry "
                            + jarEntry.getName() + "; ignoring!");
                    jarFile.close();
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
                    return false;
                }
                if (DEBUG_JAR) {
                    Slog.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry
                            + " certs=" + (certs != null ? certs.length : 0));
                    if (certs != null) {
                        final int N = certs.length;
                        for (int i=0; i<N; i++) {
                            Slog.i(TAG, "  Public key: "
                                    + certs[i].getPublicKey().getEncoded()
                                    + " " + certs[i].getPublicKey());
                        }
                    }
                }
            } else {
                Iterator<ZipEntry> entries = jarFile.iterator();
                while (entries.hasNext()) {
                    final ZipEntry je = entries.next();
                    if (je.isDirectory()) continue;

                    final String name = je.getName();

                    if (name.startsWith("META-INF/"))
                        continue;

                    if (ANDROID_MANIFEST_FILENAME.equals(name)) {
                        pkg.manifestDigest =
                                ManifestDigest.fromInputStream(jarFile.getInputStream(je));
                    }

                    final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer);
                    if (DEBUG_JAR) {
                        Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName()
                                + ": certs=" + certs + " ("
                                + (certs != null ? certs.length : 0) + ")");
                    }

                    if (localCerts == null) {
                        Slog.e(TAG, "Package " + pkg.packageName
                                + " has no certificates at entry "
                                + je.getName() + "; ignoring!");
                        jarFile.close();
                        mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
                        return false;
                    } else if (certs == null) {
                        certs = localCerts;
                    } else {
                        // Ensure all certificates match.
                        for (int i=0; i<certs.length; i++) {
                            boolean found = false;
                            for (int j=0; j<localCerts.length; j++) {
                                if (certs[i] != null &&
                                        certs[i].equals(localCerts[j])) {
                                    found = true;
                                    break;
                                }
                            }
                            if (!found || certs.length != localCerts.length) {
                                Slog.e(TAG, "Package " + pkg.packageName
                                        + " has mismatched certificates at entry "
                                        + je.getName() + "; ignoring!");
                                jarFile.close();
                                mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
                                return false;
                            }
                        }
                    }
                }
            }
            jarFile.close();

            synchronized (mSync) {
                mReadBuffer = readBufferRef;
            }
            // 注意下面还有一个Android签名与数字证书的关系
            if (certs != null && certs.length > 0) {
                final int N = certs.length;
                pkg.mSignatures = new Signature[certs.length];
                for (int i=0; i<N; i++) {
                    pkg.mSignatures[i] = new Signature(
                            certs[i].getEncoded());
                }
            } else {
                Slog.e(TAG, "Package " + pkg.packageName
                        + " has no certificates; ignoring!");
                mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
                return false;
            }
            // 这里有将所有的公钥保存到package的签名信息中,后面的验证主要也就是验证公钥
            // Add the signing KeySet to the system
            pkg.mSigningKeys = new HashSet<PublicKey>();
            for (int i=0; i < certs.length; i++) {
                pkg.mSigningKeys.add(certs[i].getPublicKey());
            }

        } catch (CertificateEncodingException e) {
            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
            return false;
        } catch (IOException e) {
            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
            return false;
        } catch (SecurityException e) {
            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
            return false;
        } catch (RuntimeException e) {
            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
            return false;
        }

        return true;
    }
这里的数字证书机制与JAVA中的完全一致,所以看到很多方法都是来源于JDK的标准方法。
    private Certificate[] loadCertificates(StrictJarFile jarFile, ZipEntry je,
            byte[] readBuffer) {
        try {
            // We must read the stream for the JarEntry to retrieve
            // its certificates.
            InputStream is = new BufferedInputStream(jarFile.getInputStream(je));
            while (is.read(readBuffer, 0, readBuffer.length) != -1) {
                // not using
            }
            is.close();
            return je != null ? jarFile.getCertificates(je) : null;
        } catch (IOException e) {
            Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e);
        } catch (RuntimeException e) {
            Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e);
        }
        r

其实到这里还只是将签名信息获取出来,还没有真正进行验证以及处理跟其相关的权限分配和用户组等过程,那些都在接下来的安装过程中。

关于Andoid签名机制这个博客有非常详细的阐述:http://blog.csdn.net/jiangwei0910410003/article/details/50443505,大家可以详细的了解。

说完这个发现本篇篇幅太长了,其他的后面再慢慢叙述吧。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值