首页 > 代码库 > 《深入理解Android2》读书笔记(三)

《深入理解Android2》读书笔记(三)

接上篇《深入理解Android2》读书笔记(二)

PackageManagerService 

技术分享

PackageManagerService负责系统中Package的管理,应用程序的安装、卸载、信息查询等。

1.IPackageManager接口类中定义了服务端和客户端通信的业务函数,还定义了内部类Stub,该类从Binder派生并实现了IPackageManager接口。

2.PackageManagerService继承自IPackageManager.Stub类,由于Stub类从Binder派生,因此PackageManagerService将作为服务端与Binder通信。

3.Stub类中定义了一个内部类Proxy,该类有一个IBinder类型(实际类型为BinderProxy)的成员变量mRemote,mRemote用于和服务器PackageManagerService通信。

4.IPackageManager接口类似定义了许多业务函数,但是出于安全等方面的考虑,Android对外(即SDK)提供的只是一个子集,该子集被封装在抽象类PackageManager中。客户端一般通过Context的getPackageManager函数返回一个类型为PackageManager对象,该对象的实际类型是PackageManager的子类ApplicationPackageManager。这种基于接口编程的方式,虽然极大降低了模块之间的耦合性,却给代码分析带来了不小的麻烦。

5.ApplicationPackageManager类继承自PackageManager类。它并没有直接参与Binder通信,而是通过mPM成员变量指向一个IPackageManager.Stub.Proxy类型的对象。

public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    ServiceManager.addService("package", m);
    return m;
}

PKMS构造函数长达600多行,主要功能:扫描Android系统中几个目标文件夹中的APK,从而建立合适的数据结构以管理诸如Package信息、四大组件信息、权限信息等各种信息。PKMS将解析APK包中的AndroidManifest.xml,并根据其中声明的Activity标签来创建与此对应的对象加以保管。

以下分析构造函数

EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
        SystemClock.uptimeMillis());

if (mSdkVersion <= 0) {
    Slog.w(TAG, "**** ro.build.version.sdk not set!");
}

mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

Setting

SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
    SharedUserSetting s = mSharedUsers.get(name);
    if (s != null) {
        if (s.userId == uid) {
            return s;
        }
        PackageManagerService.reportSettingsProblem(Log.ERROR,
                "Adding duplicate shared user, keeping first: " + name);
        return null;
    }
    s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
    s.userId = uid;
    if (addUserIdLPw(uid, s, name)) {
        mSharedUsers.put(name, s);
        return s;
    }
    return null;
}

得到SharedUserSetting

技术分享

1.Setting类定义了一个mSharedUsers成员,它是一个HashMap,以字符串(如android.uid.system)为key,对应的Value是一个SharedUserSetting对象。

2.SharedUserSetting派生自GrantedPermission类,从GrantedPermissions类的命名可知,它和权限有关。SharedUserSetting定义了一个成员变量packages,类型为HashSet,用于保存声明了相同sharedUserId的Package的权限设置信息。

3.每个Package有自己的权限设置。权限的概念由PackageSetting类表达。该类继承自PackagesettingBase,而PackageSettingBase又继承自GrantedPermissions。

4.Settings中还有两个成员,一个是mUserIds,另一个是mOtherUserIds,这两位成员的类型分别是ArrayList和SparseArray。其目的是以UID为索引,得到对应的SharedUserSetting对象。在一般情况下,以索引获取数组元素的速度,比以key获取hashMap中元素的速度要快很多。

private boolean addUserIdLPw(int uid, Object obj, Object name) {
    if (uid > Process.LAST_APPLICATION_UID) {
        return false;
    }

    if (uid >= Process.FIRST_APPLICATION_UID) {
        int N = mUserIds.size();
        final int index = uid - Process.FIRST_APPLICATION_UID;
        while (index >= N) {
            mUserIds.add(null);
            N++;
        }
        if (mUserIds.get(index) != null) {
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate user id: " + uid
                    + " name=" + name);
            return false;
        }
        mUserIds.set(index, obj);
    } else {
        if (mOtherUserIds.get(uid) != null) {
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate shared id: " + uid
                            + " name=" + name);
            return false;
        }
        mOtherUserIds.put(uid, obj);
    }
    return true;
}

 

PKMS构造函数第一阶段的工作,就是扫描并解析XML文件,并将其中的信息保存到特定的数据结构中。

PKMS第二阶段的工作就是扫描系统中的APK了,由于需要逐个扫描文件,因此手机上装的程序越多,PKMS的工作量就越大,系统启动速度也就越慢。

PKMS将扫描以下几个目录:

1./system/frameworks:该目录中的文件都是系统库,例如framework.jar、services.jar、framework-res.apk.不过scanDirLI只扫描APK文件,所以framework-res.apk是该目录中唯一“受宠”的文件

2./system/app:该目录下全是默认的系统应用,例如Browser.apk、SettingsProvider.apk等。

3./vendor/app:该目录中的文件由厂商提供,即全是厂商特定的APK文件,目前市面上的厂商都把自己的应用放在/system/app目录下

private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
    final File[] files = dir.listFiles();
    if (ArrayUtils.isEmpty(files)) {
        Log.d(TAG, "No files in app dir " + dir);
        return;
    }

    if (DEBUG_PACKAGE_SCANNING) {
        Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
                + " flags=0x" + Integer.toHexString(parseFlags));
    }

    for (File file : files) {
        final boolean isPackage = (isApkFile(file) || file.isDirectory())
                && !PackageInstallerService.isStageName(file.getName());
        if (!isPackage) {
            // Ignore entries which are not packages
            continue;
        }
        try {
            scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                    scanFlags, currentTime, null);
        } catch (PackageManagerException e) {
            Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

            // Delete invalid userdata apps
            if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                    e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                if (file.isDirectory()) {
                    mInstaller.rmPackageDir(file.getAbsolutePath());
                } else {
                    file.delete();
                }
            }
        }
    }
}

 

scanPackageLI函数首先调用PackageParser对APK文件进行解析,PackageParse完成了从物理文件到对应数据结构的转换。PackageParser功能单一,就是解析AndroidManifest.xml中的各种标签,

 

《深入理解Android2》读书笔记(三)