Android應用程序安裝和顯示

在Android啓動的過程當中,會掃描系統中特定的目錄,以便對保存在裏面的應用程序進行安裝,例如/system/app,/data/app。這個主要是靠PackageManagerService來實現,PackageManagerService在安裝一個應用程序的過程當中主要作兩件事,一、解析這個應用程序的配置文件AndroidManifest.xml 以便獲取安裝信息,二、爲這個應用程序分配Linux用戶ID和Linux用戶組ID,以便他能夠運行在系統中得到權限java

    Android系統中最後一步是啓動一個HOME程序,用來顯示系統中已經安裝的應用程序,Android提供原聲的程序是Launcher,應用程序在啓動中,會請求PackageManagerService提供安裝的應用程序信息,再將程序信息封裝成一個快捷圖標在屏幕中,這樣用戶就能夠根據圖標來啓動對應的程序,
android

    PackageManagerService在安裝一個應用程序的時候,對AndroidManifest.xml進行解析,獲取到組件信息,Android系統的每個應用程序都有一個Linux用戶ID,這些ID是有PackageManagerService負責分配的,PackageManagerService在安裝一個應用程序的時候,若是發現沒有其餘應用程序共享一個Linux用戶ID,那麼就會爲它分配惟一的Linux用戶ID,這樣能夠得到所需的權限,有了用戶ID以外還有若干個Linux用戶組ID,以便得到更多的資源,Linux用戶組ID也是PackageManagerService來負責分配的,shell

在PackageManagerService.java中app

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

首先將PackageManagerService啓動起來,而後註冊到ServiceManager中,這樣系統經過ServiceManager來獲取安裝服務。ide

 public PackageManagerService(Context context, boolean factoryTest, boolean onlyCore) {
        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;
        mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
        mMetrics = new DisplayMetrics();
        mSettings = new Settings();
        mSettings.addSharedUserLPw("android.uid.system",
                Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
        mSettings.addSharedUserLPw("android.uid.phone",
                MULTIPLE_APPLICATION_UIDS
                        ? RADIO_UID : FIRST_APPLICATION_UID,
                ApplicationInfo.FLAG_SYSTEM);
        mSettings.addSharedUserLPw("android.uid.log",
                MULTIPLE_APPLICATION_UIDS
                        ? LOG_UID : FIRST_APPLICATION_UID,
                ApplicationInfo.FLAG_SYSTEM);
        mSettings.addSharedUserLPw("android.uid.nfc",
                MULTIPLE_APPLICATION_UIDS
                        ? NFC_UID : FIRST_APPLICATION_UID,
                ApplicationInfo.FLAG_SYSTEM);

        String separateProcesses = SystemProperties.get("debug.separate_processes");
        if (separateProcesses != null && separateProcesses.length() > 0) {
            if ("*".equals(separateProcesses)) {
                mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
                mSeparateProcesses = null;
                Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
            } else {
                mDefParseFlags = 0;
                mSeparateProcesses = separateProcesses.split(",");
                Slog.w(TAG, "Running with debug.separate_processes: "
                        + separateProcesses);
            }
        } else {
            mDefParseFlags = 0;
            mSeparateProcesses = null;
        }

        mInstaller = new Installer();

        WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Display d = wm.getDefaultDisplay();
        d.getMetrics(mMetrics);

        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            mHandlerThread.start();
            mHandler = new PackageHandler(mHandlerThread.getLooper());

            File dataDir = Environment.getDataDirectory();//得到系統的數據目錄/data
            mAppDataDir = new File(dataDir, "data");
            mUserAppDataDir = new File(dataDir, "user");
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");//得到的是/data/app-private目錄,主要是受DRM保護的私有應用程序,/data/app下面是本身安裝的應用程序。

            mUserManager = new UserManager(mInstaller, mUserAppDataDir);

            readPermissions();

            mRestoredSettings = mSettings.readLPw();//恢復上一次應用程序的安裝信息,接下來就掃描和安裝保存在特定目錄的應用程序
            long startTime = SystemClock.uptimeMillis();

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);

            // Set flag to monitor and not change apk file paths when
            // scanning install directories.
            int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX;
            if (mNoDexOpt) {
                Slog.w(TAG, "Running ENG build: no pre-dexopt!");
                scanMode |= SCAN_NO_DEX;
            }

            final HashSet<String> libFiles = new HashSet<String>();

            mFrameworkDir = new File(Environment.getRootDirectory(), "framework");//得到的是/system/framework目錄
            mDalvikCacheDir = new File(dataDir, "dalvik-cache");

            boolean didDexOpt = false;

            /**
             * Out of paranoia, ensure that everything in the boot class
             * path has been dexed.
             */
            String bootClassPath = System.getProperty("java.boot.class.path");
            if (bootClassPath != null) {
                String[] paths = splitString(bootClassPath, ':');
                for (int i=0; i<paths.length; i++) {
                    try {
                        if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
                            libFiles.add(paths[i]);
                            mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
                            didDexOpt = true;
                        }
                    } catch (FileNotFoundException e) {
                        Slog.w(TAG, "Boot class path not found: " + paths[i]);
                    } catch (IOException e) {
                        Slog.w(TAG, "Cannot dexopt " + paths[i] + "; is it an APK or JAR? "
                                + e.getMessage());
                    }
                }
            } else {
                Slog.w(TAG, "No BOOTCLASSPATH found!");
            }

            /**
             * Also ensure all external libraries have had dexopt run on them.
             */
            if (mSharedLibraries.size() > 0) {
                Iterator<String> libs = mSharedLibraries.values().iterator();
                while (libs.hasNext()) {
                    String lib = libs.next();
                    try {
                        if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
                            libFiles.add(lib);
                            mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
                            didDexOpt = true;
                        }
                    } catch (FileNotFoundException e) {
                        Slog.w(TAG, "Library not found: " + lib);
                    } catch (IOException e) {
                        Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
                                + e.getMessage());
                    }
                }
            }

            // Gross hack for now: we know this file doesn't contain any
            // code, so don't dexopt it to avoid the resulting log spew.
            libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");

            /**
             * And there are a number of commands implemented in Java, which
             * we currently need to do the dexopt on so that they can be
             * run from a non-root shell.
             */
            String[] frameworkFiles = mFrameworkDir.list();
            if (frameworkFiles != null) {
                for (int i=0; i<frameworkFiles.length; i++) {
                    File libPath = new File(mFrameworkDir, frameworkFiles[i]);
                    String path = libPath.getPath();
                    // Skip the file if we alrady did it.
                    if (libFiles.contains(path)) {
                        continue;
                    }
                    // Skip the file if it is not a type we want to dexopt.
                    if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
                        continue;
                    }
                    try {
                        if (dalvik.system.DexFile.isDexOptNeeded(path)) {
                            mInstaller.dexopt(path, Process.SYSTEM_UID, true);
                            didDexOpt = true;
                        }
                    } catch (FileNotFoundException e) {
                        Slog.w(TAG, "Jar not found: " + path);
                    } catch (IOException e) {
                        Slog.w(TAG, "Exception reading jar: " + path, e);
                    }
                }
            }

            if (didDexOpt) {
                // If we had to do a dexopt of one of the previous
                // things, then something on the system has changed.
                // Consider this significant, and wipe away all other
                // existing dexopt files to ensure we don't leave any
                // dangling around.
                String[] files = mDalvikCacheDir.list();
                if (files != null) {
                    for (int i=0; i<files.length; i++) {
                        String fn = files[i];
                        if (fn.startsWith("data@app@")
                                || fn.startsWith("data@app-private@")) {
                            Slog.i(TAG, "Pruning dalvik file: " + fn);
                            (new File(mDalvikCacheDir, fn)).delete();
                        }
                    }
                }
            }

            // Find base frameworks (resource packages without code).
            mFrameworkInstallObserver = new AppDirObserver(
                mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
            mFrameworkInstallObserver.startWatching();
            scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanMode | SCAN_NO_DEX, 0);
            
            // Collect all system packages.
            mSystemAppDir = new File(Environment.getRootDirectory(), "app");//得到的是/system/app目錄
            mSystemInstallObserver = new AppDirObserver(
                mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
            mSystemInstallObserver.startWatching();
            scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
            
            // Collect all vendor packages.
            mVendorAppDir = new File("/vendor/app");//得到的是/vendor/app裏面主要是設備廠家提供的應用程序。
            mVendorInstallObserver = new AppDirObserver(
                mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
            mVendorInstallObserver.startWatching();
            scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);//scanDieLI用來安裝裏面的應用程序。

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

            // Prune any system packages that no longer exist.
            if (!mOnlyCore) {
                Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                while (psit.hasNext()) {
                    PackageSetting ps = psit.next();
                    if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0
                            && !mPackages.containsKey(ps.name)
                            && !mSettings.mDisabledSysPackages.containsKey(ps.name)) {
                        psit.remove();
                        String msg = "System package " + ps.name
                                + " no longer exists; wiping its data";
                        reportSettingsProblem(Log.WARN, msg);
                        mInstaller.remove(ps.name, 0);
                        mUserManager.removePackageForAllUsers(ps.name);
                    }
                }
            }
            
            mAppInstallDir = new File(dataDir, "app");
            //look for any incomplete package installations
            ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
            //clean up list
            for(int i = 0; i < deletePkgsList.size(); i++) {
                //clean up here
                cleanupInstallFailedPackage(deletePkgsList.get(i));
            }
            //delete tmp files
            deleteTempPackageFiles();

            if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                mAppInstallObserver = new AppDirObserver(
                    mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
                mAppInstallObserver.startWatching();
                scanDirLI(mAppInstallDir, 0, scanMode, 0);
    
                mDrmAppInstallObserver = new AppDirObserver(
                    mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
                mDrmAppInstallObserver.startWatching();
                scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                        scanMode, 0);
            } else {
                mAppInstallObserver = null;
                mDrmAppInstallObserver = null;
            }

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
            Slog.i(TAG, "Time to scan packages: "
                    + ((SystemClock.uptimeMillis()-startTime)/1000f)
                    + " seconds");

            // If the platform SDK has changed since the last time we booted,
            // we need to re-grant app permission to catch any new ones that
            // appear.  This is really a hack, and means that apps can in some
            // cases get permissions that the user didn't initially explicitly
            // allow...  it would be nice to have some better way to handle
            // this situation.
            final boolean regrantPermissions = mSettings.mInternalSdkPlatform
                    != mSdkVersion;
            if (regrantPermissions) Slog.i(TAG, "Platform changed from "
                    + mSettings.mInternalSdkPlatform + " to " + mSdkVersion
                    + "; regranting permissions for internal storage");
            mSettings.mInternalSdkPlatform = mSdkVersion;
            
            updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);

            // can downgrade to reader
            mSettings.writeLPr();

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

            // Now after opening every single application zip, make sure they
            // are all flushed.  Not really needed, but keeps things nice and
            // tidy.
            Runtime.getRuntime().gc();

            mRequiredVerifierPackage = getRequiredVerifierLPr();
        } // synchronized (mPackages)
        } // synchronized (mInstallLock)

未完善,oop