原生Android 的状况是:
手机会预置一些第三方APP ,用户不可删除。
现在实现用户可删除的预置应用的功能
1.把预留应用放在system/third-app下;
2.第一次开机 ,PKMS初始化扫描data/app之前,这些应用源文件从 /system/third-app copy到 data/app下;
恢复出厂设置仅仅就格式化/data 分区,不会格式化/system 分区,回复出厂设置后第一次就直接copy到/system/app下,因此恢复出厂设置后仍然有效
include $(CLEAR_VARS)
LOCAL_MODULE := SogouInput
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_MODULE_PATH := $(TARGET_OUT)/third_app
include $(BUILD_PREBUILT)
//install the third apps when system is first boot
private void installThirdApps(){
//the source directory not exists
File storeDir = new File("/system/third_app");
if(!storeDir.exists()){
Log.e(TAG,"/system/third_app is not exist");
return;
}
//get the apk files in /system/third_app
String apkFilesNames[] = storeDir.list();
if(apkFilesNames == null){
Log.e(TAG,"apk file name is null");
return;
}
//copy the apk files to /data/app
boolean installSucc = false;
for(int i = 0; i < apkFilesNames.length; i++){
//Uri srcFileUri = Uri.parse(storeDir+"/"+apkFilesNames[i]);
File srcFile = new File("/system/third_app",apkFilesNames[i]);
Log.e(TAG,"srcFile="+srcFile);
File destFile = new File("/data/app",apkFilesNames[i]);
Log.e(TAG,"destFile="+destFile.toString());
boolean installResult = copyThirdApps(srcFile,destFile);
if(!installResult){
Log.d(TAG,"install failed");
return;
}
}
}
/**
* File copy function.
* It will be used when installThirdApps
* @param srcFile just like '/system/third_app/***.apk'
* @param destDir just like '/data/app/***.apk'
* @return
*/
private boolean copyThirdApps(File srcFile, File destDir) {
//do some check actions
if (srcFile == null || destDir == null || !srcFile.exists()) {
Log.e(TAG, "invalid arguments for movePreinstallApkFile()");
Log.e(TAG, "move " + srcFile + " to " + destDir + " failed");
return false;
}
//create new file
try{
destDir.createNewFile();
}catch(Exception e){
Log.e(TAG, "create file faild! due to:" + e);
return false;
}
//set permission
try{
Runtime.getRuntime().exec("chmod 644 "+destDir.getAbsolutePath());
}catch(Exception e){
Log.e(TAG, "chmod file faild! due to:"+e);
}
//do copy
try{
boolean ret = FileUtils.copyFile(srcFile,destDir);
if(!ret){
Log.e(TAG,"copy file faild!");
return false;
}
}catch(Exception e){
Log.e(TAG, "copy file faild! due to:"+e);
}
return true && destDir.exists();
}
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
if(isFirstBoot()){//判断第一次开机
Log.i(TAG, "It's first boot, install the third apps");
installThirdApps();//安装三方应用(copy到data/app下)
}
scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
在PKMS的构造函数,开始处理非系统应用的时候,但是一定要在扫描data/app之前,这样才能后面扫描到data/app这些复制进去的app,才会第一次开机安装成功
以上方法会有如下问题:
1.调用接口安装的,可能Launcher启动后还没安装完。
2.而copy到data/app下又会有两份apk问题。
现在提新的方法:
1.就是放在system/third_app下,开机的时候直接扫描这个目录;
2.我们在data/system下建一个xml文件,当应用卸载的时候,我们再xml上记录该应用被卸载了,当再次开机的时候,扫描到该应用就直接跳过;
3.恢复出厂设置时data目录重置,xml文件被删除。system/third_app又会被重新扫描;
4.所有的apk就全部安装上了,而当我们卸载时,因为system/third_app的权限问题,PKMS删除不了,正好恢复出厂设置的时候可以重新恢复;
package com.android.server.pm;
... ...
final class VendorSettings {
... ...
private final File mSystemDir; //"/data/system"
private final File mVendorSettingsFilename; //"/data/system/custom-packages.xml"
private final File mVendorBackupSettingsFilename; //“/data/system/custom-packages-backup.xml”
final HashMap mVendorPackages =
new HashMap();
VendorSettings() {
this(Environment.getDataDirectory());
}
VendorSettings(File dataDir) {
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);
//=1= : 创建"/data/system/custom-packages.xml 和 其备份文件,类似/data/system/package-list.xml"
mVendorSettingsFilename = new File(mSystemDir, "custom-packages.xml");
mVendorBackupSettingsFilename = new File(mSystemDir, "custom-packages-backup.xml");
}
// =2= : mVendorPackages 中增加新的pkg
void insertPackage(String packageName, boolean installStatus) {
VendorPackageSettings vps = mVendorPackages.get(packageName);
if (vps != null) {
vps.setIntallStatus(installStatus);
} else {
vps = new VendorPackageSettings(packageName, installStatus);
mVendorPackages.put(packageName, vps);
}
}
// =3= :
void setPackageStatus(String packageName, boolean installStatus) {
VendorPackageSettings vps = mVendorPackages.get(packageName);
... ...
vps.setIntallStatus(installStatus);
... ...
}
// =4= : mVendorPackages 中减去的pkg
void removePackage(String packageName) {
... ...
mVendorPackages.remove(packageName);
... ...
}
void readLPw() {
// 1.读取/system/etc/custom-packages.xml ,将其中的VendorPackageSettings 信息 读取在PKMS.mVendorSettings.mVendorPackages数组中.
}
void writeLPr() {
// 1.将PKMS.mVendorSettings.mVendorPackages 同步到/system/etc/custom-packages.xml 中.
}
}
package com.android.server.pm;
final class VendorPackageSettings {
final String mPackageName;
boolean mIntallStatus = true;
VendorPackageSettings(String packageName) {
this.mPackageName = packageName;
}
VendorPackageSettings(String packageName, boolean intallStatus) {
this.mPackageName = packageName;
this.mIntallStatus = intallStatus;
}
boolean getIntallStatus() {
return mIntallStatus;
}
void setIntallStatus(boolean mIntallStatus) {
this.mIntallStatus = mIntallStatus;
}
String getPackageName() {
return mPackageName;
}
}
public class PackageManagerService extends IPackageManager.Stub {
... ...
+ final HashMap mVendorPackages =
+ new HashMap();
... ...
final Settings mSettings;
... ...
+ final VendorSettings mVendorSettings;
boolean mRestoredSettings;
public class PackageManagerService extends IPackageManager.Stub {
... ...
mSettings = new Settings(mPackages);
//(1): 初始化PKMS.mVendorSettings
+ mVendorSettings = new VendorSettings();
... ...
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
mSdkVersion, mOnlyCore);
//(2): 调用VendorSettings.readLPw,初始化mVendorSettings.mVendorPackages
+ mVendorSettings.readLPw();
... ...
... ...
scanDirLI( "/vendor/overlay" , ...);
scanDirLI( "/system/framework" , ...);
scanDirLI( "/system/priv-app" , ...);
scanDirLI( "/system/app" , ...);
//(3): PKMS 开始扫描/system/app等APP 时,同样调用scanDirLI扫描/system/third_app下的app
+ final File operatorAppDir = new File("/system/third_app");
+
+ //Add PARSE_IS_VENDOR for operator apps
+ final File[] operatorAppFiles = operatorAppDir.listFiles();
+ for (File file : operatorAppFiles) {
+ scanDirLI(file, PackageParser.PARSE_IS_VENDOR, scanFlags, 0);
+ }
... ...
//(4):1. PKMS.mVendorSettings.mVendorPackages包含解析custom-packages.xml 中记录的所有third_app信息 ;
//(4):2. PKMS.mVendorPackages 包含所有scanDirLI() 解析/system/third_app 新出来的PackageParser.Package 对象
//(4):3. PKMS.mVendorPackages如果没有PKMS.mVendorSettings.mVendorPackages中的记录,说明现实中的third_app 比 custom-packages.xml 中的少,需要跟新custom-packages.xml
+ Iterator vpsit = mVendorSettings.mVendorPackages.values().iterator();
+ while (vpsit.hasNext()) {
+ VendorPackageSettings vps = vpsit.next();
+ final PackageParser.Package scannedVendorPkg = mVendorPackages.get(vps.getPackageName());
+ if (scannedVendorPkg == null) {
+ vpsit.remove();
+ Slog.w(TAG, "Vendor package: " + vps.getPackageName()
+ + " has been removed from system");
+ }
+ }
... ...
mSettings.writeLPr();
//(5): 1.PKMS 初始化完成,调用 mSettings 和 mVendorSettings 的writeLPr()函数,分别将mSettings.mPackages 同步到package-list.xml 和 将mVendorSettings.mVendorPackages 同步到custom-packages.xml
+ mVendorSettings.writeLPr();
}
public final static int PARSE_IS_VENDOR = 1<<10;
代表 pkg 是 /system/third_app
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user) throws PackageManagerException {
... ...
PackageParser pp = new PackageParser();
... ...
+ //If the newly installed package is vendor app,
+ //add or update it in vendor settings
+ if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {
+ mVendorPackages.put(pkg.packageName,true);
+ }
+
+ //Check whether we should skip the scan of current package
+ //We should only check vendor packages
+ if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {
+ VendorPackageSettings vps = +
+ mVendorSettings.mVendorPackages.get(pkg.packageName);
+ if (vps != null) {
+ if (!vps.getIntallStatus()) {
+ //Skip the vendor package that was uninstalled by user
+ Log.i(TAG, "Package " + vps.getPackageName()+ " skipped due to + +
+ uninstalled");
+ return null;
+ }
+
+ }
+ }
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags, ... ) {
... ...
synchronized (mPackages) {
// (1) : scanPackageDirtyLI () 中开始进行PKMS.mSettings.mPackage 的更新
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
//(2) : PKMS.mPackages 的更新
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
//(3) : 清楚PKMS.mSettings.mPackages 多余项
// Make sure we don't accidentally delete its data.
final Iterator iter = mSettings.mPackagesToBeCleaned.iterator();
while (iter.hasNext()) {
PackageCleanItem item = iter.next();
if (pkgName.equals(item.packageName)) {
iter.remove();
}
}
}
... ...
//(4) : 更新PKMS.mVendorSettings.mVendorPackage[i].mInstallStatus = true ,表示已经安装
+ //If the newly installed package is vendor app,
+ //add or update it in vendor settings
+ if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {
+ mVendorSettings.insertPackage(pkg.packageName,true);
+ }
... ...
}
在PKMS.mPackages 和 PKMS.mSettings.mPackages 进行同步后,更新PKMS.mVendorSettings.mVendorPackage[i].mInstallStatus = true ,表示已经安装状态
private void removePackageDataLI(PackageSetting ps,
int[] allUserHandles, boolean[] perUserInstalled,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
... ...
final PackageSetting deletedPs;
+ final VendorPackageSettings delVps;
... ...
deletedPs = mSettings.mPackages.get(packageName);
+ delVps = mVendorSettings.mVendorPackages.get(packageName);
... ...
mHandler.post(new Runnable() {
@Override
public void run() {
// This has to happen with no lock held.
killApplication(deletedPs.name, deletedPs.appId,
KILL_APP_REASON_GIDS_CHANGED);
}
});
... ...
+ if (delVps != null) {
+ //If the deleted package is vendor package
+ //remove it from vendor settins
+ mVendorSettings.setPackageStatus(packageName, false);
+ mVendorSettings.writeLPr();
+ }
}
pkg被scanDirLI扫描时 加上的PackageParser.PARSE_IS_VENDOR的flag,system 的app 也是这个时候加上的flag;
注意:scanDirLI是扫描PKG 最开始的函数,scanDirLI (... , PackageParser.PARSE_IS_VENDOR, ...) 此时添加了PackageParser.PARSE_IS_VENDOR 这个flag ,那么后续扫描都有在PackageParser.PARSE_IS_VENDOR 这个flag 的基础上继续添加新的flag。
// /vendor/overlay
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
// /system/framework
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// /system/priv-app
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// /system/app
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// /vendor/app
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
1./vendor/overlay
2./system/framework
3./system/priv-app
4./system/app
5./vendor/app
以上目录下的app 都是flags = system_app
PKMS.mPackage是scanDirLI () 扫描了XXXX.apk 后生成的 成员是PackageParser.package 的数组,是当前系统中app 的信息;
PKMS.mSettings.mPackages是Settings 调用readPlw 读取/data/system/package-list.xml 后生成的文件解析信息;
PKMS 初始化最后会将PKMS.mPackage 同步到PKMS.mSettings.mPackages 中并最后写进package-list.xml 文件中;