需求

1,实现预置APP,在系统恢复出厂设置后能够依然存在
2,能够实现ota增量升级,如果用户当前的应用和ota中带的应用相同且用户版本的高则当前用户版本为准
3,被用户手动卸载掉的app在ota升级之后依然不出现
4,内置app的目录可以放在system和vendor目录下以防空间不够的问题

PackageManagerService

PackageManagerService主要负责Android系统的应用管理,它提供对各种APK文件的安装、卸载、优化和查询服务。
PackageManagerService在系统启动时会扫描所有存在的APK文件和Jar包信息,并将它们读取、保存起来;这样系统在运行时,就能很快的查询到各种应用和相关组件的信息。
预置app的原理就是在PackageManagerService扫描当前系统app之前将需要内置的app放到特定的目录,所以了解PackageManagerService的工作流程是最关键的。

PackageManagerService启动流程

不走流程 直接上图

android 预装应用和系统应用 安卓系统预装软件_构造函数


PackageManagerService 作为系统的核心服务,由 SystemServer 创建,SystemServer 调用了 PackageManagerService 的 main() 创建 PackageManagerService 实例

SystemServer.java

public static void main(String[] args) {
        new SystemServer().run();
    }
// Start services.
    try {
        traceBeginAndSlog("StartServices");
        startBootstrapServices();
        startCoreServices();
        startOtherServices();
        SystemServerInitThreadPool.shutdown();
    } catch (Throwable ex) {
        Slog.e("System", "******************************************");
        Slog.e("System", "************ Failure starting system services", ex);
        throw ex;
    } finally {
        traceEnd();
    }

startBootstrapServices()

// Start the package manager.
        if (!mRuntimeRestart) {
            MetricsLogger.histogram(null, "boot_package_manager_init_start",
                    (int) SystemClock.elapsedRealtime());
        }
        traceBeginAndSlog("StartPackageManagerService");
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();
        traceEnd();
        if (!mRuntimeRestart && !isFirstBootOrUpgrade()) {
            MetricsLogger.histogram(null, "boot_package_manager_init_ready",
                    (int) SystemClock.elapsedRealtime());
        }
        // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename
        // A/B artifacts after boot, before anything else might touch/need them.
        // Note: this isn't needed during decryption (we don't have /data anyways).
        if (!mOnlyCore) {
            boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
                    false);
            if (!disableOtaDexopt) {
                traceBeginAndSlog("StartOtaDexOptService");
                try {
                    OtaDexoptService.main(mSystemContext, mPackageManagerService);
                } catch (Throwable e) {
                    reportWtf("starting OtaDexOptService", e);
                } finally {
                    traceEnd();
                }
            }
        }

PackageManagerService.java

public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();

        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }

PackageManagerService构造函数

PMS构造函数工作是扫描 Android 系统中几个目标文件夹中的 APK,从而建立合适的数据结构来管理各种信息,如:Package 信息、四大组件信息、权限信息等

1,Settings

synchronized (mPackages) {
            // Expose private service for system components to use.
            LocalServices.addService(
                    PackageManagerInternal.class, new PackageManagerInternalImpl());
            sUserManager = new UserManagerService(context, this,
                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
            mPermissionManager = PermissionManagerService.create(context,
                    new DefaultPermissionGrantedCallback() {
                        @Override
                        public void onDefaultRuntimePermissionsGranted(int userId) {
                            synchronized(mPackages) {
                                mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
                            }
                        }
                    }, mPackages /*externalLock*/);
            mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
            mSettings = new Settings(mPermissionManager.getPermissionSettings(), 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);
        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

uid值的定义
Process.java

//系统进程使用的UID/GID,值为1000
   publicstatic final int SYSTEM_UID = 1000;
   //Phone进程使用的UID/GID,值为1001
   publicstatic final int PHONE_UID = 1001;
   //shell进程使用的UID/GID,值为2000
   publicstatic final int SHELL_UID = 2000;
   //使用LOG的进程所在的组的UID/GID为1007
   publicstatic final int LOG_UID = 1007;
   //供WIF相关进程使用的UID/GID为1010
   publicstatic final int WIFI_UID = 1010;
  //mediaserver进程使用的UID/GID为1013
   publicstatic final int MEDIA_UID = 1013;
   //设置能读写SD卡的进程的GID为1015
   publicstatic final int SDCARD_RW_GID = 1015;
   //NFC相关的进程的UID/GID为1025
   publicstatic final int NFC_UID = 1025;
   //有权限读写内部存储的进程的GID为1023
   publicstatic final int MEDIA_RW_GID = 1023;
   //第一个应用Package的起始UID为10000
   publicstatic final int FIRST_APPLICATION_UID = 10000;
   //系统所支持的最大的应用Package的UID为99999
   publicstatic final int LAST_APPLICATION_UID = 99999;
   //和蓝牙相关的进程的GID为2000
   publicstatic final int BLUETOOTH_GID = 2000;

android 预装应用和系统应用 安卓系统预装软件_android 预装应用和系统应用_02

PMS构造函数的时候会先new一个Settings类来建立与某些系统配置文件、目录之间的关联。

public final class Settings {
    ... ...
 
    Settings(PermissionSettings permissions, Object lock) {
        this(Environment.getDataDirectory(), permissions, lock);
    }
 
    Settings(File dataDir, PermissionSettings permission, Object lock) {
        mLock = lock;
        mPermissions = permission;
        mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
 
        // 创建指向 /data/system/ 目录的 File
        mSystemDir = new File(dataDir, "system");
        // 创建目录
        mSystemDir.mkdirs();
 
        FileUtils.setPermissions(mSystemDir.toString(),
                  FileUtils.S_IRWXU|FileUtils.S_IRWXG
                  |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                  -1, -1);
 
        // 用于描述系统所安装的 Package 信息
        mSettingsFilename = new File(mSystemDir, "packages.xml");
 
        // packages.xml的备份信息
        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
 
        // 保存系统中存在的所有非系统自带的 APK 信息,即 UID 大于 10000 的 apk
        mPackageListFilename = new File(mSystemDir, "packages.list");
        FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
 
        // sdcardfs 相关的文件
        final File kernelDir = new File("/config/sdcardfs");
        mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
 
        // 记录系统中被强制停止运行的 App 信息,如有 App 被强制停止运行,会将一些信息记录到该文件中
        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        
        // packages-stopped.xml 的备份信息
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
    }

上面源码中涉及到 5 个文件:

✨ packages.xml: PMS 扫描完目标文件夹后,会创建packages.xml。当系统进行程序安装、卸载和更新等操作时,均会更新该文件;
       ✨ packages-backup.xml:packages.xml 文件的备份;

       ✨ packages.list:用于描述系统中存在的所有非系统自带的 APK 信息。当这些 APK 有变化时,PKMS就会更新该文件;

       ✨ packages-stopped.xml:记录被用户强行停止的应用的 Package 信息(例如,从设置进入某个应用,然后点击强行停止,那么应用的Package信息就会被记录);

       ✨ packages-stopped-back.xml:packages-stopped.xml 文件的备份。
       
       上面的介绍中涉及到了两个 back-up 文件,它们是做什么的呢?其实 Android 系统在修改 packages.xml、packages-stopped.xml 之前,会先对它们进行备份。当对它们的修改操作正常完成,则会删掉备份的文件。如果在修改过程中系统出现问题重启了,会再次去读取这两个文件;如果此时发现它们的备份文件还存在,则说明上一次对两份文件的修改操作发生了异常,这两份文件的内容可能已经不准确了,这时系统会去使用之前备份的文件的内容。

packages.xml

<package name="com.xp.browser" codePath="/system/app/xpbrowser" nativeLibraryPath="/system/app/xpbrowser/lib" primaryCpuAbi="arm64-v8a" publicFlags="945339973" privateFlags="0" ft="16c1abf5ae0" it="16c1abf5ae0" ut="16c1abf5ae0" version="601600" userId="10101" isOrphaned="true">
						<sigs count="1" schemeVersion="1">
							<cert index="8" key="3082036f30820257a0030201020204216b0842300d06092a864886f70d01010b05003068310b30090603550406130238363110300e060355040813076c696579696e673111300f060355040713087368656e7a68656e3110300e060355040a13076c696579696e673110300e060355040b13076c696579696e673110300e060355040313076c696579696e67301e170d3136303731393038353831355a170d3431303731333038353831355a3068310b30090603550406130238363110300e060355040813076c696579696e673111300f060355040713087368656e7a68656e3110300e060355040a13076c696579696e673110300e060355040b13076c696579696e673110300e060355040313076c696579696e6730820122300d06092a864886f70d01010105000382010f003082010a0282010100e85892ff05c9bba893bababe5b5993e2f89fdd66f6a07b9e5e42038142ca71eaf8d8a3c951821298224c227bd5b92c5e544ed69789eaaf43335312cab857ce0d6f40cdd2a8361a314bb1944a5a462719a1e3370a7fb8b22ee60d883f6e006d43376e4e2d1f63f6bd489688c500be2329be9663a24b205fcf7d6e630f6f30b77dcadbd845f2462a0a98dfff00b6f56b019aeef3b3b21d98eaac453330c17ec8d25c7022494b55f7bca50953c69374065b3f5d53f4719449d0e4b66bbf1f781cebeede4bde06cd956da1cd4a719cd27987ebaa9b39ab4741d02d297e02d9572d6ebc86cc1d3c95419572ffc332d45f6225775b31c4b65619d393c626f981b0c61f0203010001a321301f301d0603551d0e04160414e034d4ba9946d12fbb3e15f6db370bf983a8c153300d06092a864886f70d01010b050003820101005558409dd627bf1eaad4731ac39fa71e9fe554996dffd6388abc88e018e7046b514557b708197bdade44c80ff6a96d429f7fd935035eecfe7ecdcbc1f9505e4fc791570ebb71b4509adccb27057c96c9baa6991bacd514111492321322fc91330b6f9e55e927b2e1f45b0d95c50e4c628a9403bbd407a1d5ae83afa42ca6bb1c81796e77bdc68d260dcb716771003e5fce2647b83e48f1f6450d10e80793e83e7d2783e8eedc83cee85e788c0b5222e44453eba37811ad75356e73b76e1146bc9d12e58d698275ac8fe799ffccd0afc9dc8e9359f3ea9f8014f9f5fd5db7a30f0a18482808a72486cf4ccd3c2c2218a1d3d54e20773ffc291ed3fc9798ce0124" />
						</sigs>
						<perms>
							<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
							<item name="android.permission.RESTART_PACKAGES" granted="true" flags="0" />
							<item name="android.permission.MODIFY_AUDIO_SETTINGS" granted="true" flags="0" />
							<item name="android.permission.SYSTEM_ALERT_WINDOW" granted="true" flags="0" />
							<item name="android.permission.NFC" granted="true" flags="0" />
							<item name="android.permission.CHANGE_NETWORK_STATE" granted="true" flags="0" />
							<item name="android.permission.FOREGROUND_SERVICE" granted="true" flags="0" />
							<item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
							<item name="android.permission.EXPAND_STATUS_BAR" granted="true" flags="0" />
							<item name="android.permission.GET_TASKS" granted="true" flags="0" />
							<item name="android.permission.INTERNET" granted="true" flags="0" />
							<item name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" granted="true" flags="0" />
							<item name="android.permission.CHANGE_WIFI_STATE" granted="true" flags="0" />
							<item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
							<item name="android.permission.DISABLE_KEYGUARD" granted="true" flags="0" />
							<item name="android.permission.SET_WALLPAPER" granted="true" flags="0" />
							<item name="android.permission.SET_WALLPAPER_HINTS" granted="true" flags="0" />
							<item name="android.permission.VIBRATE" granted="true" flags="0" />
							<item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
							<item name="com.android.launcher.permission.INSTALL_SHORTCUT" granted="true" flags="0" />
							<item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
						</perms>
					<proper-signing-keyset identifier="12" />
					</package>

ft表示apk文件上次被更改的时间,it表示app第一次安装的时间,ut表示app上次被更新时间
关于package.xml更详细的说明请参考:

2,SystemConfig.java

SystemConfig通过readPermissions()读取指定/system/etc/、/oem/etc/等目录下的permission xml文件,解析其中的内容并转换为特定的数据结构

SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);

        // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);

        // Vendors are only allowed to customze libs, features and privapp permissions
        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
            // For backward compatibility
            vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
        }
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);

        // Allow ODM to customize system configs as much as Vendor, because /odm is another
        // vendor partition other than /vendor.
        int odmPermissionFlag = vendorPermissionFlag;
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);

        // Allow OEM to customize features and OEM permissions
        int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);

        // Allow Product to customize system configs around libs, features, permissions and apps
        int productPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
                ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS;
        readPermissions(Environment.buildPath(
                Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag);
    }
frameworks/base/core/java/com/android/server/SystemConfig.java
    
void readPermissions(File libraryDir, int permissionFlag) {
    ... ...
 
    // Iterate over the files in the directory and scan .xml files
    File platformFile = null;
    for (File f : libraryDir.listFiles()) {
        // We'll read platform.xml last
        // 处理该目录下的非 platform.xml 文件
        if (f.getPath().endsWith("etc/permissions/platform.xml")) {
            platformFile = f;
            continue;
        }
        ... ...
            
        // 调用 readPermissionsFromXml 解析此 XML 文件
        readPermissionsFromXml(f, permissionFlag);
    }
 
    // Read platform permissions last so it will take precedence
    if (platformFile != null) {
        // 不知道你有没有发现,platform.xml文件的解析优先级最高!
        readPermissionsFromXml(platformFile, permissionFlag);
    }
}

platform.xml

android 预装应用和系统应用 安卓系统预装软件_java_03


plateform.xml中出现的标签种类则较为多样,它们的含义分别是:

<permission >标签:把属性name所描述的权限赋予给<group>标签中属性gid所表示的用户组
<assign-permission>标签:把属性name所描述的权限赋予给uid属性所表示的用户
<library>标签:除framework中动态库以外的,所有系统会自动加载的动态库
最后将上面xml解析出来的数据做如下存储:

<group>标签gid属性的值会存放在mGlobalGids数组中;
<permission>标签,解析得到的值会存放在mPermissions集合中;
<assign-permission>标签解析得到的值会存放在mSystemPermissions中;
<library>标签解析得到的值会存放在mSharedLibraries中;

3 mFirstBoot

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

读取packages.xml的内容,如果读取不到packages.xml则认为是首次开机, 并对mSettings::mPackages等成员进行赋值;packages.xml文件中的内容是上一次扫描apk目录的结果;当前这一次扫描的结果是保存在PackageManagerService::mPackages列表中

4 scanDirTracedLI
扫描系统App

scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);
            scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRODUCT,
                    0);

            mParallelPackageParserCallback.findStaticOverlayPackages();

            // Find base frameworks (resource packages without code).
            scanDirTracedLI(frameworkDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_NO_DEX
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect privileged system packages.
            final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            scanDirTracedLI(privilegedAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect ordinary system packages.
            final File systemAppDir = new File(Environment.getRootDirectory(), "app");
            scanDirTracedLI(systemAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM,
                    0);

            // Collect privileged vendor packages.
            File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
            try {
                privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            scanDirTracedLI(privilegedVendorAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect ordinary vendor packages.
            File vendorAppDir = new File(Environment.getVendorDirectory(), "app");
            try {
                vendorAppDir = vendorAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            scanDirTracedLI(vendorAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);

            // Collect privileged odm packages. /odm is another vendor partition
            // other than /vendor.
            File privilegedOdmAppDir = new File(Environment.getOdmDirectory(),
                        "priv-app");
            try {
                privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            scanDirTracedLI(privilegedOdmAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect ordinary odm packages. /odm is another vendor partition
            // other than /vendor.
            File odmAppDir = new File(Environment.getOdmDirectory(), "app");
            try {
                odmAppDir = odmAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            scanDirTracedLI(odmAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);

            // Collect all OEM packages.
            final File oemAppDir = new File(Environment.getOemDirectory(), "app");
            scanDirTracedLI(oemAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_OEM,
                    0);

            // Collected privileged product packages.
            File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
            try {
                privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            scanDirTracedLI(privilegedProductAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRODUCT
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect ordinary product packages.
            File productAppDir = new File(Environment.getProductDirectory(), "app");
            try {
                productAppDir = productAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            scanDirTracedLI(productAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRODUCT,
                    0);

            // Collect all Regionalization packages form Carrier's res packages.
            if (RegionalizationEnvironment.isSupported()) {
                Log.d(TAG, "Load Regionalization vendor apks");
                final List<File> RegionalizationDirs =
                        RegionalizationEnvironment.getAllPackageDirectories();
                for (File f : RegionalizationDirs) {
                    File RegionalizationSystemDir = new File(f, "system");
                    // Collect packages in <Package>/system/app
                    scanDirLI(new File(RegionalizationSystemDir, "app"),
                            PackageParser.PARSE_IS_SYSTEM_DIR,
                            scanFlags
                            | SCAN_AS_SYSTEM,
                            0);
                    // Collect overlay in <Package>/system/vendor
                    scanDirLI(new File(RegionalizationSystemDir, "vendor/overlay"),
                            PackageParser.PARSE_IS_SYSTEM_DIR,
                            scanFlags
                            | SCAN_AS_SYSTEM
                            | SCAN_AS_VENDOR,
                            0);
                }
            }

扫描非系统App

if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

                scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
                        | PackageParser.PARSE_FORWARD_LOCK,
                        scanFlags | SCAN_REQUIRE_KNOWN, 0);

                // Remove disable package settings for updated system apps that were
                // removed via an OTA. If the update is no longer present, remove the
                // app completely. Otherwise, revoke their system privileges.
                for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
                    PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
                    mSettings.removeDisabledSystemPackageLPw(deletedAppName);
                    final String msg;
                    if (deletedPkg == null) {
                        // should have found an update, but, we didn't; remove everything
                        msg = "Updated system package " + deletedAppName
                                + " no longer exists; removing its data";
                        // Actual deletion of code and data will be handled by later
                        // reconciliation step
                    } else {
                        // found an update; revoke system privileges
                        msg = "Updated system package + " + deletedAppName
                                + " no longer exists; revoking system privileges";

                        // Don't do anything if a stub is removed from the system image. If
                        // we were to remove the uncompressed version from the /data partition,
                        // this is where it'd be done.

                        final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                        deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
                        deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
                    }
                    logCriticalInfo(Log.WARN, msg);
                }

                /*
                 * Make sure all system apps that we expected to appear on
                 * the userdata partition actually showed up. If they never
                 * appeared, crawl back and revive the system version.
                 */
                for (int i = 0; i < mExpectingBetter.size(); i++) {
                    final String packageName = mExpectingBetter.keyAt(i);
                    if (!mPackages.containsKey(packageName)) {
                        final File scanFile = mExpectingBetter.valueAt(i);

                        logCriticalInfo(Log.WARN, "Expected better " + packageName
                                + " but never showed up; reverting to system");

                        final @ParseFlags int reparseFlags;
                        final @ScanFlags int rescanFlags;
                        if (FileUtils.contains(privilegedAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_PRIVILEGED;
                        } else if (FileUtils.contains(systemAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM;
                        } else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
                                || FileUtils.contains(privilegedOdmAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_VENDOR
                                    | SCAN_AS_PRIVILEGED;
                        } else if (FileUtils.contains(vendorAppDir, scanFile)
                                || FileUtils.contains(odmAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_VENDOR;
                        } else if (FileUtils.contains(oemAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_OEM;
                        } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_PRODUCT
                                    | SCAN_AS_PRIVILEGED;
                        } else if (FileUtils.contains(productAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_PRODUCT;
                        } else {
                            Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
                            continue;
                        }

                        mSettings.enableSystemPackageLPw(packageName);

                        try {
                            scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
                        } catch (PackageManagerException e) {
                            Slog.e(TAG, "Failed to parse original system package: "
                                    + e.getMessage());
                        }
                    }
                }

其中,系统安装目录有:

/system/framework 系统库

/system/app 默认的系统应用

/vendor/app 厂商定制的应用

非系统apk信息目录:
/data/app/

/data/app-private/

android 预装应用和系统应用 安卓系统预装软件_java_04


android 预装应用和系统应用 安卓系统预装软件_android_05


scanDirLI()

try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback)) {
            // Submit files for parsing in parallel
            int fileCount = 0;
            //=================yubang add for presetapp start==========================
            int originalScanFlags = scanFlags;
            //=================yubang add for presetapp end==========================
            for (File file : files) {
                //=================yubang add for presetapp start==========================
                if("/data/app".equals(scanDir.getPath()) && !file.getPath().endsWith("==") && !file.getPath().contains(".")){
                    scanFlags = SCAN_BOOTING | SCAN_INITIAL & ~SCAN_REQUIRE_KNOWN;
                }else{
                    scanFlags = originalScanFlags;
                }
                //=================yubang add for presetapp end==========================
                final boolean isPackage = (isApkFile(file) || file.isDirectory())
                        && !PackageInstallerService.isStageName(file.getName());
                if (!isPackage) {
                    // Ignore entries which are not packages
                    continue;
                }

                // Exclude Regionalization exclude.list from Carrier's configure.
                if (RegionalizationEnvironment.isSupported()) {
                    if (RegionalizationEnvironment.isExcludedApp(file.getName())) {
                        Log.d(TAG, "Regionalization Excluded:" + file.getName());
                        continue;
                    }
                }
                parallelPackageParser.submit(file, parseFlags);
                fileCount++;
            }

ParallelPackageParser.java
submit()

public void submit(File scanFile, int parseFlags) {
        mService.submit(() -> {
            ParseResult pr = new ParseResult();
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
            try {
                PackageParser pp = new PackageParser();
                pp.setSeparateProcesses(mSeparateProcesses);
                pp.setOnlyCoreApps(mOnlyCore);
                pp.setDisplayMetrics(mMetrics);
                pp.setCacheDir(mCacheDir);
                pp.setCallback(mPackageParserCallback);
                pr.scanFile = scanFile;
                pr.pkg = parsePackage(pp, scanFile, parseFlags);
            } catch (Throwable e) {
                pr.throwable = e;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            try {
                mQueue.put(pr);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                // Propagate result to callers of take().
                // This is helpful to prevent main thread from getting stuck waiting on
                // ParallelPackageParser to finish in case of interruption
                mInterruptedInThread = Thread.currentThread().getName();
            }
        });
    }

android 预装应用和系统应用 安卓系统预装软件_android 预装应用和系统应用_06

系统app的升级逻辑

addForInitLI()

boolean shouldHideSystemApp = false;
        // A new application appeared on /system, but, we already have a copy of
        // the application installed on /data.
        //我们安装一个系统APP的时候,发现已经有了一个相同包名的APP,而且这个相同包名APP是在非系统的分区中
        if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
                && !pkgSetting.isSystem()) {

            if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails,
                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
                            && !pkgSetting.signatures.mSigningDetails.checkCapability(
                                    pkg.mSigningDetails,
                                    PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
                logCriticalInfo(Log.WARN,
                        "System package signature mismatch;"
                        + " name: " + pkgSetting.name);
                try (PackageFreezer freezer = freezePackage(pkg.packageName,
                        "scanPackageInternalLI")) {
                    // 如果两个APK 签名不匹配,则调用deletePackageLI方法清除APK文件及其数据
                    deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
                }
                pkgSetting = null;
            } else if (newPkgVersionGreater) {
                // The application on /system is newer than the application on /data.
                // Simply remove the application on /data [keeping application data]
                // and replace it with the version on /system.
                // 如果新安装的系统APP的版本号比当前已经安装的版本号要高,所以要要删之前已安装的APP,并安装新的系统APP
                logCriticalInfo(Log.WARN,
                        "System package enabled;"
                        + " name: " + pkgSetting.name
                        + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
                        + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
                InstallArgs args = createInstallArgsForExisting(
                        packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
                        pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
                synchronized (mInstallLock) {
                    args.cleanUpResourcesLI();
                }
            } else {
                // The application on /system is older than the application on /data. Hide
                // the application on /system and the version on /data will be scanned later
                // and re-added like an update.
                shouldHideSystemApp = true;
                logCriticalInfo(Log.INFO,
                        "System package disabled;"
                        + " name: " + pkgSetting.name
                        + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
                        + "; new: " + pkg.codePath + " @ " + pkg.codePath);
            }
        }

##第三方APP的安装流程

android 预装应用和系统应用 安卓系统预装软件_android 预装应用和系统应用_07

PMS主体启动过程
创建一个PMS.Settings,在该对象的构造函数中,将给裁员变量静态赋值
调用mSettings.addSharedUserLP()添加四个共享用户ID
创建一个Installer对象,该对象将辅助程序的安装。
给以下几个数据文件路径静态赋值:
mAppDataDir,代表程序的数据目录,其值为/data/data
mSecureAppDataDir,所谓的解密数据区,目前无用,路径为/data/secure/data
mDrmAppPrivateInstallDir,所谓的DRM数据区,目前无用,路径为/data/app-private
调用readPermission()函数,从/system/etc/permissions目录下读取全部的xml文件
调用mSettings对象的readLP()函数从/data/system/packages.xml文件中读取所有应用程序中和包管理相关的信息,将其保存到mSettings.mPackages变量中。
对java系统中的库文件进行dex提取(转换)
创建FileObserver,用来检测目录中的添加、删除文件的时间,并当时间发生时执行相应的操作
调用scanDirLI()扫描AndroidManifest文件,并将扫描结果保存到PMS中。
删除已经不存在程序对应的数据记录。
清除没有安装成功的数据记录
调用deleteTempPackageFiles()删除/data/app目录下,一vmdl开头的.tmp结尾的文件
如果系统升级,调用updatePermissionsLP()重新为应用程序赋予权限。
调用mSettings.writeLP()将mSettings.mPackages中的数据值重新写入packages.xml文件中

预置APP和手动安装APP最终都会调到PMS的scanPackageTracedLI()经行最终的安装

预置APP的原理

android 预装应用和系统应用 安卓系统预装软件_android_08


android 预装应用和系统应用 安卓系统预装软件_java_09


android 预装应用和系统应用 安卓系统预装软件_android_10


PackageManagerService启动之后在扫描系统app之前通过如下三个条件判断是否执行预制动作

a,通过mFirstBoot判断当前是否是第一次开机(恢复出厂设置后也算)

b,通过比较fingerprint的value判断是否是ota升级之后第一次开机

c,通过读取/data/system/cpreset.txt文件内容判断是否之前有拷贝失败的情况 如果有则需要重新copy

如果满足上诉条件任何之一则将/system/presetapp/目录下的app通过PackageManagerServicePresetInjector的copyPresetApp()copy到/data/app/并且赋予权限。

copy之前会解析/system/presetapp目录下的app包名和versionCode,通过包名去/data/app/目录查询是否用户已经安装该应用,如果安装了再比较两个app的versionCode,然后使用版本高的。

会记录用户卸载的预置app包名 下次ota的时候会去根据包名检测内置的app是否被删除过 如果删除过则放弃掉。

预置方法
内置app的路径为:vendor/blackshark/prebuilt/

更新app:
如果内置的app本身在vendor/blackshark/prebuilt/已经存在 只是更新app的话 将新的app改为和原来的app一样的名字然后直接覆盖就好,同样的如果app里面包含so文件的话需要将新的app里面的so文件提取出来 替换掉/lib/下面的全部so文件。

预置新app:
如果需要预制的app 在vendor/blackshark/prebuilt/不存在则需要新建一个文件夹将apk放进去 然后新建一个Android.mk文件 例如:

########################################start########################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := baiduime-system.presetapp
LOCAL_MODULE_PATH := $(TARGET_OUT)/presetapp
LOCAL_POST_INSTALL_CMD := mkdir -p $(PRODUCT_OUT)/system/presetapp/baiduime/lib/arm/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/*.apk $(PRODUCT_OUT)/system/presetapp/baiduime/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/lib/*.so $(PRODUCT_OUT)/system/presetapp/baiduime/lib/arm/
include $(BUILD_PHONY_PACKAGE)
 
include $(CLEAR_VARS)
LOCAL_MODULE := baiduime-system.app
LOCAL_MODULE_PATH := $(TARGET_OUT)/app
LOCAL_POST_INSTALL_CMD := mkdir -p $(PRODUCT_OUT)/system/app/baiduime/lib/arm/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/*.apk $(PRODUCT_OUT)/system/app/baiduime/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/lib/*.so $(PRODUCT_OUT)/system/app/baiduime/lib/arm/
include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
LOCAL_MODULE := baiduime-system.privapp
LOCAL_MODULE_PATH := $(TARGET_OUT)/priv-app
LOCAL_POST_INSTALL_CMD := mkdir -p $(PRODUCT_OUT)/system/priv-app/baiduime/lib/arm/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/*.apk $(PRODUCT_OUT)/system/priv-app/baiduime/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/lib/*.so $(PRODUCT_OUT)/system/priv-app/baiduime/lib/arm/
include $(BUILD_PHONY_PACKAGE)
########################################end########################################

鉴于之前app可能需要内置发方式或者路径可以会变化 所以对应的Android.mk文件里面写三个MODULE 分别控制不同的内置路径。在preset_app_xxx.mk文件中引入最终需要内置LOCAL_MODULE即可。

如果app有so文件将apk里面的so文件提出出来放在同目录下面的/lib/arm/目录下

apk文件的命令最好按照应用的英文名称 后续更新的时候都要改为一样的名字直接覆盖。

preset_app_xxx.mk xxx是对应的项目名(它会在device/blackshark/xxx/xxx.mk文件中引用)

如skywalk 对应的未preset_app_如skywalk.mk 所有需要在skywalk内置的应用需要把LOCAL_MODULE在文件里面注册 例如:

PRODUCT_PACKAGES += \
    xpbrowser-system.presetapp \
    mi_mall-system.presetapp \
    mibrowser-system.privapp \
    miweather-system.presetapp \
    BSMall-system.presetapp \
    preputetc-system.privapp \
    h5_gamehall-system.presetapp \
    baiduime-system.app

注意事项

1) 更新apk要保证apk文件名和之前版本的文件名一样覆盖。

2) ota升级预置的应用如果手机已经安装了该应并且版本比ota里面的应用高则不会更新反之则替换为ota里面新版本。

3) 预置应用一定要讲app里面的so文件提取出来 使用makefile文件copy

参考资料

https://www.jianshu.com/p/8e2831428110

项目差异比较

android 预装应用和系统应用 安卓系统预装软件_java_11


android 预装应用和系统应用 安卓系统预装软件_android 预装应用和系统应用_12


1 p-q 预置app丢失

2 p-q 第三方app丢失

3 p-q 删除掉的预置app又回来了

4 删除掉的预置app 用户又安装回来了 p-q app丢失

5 p-q 预置app打开闪退

6 p-q(小米签名) 预置app丢失

a,q的预置app versioncode 不大于p的预置app

b,用户自己安装的使用了黑鲨签名的系统级应用 比如:sharkmonitor