01-APP的安装过程

Android应用程序安装有四种方式,分别如下:

  • 系统启动时安装,没有安装界面
  • 第三方应用安装,有安装界面,也是我们最熟悉的方式
  • ADB命令安装,没有安装界面
  • 通过Google Play市场安装,没有安装界面

虽然安装方式不同,但是最后四种方式都是通过PackageManagerService服务来完成应用程序的安装。而PackageManagerService服务则通过与Installd服务通信,发送具体的指令来执行应用程序的安装、卸载等工作。它们的关系如下图所示:

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

应用程序在安装时涉及到如下几个重要目录:

system/app 系统应用程序的目录
data/app 用户程序安装的目录
data/data 存放应用程序数据的目录
data/dalvik-cache 存放的是经过优化的dex文件

1.1 系统启动时安装

系统启动时安装APK的函数关系图如下:

1.1.1 PackageManagerService简介

    应用程序管理服务PackageManagerService是Android系统的核心服务之一,在系统启动的时候由SystemServer组件负责启动起来。PackageManagerService用于管理系统中的所有安装包信息以及应用程序的安装和卸载,但是实际应用程序的安装卸载并不是由PackageManagerService亲自完成,而是通过socket通信,PackageManagerService来访问installd服务来实现应用程序的安装和卸载。

    SystemServer关键代码如下:

    SystemServer.java位置:/frameworks/base/services/java/com/android/server/SystemServer.java。

public class SystemServer {
    ...
	/**
     * Called to initialize native system services.
    */
    private static native void nativeInit();
	public static void main(String[] args) {
	...
	System.loadLibrary("android_servers");

	Slog.i(TAG, "Entered the Android system server!");

	// Initialize native services.
	nativeInit();
	// This used to be its own separate thread, but now it is
	// just the loop we run on the main thread.
	ServerThread thr = new ServerThread();
	thr.initAndLoop();
	}
}

SystemServer在main函数中创建ServerThread类对象来启动PackageManagerService。关键代码如下:

class ServerThread {
	private static final String TAG = "SystemServer";
    ……
    ContentResolver mContentResolver;
    ……

    public void initAndLoop() {
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
        SystemClock.uptimeMillis());
        Looper.prepareMainLooper();

        ……
		boolean onlyCore = false;
        boolean firstBoot = false;
        ……
        try {
            Slog.i(TAG, "Display Manager");
            display = new DisplayManagerService(context, wmHandler);
            ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);

            Slog.i(TAG, "Telephony Registry");
            telephonyRegistry = new TelephonyRegistry(context);
            ServiceManager.addService("telephony.registry", telephonyRegistry);

            Slog.i(TAG, "Scheduling Policy");
            ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());

            AttributeCache.init(context);

            if (!display.waitForDefaultDisplay()) {
                reportWtf("Timeout waiting for default display to be initialized.",
                        new Throwable());
            }

            Slog.i(TAG, "Package Manager");
            // 处于加密状态时,紧解析核心应用
            String cryptState = SystemProperties.get("vold.decrypt");
            if (ENCRYPTING_STATE.equals(cryptState)) {
                Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
                onlyCore = true;
            } else if (ENCRYPTED_STATE.equals(cryptState)) {
                Slog.w(TAG, "Device encrypted - only parsing core apps");
                onlyCore = true;
            }

            pm = PackageManagerService.main(context, installer,
                    factoryTest != SystemServer.FACTORY_TEST_OFF,
                    onlyCore);
            
           ……
      }
}

initAndLoop函数中代码:“String cryptState = SystemProperties.get("vold.decrypt");”是获取Android磁盘加密状态。Android磁盘加密机制是从Android3.0引入的,详细可以参考:《Android安全架构深究》一书第10章设备安全的磁盘加密章节。在默认情况下,Android磁盘加密功能是关闭的,必须由用户手动打开或托管设备的设备策略打开。所以,默认情况下,传递给PackageManagerService.main函数的onlyCore参数为false。
接着,ServerThread调用PackageManagerService的静态函数main安装应用。
在ServerThread类中,除了启动PackageManagerService外,还启动其他系统服务,包括ActivityManagerService、WindowManagerService、AlarmManagerService等,详细见ServerThread类的源码,这里我们只关注PackageManagerService,下面的章节,我们就开始从PackageManagerService.main函数分析开始。

1.1.2 扫描关键目录

PackageManagerService位置:
/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
上述main函数定义如下:

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

main函数中创建PackageManagerService服务对象,并把服务添加到ServiceManager中。ServiceManager是Android系统Binder进程通信机制的守护进程,一直运行在后台。它主要负责管理系统中的Binder对象。

我们接着分析PackageManagerService类的构造函数,关键代码如下:

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
	synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            mHandlerThread.start();
            mHandler = new PackageHandler(mHandlerThread.getLooper());
            Watchdog.getInstance().addThread(mHandler, mHandlerThread.getName(),
                    WATCHDOG_TIMEOUT);

            File dataDir = Environment.getDataDirectory();
            mAppDataDir = new File(dataDir, "data");  
            mAppInstallDir = new File(dataDir, "app");  
            mAppLibInstallDir = new File(dataDir, "app-lib"); 
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();  
            mUserAppDataDir = new File(dataDir, "user");  
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); 
            ……
            mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
            mDalvikCacheDir = new File(dataDir, "dalvik-cache");
            ……
           // Find base frameworks (resource packages without code).
            mFrameworkInstallObserver = new AppDirObserver(
                frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
            mFrameworkInstallObserver.startWatching();
            //扫描”/system/framework”目录下的apk
            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED,
                    scanMode | SCAN_NO_DEX, 0);

            // Collected privileged system packages.
            File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            mPrivilegedInstallObserver = new AppDirObserver(
                    privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true);
            mPrivilegedInstallObserver.startWatching();
            //扫描”/system/priv-app”目录下的apk
            scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
                        | PackageParser.PARSE_IS_SYSTEM_DIR
                        | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);

            // Collect ordinary system packages.
            File systemAppDir = new File(Environment.getRootDirectory(), "app"); 
            mSystemInstallObserver = new AppDirObserver(
                systemAppDir.getPath(), OBSERVER_EVENTS, true, false);
            mSystemInstallObserver.startWatching();
            //扫描”/system/app”目录下的apk
            scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

            // Collect all vendor packages.
            File vendorAppDir = new File("/vendor/app");
            mVendorInstallObserver = new AppDirObserver(
                vendorAppDir.getPath(), OBSERVER_EVENTS, true, false);
            mVendorInstallObserver.startWatching();
            // vender目录其实连接到/system/vendor,实际扫描:/system/vendor/app目录下的apk
            scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

            if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
            mInstaller.moveFiles();

            // Prune any system packages that no longer exist.
            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
           //由上一部分内容知道,默认情况下磁盘加密状态关闭,即mOnlyCore状态false
           if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                mAppInstallObserver = new AppDirObserver(
                    mAppInstallDir.getPath(), OBSERVER_EVENTS, false, false);
                mAppInstallObserver.startWatching();
                //扫描”/data/app”目录下的apk
                scanDirLI(mAppInstallDir, 0, scanMode, 0);
    
                mDrmAppInstallObserver = new AppDirObserver(
                    mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false, false);
                mDrmAppInstallObserver.startWatching();
                //扫描”/data/app-private”目录下的apk
                scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                        scanMode, 0);

                ……
            } 
        ……..
}

如上代码,可以看到PackageManagerService在构造函数中,会扫描如下6个目录下的APK,保证这些APK的信息被预先加载进来。

/system/framework
/system/priv-app
/system/app
/system/vendor/app
/data/app
/data/app-private

/system/app目录存放的是系统自带的apk;
/data/app目录下存放的是用户安装的apk;
/system/priv-app目录下的应用是特权应用,这对这些应用富裕signatureOrSystem保护级别的权限,而不是所有/system下的应用
TOOD:查看Android安全架构深究
我们接着分析scanDirLI函数,其函数定义如下:

private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
    String[] files = dir.list();
    ……

    int i;
    for (i=0; i<files.length; i++) {
        File file = new File(dir, files[i]);
        //只扫描apk后缀的文件
        if (!isPackageFilename(files[i])) {
            // Ignore entries which are not apk's
            continue;
        }
        
        PackageParser.Package pkg = scanPackageLI(file,
                flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
       ……
    }
}

scanPackageLI函数主要扫描apk文件并返回解析后的PackageParser.Package对象。
scanPackageLI的关键代码:

private PackageParser.Package scanPackageLI(File scanFile,
    int parseFlags, int scanMode, long currentTime, UserHandle user) {
    ……
    String scanPath = scanFile.getPath();
    parseFlags |= mDefParseFlags;
    PackageParser pp = new PackageParser(scanPath);
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setOnlyCoreApps(mOnlyCore);
    //解析安装包
    final PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags);
    ……
    //保存解析后的安装包
    PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
            | SCAN_UPDATE_SIGNATURE, currentTime, user);
    ……
    return scannedPkg;
}

1.1.3 解析保存应用程序信息

查看parsePackage函数位置:frameworks/base/core/java/android/content/pm/PackageParser.java,其关键代码如下:

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

    mArchiveSourcePath = sourceFile.getPath();
    ……
	XmlResourceParser parser = null;
    AssetManager assmgr = null; //AssetManager用来管理包中的资源
    Resources res = null; //Resources就是资源,包括图片、xml等
    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);
            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);
    }
    ……
    Package pkg = null;
    Exception errorException = null;
    try {
        // XXXX todo: need to figure out correct configuration.
        pkg = parsePackage(res, parser, flags, errorText);
    } catch (Exception e) {
        errorException = e;
        mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
    }
    ……
    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;
}

上述函数主要是获取apk中的资源对象res和对Androidmanifest.xml文件进行格式化,并调用另一个parsePackage函数进一步做解析,这个才是Package真正生成的地方。
其函数关键代码如下:

private Package parsePackage(
    Resources res, XmlResourceParser parser, int flags, String[] outError)
    throws XmlPullParserException, IOException {
	    ……
	    
	    String pkgName = parsePackageName(parser, attrs, flags, outError);
	    ……

	    final Package pkg = new Package(pkgName);
	    ……
	    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) {
	        ……
	        pkg.mSharedUserId = str.intern();
	        pkg.mSharedUserLabel = sa.getResourceId(
	                com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
	    }
	    ……

	    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;
	    }
	    ……
	    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")) {
	            ……
	            if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
	                return null;
	            }
	        } 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")) {  
	            ......  
	        } else if (tagName.equals("original-package")) {  
	            ......  
	        } else if (tagName.equals("adopt-permissions")) {  
	            ......  
	        } else if (tagName.equals("uses-gl-texture")) {  
	            ......  
	        } else if (tagName.equals("compatible-screens")) {  
	            ......  
	        } else if (tagName.equals("eat-comment")) {  
	            ......  
	        } else if (RIGID_PARSER) {  
	            ......  
	        } else {  
	            ......  
	        }
	  ……
	  return pkg;
}

由上面的代码知道,该函数主要是解析安装包并根据AndroidManifest.xml文件的标签调用响应的解析函数进行解析,如:parseApplication、parseKeys、parsePermissionGroup、parsePermission、parseUsesPermission等等,并把解析后的信息保存到Package对象pkg中并返回。这里我们只看parseApplication解析函数,其他解析函数类似,具体细节可以查看frameworks/base/core/java/android/content/pm/PackageParser.java函数查看。

parseApplication关键代码如下:

private boolean parseApplication(Package owner, Resources res,
    XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
    throws XmlPullParserException, IOException {
        final ApplicationInfo ai = owner.applicationInfo;
        final String pkgName = owner.applicationInfo.packageName;
        ……
        ai.icon = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
        ai.logo = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
        ai.theme = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
        ai.descriptionRes = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
        ……
        ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName, str, outError); 
         ……
		ai.uiOptions = sa.getInt(com.android.internal.R.styleable.AndroidManifestApplication_uiOptions, 0);
		……
		while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
		      && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
		  	……
			String tagName = parser.getName();
			if (tagName.equals("activity")) {
			     Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
			              hardwareAccelerated);
			     ……
			     owner.activities.add(a);
			} else if (tagName.equals("receiver")) {
			     Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
			     ……
			     owner.receivers.add(a);
			} else if (tagName.equals("service")) {
			     Service s = parseService(owner, res, parser, attrs, flags, outError);
			     ……
				owner.services.add(s);
			} else if (tagName.equals("provider")) {
			    Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
			    ……
				owner.providers.add(p);
			} else if (tagName.equals("activity-alias")) {
			   Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
			    ……
				owner.activities.add(a);
			} else if (parser.getName().equals("meta-data")) {
			   ……
			} else if (tagName.equals("library")) {
			    ……
			} else if (tagName.equals("uses-library")) {
			   ……
			}else if (tagName.equals("uses-package")) {
			  ……
			}else{
			……
			}
		}//end of while
		return true;
}

parseApplication代码主要解析application标签下的子标签内容,并调用相应子标签对应的函数解析,如:parseActivity、parseService、parseProvider等,并将解析的数据保存到ApplicationInfo和Package对象里,同时检查application及其子标签的格式是否合法。

1.1.4 安装应用程序

到这里,我们再返回到1.1.2节中scanPackageLI完成解析后,调用了另外一个scanPackageLI函数,其定义如下:

private PackageParser.Package scanPackageLI(PackageParser.Package pkg,  
    int parseFlags, int scanMode, long currentTime) {  
    ......  
    synchronized (mPackages) {  
        ......  
        // Add the new setting to mPackages  
        mPackages.put(pkg.applicationInfo.packageName, pkg);  
        ......  
        int N = pkg.providers.size();  
        int i;  
        for (i=0; i<N; i++) {  
            PackageParser.Provider p = pkg.providers.get(i);  
            p.info.processName = fixProcessName(pkg.applicationInfo.processName,  
                p.info.processName, pkg.applicationInfo.uid);  
            mProvidersByComponent.put(new ComponentName(p.info.packageName,  
                p.info.name), p);  
            ......  
        }  

        N = pkg.services.size();  
        for (i=0; i<N; i++) {  
            PackageParser.Service s = pkg.services.get(i);  
            s.info.processName = fixProcessName(pkg.applicationInfo.processName,  
                s.info.processName, pkg.applicationInfo.uid);  
            mServices.addService(s);  

            ......  
        }  

        N = pkg.receivers.size();  
        r = null;  
        for (i=0; i<N; i++) {  
            PackageParser.Activity a = pkg.receivers.get(i);  
            a.info.processName = fixProcessName(pkg.applicationInfo.processName,  
                a.info.processName, pkg.applicationInfo.uid);  
            mReceivers.addActivity(a, "receiver");  
            ......  
        }  

        N = pkg.activities.size();  
        for (i=0; i<N; i++) {  
            PackageParser.Activity a = pkg.activities.get(i);  
            a.info.processName = fixProcessName(pkg.applicationInfo.processName,  
                a.info.processName, pkg.applicationInfo.uid);  
            mActivities.addActivity(a, "activity");  
            ......  
        }  
        ......  
    }  
    ...... 
    //这里主要是获取应用程序的安装目录,并保存到pkg.applicationInfo.dataDir中
    File dataPath;
    if (mPlatformPackage == pkg) {
        // The system package is special.
        dataPath = new File (Environment.getDataDirectory(), "system");
        pkg.applicationInfo.dataDir = dataPath.getPath();
    } else {
        dataPath = getDataPathForPackage(pkg.packageName, 0);

       ……

        if (dataPath.exists()) {
            //条件1:已经安装过,这里系统应用程序是已经安装了的,所以是执行此处的逻辑
            ……
            pkg.applicationInfo.dataDir = dataPath.getPath();
        } else {
            ……
            //条件2:createDataDirsLI才是实际进行安装的操作
            int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
                                       pkg.applicationInfo.seinfo);
            ……
            if (dataPath.exists()) {
                pkg.applicationInfo.dataDir = dataPath.getPath();
            } else {
                Slog.w(TAG, "Unable to create data directory: " + dataPath);
                pkg.applicationInfo.dataDir = null;
            }
        }
        ……
    }
    ……
    return pkg;  
}

这个函数主要是把前面解析安装包得到的activity、service、receiver、provider等信息保存到PackageParse.Package对象中。
该函数在解析过程中,如果应用程序第一次安装,则进入条件2的代码逻辑,并调用createDataDirsLI函数,这个函数才是真正安装操作的,安装程序并保存创建的程序目录。但是由于我们是系统启动方式,此时的应用程序都是已经安装了的,所以是进入条件1的代码逻辑。我们会在1.2节中讲手动安装apk的方式,此时进入的就是条件2的代码逻辑。这里我们假设是应用程序未安装,接着分析createDataDirsLI函数。
createDataDirsLI函数定义如下:

private int createDataDirsLI(String packageName, int uid, String seinfo) {
    int[] users = sUserManager.getUserIds();
    int res = mInstaller.install(packageName, uid, uid, seinfo);
    if (res < 0) {
        return res;
    }
    for (int user : users) {
        if (user != 0) {
            res = mInstaller.createUserData(packageName,
                    UserHandle.getUid(user, uid), user);
            if (res < 0) {
                return res;
            }
        }
    }
    return res;
}

可以看到该函数调用Installer类的install成员函数执行安装操作,安装成功后才调用createUserData函数创建应用程序的用户数据。这里我们主要分析Installer类的install成员函数细节。
Installer.java位置: frameworks/base/services/java/com/android/server/pm/Installer.java
install函数定义如下:

public int install(String name, int uid, int gid, String seinfo) {
    StringBuilder builder = new StringBuilder("install");
    builder.append(' ');
    builder.append(name);
    builder.append(' ');
    builder.append(uid);
    builder.append(' ');
    builder.append(gid);
    builder.append(' ');
    builder.append(seinfo != null ? seinfo : "!");
    return execute(builder.toString());
}

该函数将传递的参数组合成一个install的字符串命令,传递给execute函数执行。我们接着分析execute函数。
execute函数定义:

private int execute(String cmd) {
    String res = transaction(cmd);
    try {
        return Integer.parseInt(res);
    } catch (NumberFormatException ex) {
        return -1;
    }
}

该函数调用transaction执行命令。
transaction函数定义:

private synchronized String transaction(String cmd) {
    if (!connect()) {
        ……
    }

    if (!writeCommand(cmd)) {
        ……
    }
    ……
}

这里transaction实际是通过CS的通信方式发送指令给服务端进行具体的操作,包括安装卸载等。
connect是跟服务端进行连接,连接成功后才调用writeCommand函数将具体的命令cmd发送过去。
我们接着看connect函数实现,看它是跟那个服务通信。
connect函数定义:

private boolean connect() {
        if (mSocket != null) {
            return true;
        }
        Slog.i(TAG, "connecting...");
        try {
            mSocket = new LocalSocket();

            LocalSocketAddress address = new LocalSocketAddress("installd",
                    LocalSocketAddress.Namespace.RESERVED);

            mSocket.connect(address);

            mIn = mSocket.getInputStream();
            mOut = mSocket.getOutputStream();
        } catch (IOException ex) {
            disconnect();
            return false;
        }
        return true;
    }

writeCommand函数定义:

private boolean writeCommand(String _cmd) {
    byte[] cmd = _cmd.getBytes();
    int len = cmd.length;
    if ((len < 1) || (len > 1024))
        return false;
    buf[0] = (byte) (len & 0xff);
    buf[1] = (byte) ((len >> 8) & 0xff);
    try {
        mOut.write(buf, 0, 2);
        mOut.write(cmd, 0, len);
    } catch (IOException ex) {
        Slog.e(TAG, "write error");
        disconnect();
        return false;
    }
    return true;
}

到这里,我们就明了,原来PackageManagerService是跟Installd服务进行通信,通过给Installd服务进程发送具体指令进行安装卸载应用程序,,如上面安装程序操作则发送了一个”install”开头的字符串命令。并通过writeCommand函数发送给Installd服务端进程。
Installd服务进程是由init进程启动的,其源码位于:frameworks/base/cmds/installd。

仔细看installd.c源码,可以看到一个命令数组:

struct cmdinfo {
    const char *name;
    unsigned numargs;
    int (*func)(char **arg, char reply[REPLY_MAX]);
};

struct cmdinfo cmds[] = {
    //命令名称       参数个数  对应函数
    { "ping",                 0, do_ping },
    { "install",              4, do_install },
    { "dexopt",               3, do_dexopt },
    { "movedex",              2, do_move_dex },
    { "rmdex",                1, do_rm_dex },
    { "remove",               2, do_remove },
    { "rename",               2, do_rename },
    { "fixuid",               3, do_fixuid },
    { "freecache",            1, do_free_cache },
    { "rmcache",              2, do_rm_cache },
    { "getsize",              6, do_get_size },
    { "rmuserdata",           2, do_rm_user_data },
    { "movefiles",            0, do_movefiles },
    { "linklib",              3, do_linklib },
    { "mkuserdata",           3, do_mk_user_data },
    { "rmuser",               1, do_rm_user },
};

到这里,我们就明白了上面的install函数为什么构造一个带有”install”的字符串,其实是发送给Installd服务进程调用do_install执行安装操作。

public int install(String name, int uid, int gid, String seinfo) {
    StringBuilder builder = new StringBuilder("install");
    …
    return execute(builder.toString());
}

我们看do_install函数的实现:

static int do_install(char **arg, char reply[REPLY_MAX])
{
    return install(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); /* pkgname, uid, gid, seinfo */
}

这里它调用了commands.c文件的install函数:
install函数定义:

int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo)
{
    char pkgdir[PKG_PATH_MAX];  //程序目录路径最长256
    char libsymlink[PKG_PATH_MAX];
    char applibdir[PKG_PATH_MAX];
    struct stat libStat;
    // 权限判断
    if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
        ALOGE("invalid uid/gid: %d %d\n", uid, gid);
        return -1;
    }
    // 组合应用程序安装目录pkgdir=”/data/data/应用程序包名”
    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
        ALOGE("cannot create package path\n");
        return -1;
    }
    ……
    // 创建应用程序安装目录pkgdir=”/data/data/应用程序包名”
    if (mkdir(pkgdir, 0751) < 0) {
        ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
        return -1;
	}
	// 修改应用程序安装目录pkgdir=”/data/data/应用程序包名”权限
    if (chmod(pkgdir, 0751) < 0) {
        ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
        unlink(pkgdir);
        return -1;
    }
    ……
    return 0;
}

至此,我们就分析完系统启东市安装apk的完整流程

1.2 第三方应用安装

手动安装apk函数关系如下图:

1.2.1 调用PackageInstaller应用程序的PackageInstallActivity

我们看下PackageInstaller.apk的AndroidManifest.xml文件中

<activity android:configChanges="keyboardHidden|orientation|screenSize" android:excludeFromRecents="true" android:name=".PackageInstallerActivity">
    <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>

点击的时候发送” android.intent.action.INSTALL_PACKAGE”消息,调用起PackageInstaller.apk应用程序的PackageInstallerActivity处理并开始安装程序。
PackageInstallerActivity定义如下:

public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {
	private static final String TAG = "PackageInstaller";

	……
	@Override
	    protected void onCreate(Bundle icicle) {
	        super.onCreate(icicle);

	        // 获取Intent传递过来的数据
	        final Intent intent = getIntent();
	        mPackageURI = intent.getData();
	        mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
	        mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
	        mPm = getPackageManager();
	        …
	        initiateInstall();
	}
	……
}

我们看到PackageInstallerActivity接收到Intent后,获取其地址,并调用initiateInstall函数开始安装。
initiateInstall函数关键代码如下:

private void initiateInstall() {
    String pkgName = mPkgInfo.packageName;
    ……
    startInstallConfirm();
}

initiateInstall开始检查安装包是否安装过,并调用startInstallConfirm函数弹出选择对话框供用户选择是否安装。也就是下面我们常见的安装界面:

接着分析startInstallConfirm函数,关键代码如下:

private void startInstallConfirm() {
    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;
            }
        });
    }
}

startInstallConfirm初始化并显示安装界面,同时绑定确认和取消按钮事件。接下来,我们看onClick函数。
其关键代码如下:

public void onClick(View v) {
    if(v == mOk) {
        if (mOkCanInstall || mScrollView == null) {
            //确认安装,并调用另一个Activity处理
            ……
            newIntent.setClass(this, InstallAppProgress.class);
            ……
            startActivity(newIntent);
            finish();
        } 
        ……
    } else if(v == mCancel) {
        //取消安装
        ……
    }
}

这里面onClick函数发送Intent给InstallAppProgess类处理安装任务。

1.2.2 调用PackageInstaller应用程序的InstallAppProgess

我们查看InstallAppProgess类的onCreate函数。

@Override
public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    Intent intent = getIntent();
    mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
    mInstallFlowAnalytics = intent.getParcelableExtra(EXTRA_INSTALL_FLOW_ANALYTICS);
    mInstallFlowAnalytics.setContext(this);
    mPackageURI = intent.getData();

    final String scheme = mPackageURI.getScheme();
    if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
        mInstallFlowAnalytics.setFlowFinished(
                InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
        throw new IllegalArgumentException("unexpected scheme " + scheme);
    }

    initView();
}

onCreate获取传递过来的Intent并调用initView函数。
initView函数关键代码如下:

public void initView() {
    ……
    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 {
        //第一次安装
        pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
                installerPackageName, verificationParams, null);
    }
}

initView先是做一些布局初始化的工作,如果是第一次安装,则调用PackageManager类的installPackageWithVerificationAndEncryption函数完成安装。传递过去的第一个参数实际是安装包的路径,第二个参数则是一个应用程序安装的观察者。
这里PackageManager是抽象类,该函数的具体实现在PackageManagerService类里,所以实际上是调用PackageManagerService服务进行安装。

1.2.3 调用PackageManagerService服务的函数完成安装

跳转到PacakgeManagerService类,查看installPackageWithVerificationAndEncryption函数关键代码:

public void installPackageWithVerificationAndEncryption(Uri packageURI,
    IPackageInstallObserver observer, int flags, String installerPackageName,
    VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
    ……
    //该函数发送一个”INIT_COPY”的消息和一个InstallParams类对象给
	//PackageHandler类对象mHandler处理 
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
            verificationParams, encryptionParams, user);
    mHandler.sendMessage(msg);
}

我们跳转到PackageHandler类的消息处理函数handleMessage:

public void handleMessage(Message msg) {
    try {
        doHandleMessage(msg);
    } finally {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    }
}

该函数调用doHandleMessage进行消息处理,我们看看doHandleMessage接收到INIT_COPY消息后如何处理:

void doHandleMessage(Message msg) {
	switch (msg.what) {
	    case INIT_COPY: {
	        ……
	        if (!mBound) {
	           ……
	        } else {
	            mPendingInstalls.add(idx, params);
	            //发送MCS_BOUND消息
	            if (idx == 0) {
	                mHandler.sendEmptyMessage(MCS_BOUND);
	            }
	        }
	        break;
	    }
	 case MCS_BOUND: {
	        ……
	        if (mContainerService == null) {
	            ……
	        } else if (mPendingInstalls.size() > 0) {
	            HandlerParams params = mPendingInstalls.get(0);
	            if (params != null) {
	                if (params.startCopy()) {
	                    ……
	                    
	                }
	            }
	        }
	        ……
	    }
	……
	}
}

在接收到INIT_COPY消息后,mHandler又给自己发送MCS_BOUND消息。在该消息的处理过程中,调用HandlerParams类的startCopy函数。注意的是HandlerParams是抽象类,InstallParams继承了HandlerParams并实现下述3个抽象函数。后面分析到这3个函数,我们 就需要到HandleParams类下查找。

  • abstract void handleStartCopy() throws RemoteException;
  • abstract void handleServiceError();
  • abstract void handleReturnCode();

我们继续看startCopy函数的关键代码:

final boolean startCopy() {
    boolean res;
    try {
        ……

        if (++mRetries > MAX_RETRIES) {
            ……
            handleServiceError();
            return false;
        } else {
            handleStartCopy();
            res = true;
        }
    } catch (RemoteException e) {
        ……
    }
    handleReturnCode();
    return res;
}

startCopy调用了3个函数:handleServiceError、handleStartCopy和handleReturnCode。

handleServiceError是用来处理安装错误的,这里我们不展开讲;
handleStartCopy主要是获取安装包信息,并给两个关键变量完成赋值工作,分别是InstallArgs类对象mArgs和mRet变量。
handleStartCopy关键代码:

public void handleStartCopy() throws RemoteException {
    int ret = PackageManager.INSTALL_SUCCEEDED;
    ……

    final InstallArgs args = createInstallArgs(this);
    mArgs = args;
    ……
    mRet = ret;
}

handleReturnCode函数定义如下:

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

这里,我们看到handleReturnCode调用processPendingInstall并传递handleStartCopy赋值的两个参数mArgs和mRet。
processPendingInstall函数关键代码如下:

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, true, res);
                }
                args.doPostInstall(res.returnCode, res.uid);
            }
            ……
        }
    });
}

该函数启动了一个线程并调用installPackageLI安装。

private void installPackageLI(InstallArgs args,
        boolean newInstall, PackageInstalledInfo res) {
    ……
	if (replace) {
        replacePackageLI(pkg, parseFlags, scanMode, args.user,
                installerPackageName, res);
    } else {
        installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user,
                installerPackageName, res);
    }
    ……
}

这里,installPackageLI有两种操作,如果应用程序已经在,则调用replacePackageLI替换,如果是第一次安装,则调用installNewPackageLI函数,这里我们假设是第一次安装。
接下来分析installNewPackageLI函数的关键代码:

private void installNewPackageLI(PackageParser.Package pkg,
    int parseFlags, int scanMode, UserHandle user,
    String installerPackageName, PackageInstalledInfo res) {
	……
	PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
	        System.currentTimeMillis(), user);
	……
}

这里,我们又看到1.1分析中调用的scanPackageLI函数,后面的分析就跟1.1.4节一样了,这里就不重复讲。

1.3通过ADB命令安装

TODO:继续详细化ADB内容
ADB的执行机制其实是按照客户端、服务端的方式工作。
(1)客户端:adb或adb.exe,运行于PC段
(2)服务端:adbd,运行在手机端或模拟器等设备上的守护进程。该进程由init父进程创建。

通过ADB命令方式安装,其整体函数关系如下图所示:

首先,要想进一步了解ADB命令安装的工作方式,就需要我们进一步研究adb模块的源码。adb运行的入口在:
system\core\adb\adb.c,我们查看main函数定义:

int main(int argc, char **argv)
{
#if ADB_HOST //也就是我们的PC客户端
    adb_sysdeps_init();
    adb_trace_init();
    D("Handling commandline()\n");
    return adb_commandline(argc - 1, argv + 1); //运行PC段的adb/adb.exe可执行程序
#else
    /* If adbd runs inside the emulator this will enable adb tracing via
     * adb-debug qemud service in the emulator. */
    adb_qemu_trace_init();
    if((argc > 1) && (!strcmp(argv[1],"recovery"))) {
        adb_device_banner = "recovery";
        recovery_mode = 1;
    }

    start_device_log();
    D("Handling main()\n");
    return adb_main(0, DEFAULT_ADB_PORT); //运行在目标设备上的adbd可执行程序
#endif
}

接着分析adb_commandline函数的关键代码:

int adb_commandline(int argc, char **argv)
{
	……
	if(!strcmp(argv[0], "devices")) {….}
	if(!strcmp(argv[0], "connect")) {…}
	if(!strcmp(argv[0], "disconnect")) {…}
	if (!strcmp(argv[0], "emu")) {…}
	if(!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {…}
	if(!strcmp(argv[0], "kill-server")) {…}
	if(!strcmp(argv[0], "sideload")) {…}
	if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot")
	            || !strcmp(argv[0], "reboot-bootloader")
	            || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb")
	            || !strcmp(argv[0], "root")) {…}
	……
	if(!strcmp(argv[0], "install")) {
	        if (argc < 2) return usage();
	        return install_app(ttype, serial, argc, argv);
	    }

	    if(!strcmp(argv[0], "uninstall")) {
	        if (argc < 2) return usage();
	        return uninstall_app(ttype, serial, argc, argv);
	    }
	……
}

adb_commandline其实就是处理我们输入的命令行,调用响应的函数工作。

我们看到当收到”install”指令时,调用install_app函数执行安装工作。

int install_app(transport_type transport, char* serial, int argc, char** argv)
{
	……
	apk_file = argv[file_arg];
	……
	err = do_sync_push(apk_file, apk_dest, verify_apk);
	……
	pm_command(transport, serial, argc, argv);
	……
}

install_app再安装前,调用了pm_command函数做一些命令行修改,实际就是给命令行字符串添加”shell:pm”指令,这里pm其实就是pm.jar,Package管理器。pm.jar主要执行终端下传递过来的pm命令。类似的jar包还有am.jar,Activity管理器等,它们都位于\system\framework目录下。
也就是说,adb命令安装方式实际上是调用pm包管理器来对安装包进行安装卸载等工作。
pm.jar包的关键方法:Pm.java,其文件位置:com/android/commands/pm/Pm.java。
该文件的入口函数为:main函数:

public final class Pm {
	IPackageManager mPm;
	IUserManager mUm;
	    ……

	    public static void main(String[] args) {
	        new Pm().run(args);
	}
	……
}

main函数创建Pm类对象并调用run函数,其关键代码如下:

public void run(String[] args) {
    ……
    mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
    mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    ……

    if ("list".equals(op)) {
        runList();
        return;
    }

    if ("path".equals(op)) {
        runPath();
        return;
    }

    if ("dump".equals(op)) {
        runDump();
        return;
    }

    if ("install".equals(op)) {
        runInstall();
        return;
    }

    if ("uninstall".equals(op)) {
        runUninstall();
        return;
    }
    ……
}

我们分析下面这行代码:
TODO: 学习了解下Binder机制
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"))。
Stub是接口IPackageManager的静态抽象类,asInterface是返回IPackageManager代理的静态方法。
我们在返回看PackageManagerService.java的定义:

public class PackageManagerService extends IPackageManager.Stub {
……
}
所以,IPackageManager类对象mPm的函数实际上是调用PackageManagerService类对应的函数。

安装操作会调用runInstall函数。
runInstall函数关键代码如下:


/* Called when a downloaded package installation has been confirmed by the user */
public void installPackage(
        final Uri packageURI, final IPackageInstallObserver observer, final int flags) {
    installPackage(packageURI, observer, flags, null);
}

通过函数的注释我们可以知道这个是通过网络下载的方式安装APK。
该函数调用另一个installPackage函数:

public void installPackage(
    final Uri packageURI, final IPackageInstallObserver observer, final int flags,
    final String installerPackageName) {
    installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,
            null, null);
}

该函数调用installPackageWithVerification:

@Override
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
        int flags, String installerPackageName, Uri verificationURI,
        ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
    VerificationParams verificationParams = new VerificationParams(verificationURI, null, null,
            VerificationParams.NO_UID, manifestDigest);
    installPackageWithVerificationAndEncryption(packageURI, observer, flags,
            installerPackageName, verificationParams, encryptionParams);
}

到这里,我们看到installPackageWithVerification调用了installPackageWithVerificationAndEncryption函数进行安装,该函数我们在1.2.3节中已经分析,这里就不做展开。下面附上通过该方式安装应用程序的函数关系图:

 

1.5 总结

posted @ 2022-11-07 16:14  Domefy  阅读(1473)  评论(0编辑  收藏  举报