【项目】Android(安卓)预置第三方应用可卸载功能的实现
原生Android 的状况是:
手机会预置一些第三方APP ,用户不可删除。
现在实现用户可删除的预置应用的功能
1.修改预置应用安装路径:
1.1 /system 下创建/third_app 文件夹
1.把预留应用放在system/third-app下;
2.第一次开机 ,PKMS初始化扫描data/app之前,这些应用源文件从 /system/third-app copy到 data/app下;
恢复出厂设置仅仅就格式化/data 分区,不会格式化/system 分区,回复出厂设置后第一次就直接copy到/system/app下,因此恢复出厂设置后仍然有效
1.1.1 修改预置第三放APP Android.mk
include $(CLEAR_VARS)LOCAL_MODULE := SogouInputLOCAL_SRC_FILES := $(LOCAL_MODULE).apkLOCAL_MODULE_TAGS := optionalLOCAL_MODULE_CLASS := APPSLOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)LOCAL_CERTIFICATE := PRESIGNEDLOCAL_DEX_PREOPT := falseLOCAL_MODULE_PATH := $(TARGET_OUT)/third_appinclude $(BUILD_PREBUILT)
1.1.2 添加copy 代码
//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(); }
1.1.3 PKMS 中插入copy代码
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删除不了,正好恢复出厂设置的时候可以重新恢复;
简单的逻辑就是把/system/third_app 下的APP专门由PKMS.mVendorSettings.mVendorPackages 进行管理(与PKMS.mSettings.mPackages 对应),但是,PKMS.mSettings.mPackages仍然包含PKMS.mVendorSettings.mVendorPackages中的PKG
1.增加“services/core/java/com/android/server/pm/VendorSettings.java”
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 中. }}
2.增加/services/core/java/com/android/server/pm/VendorPackageSettings.java
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; }}
3.PackageManagerService 中的修改:
3.1. 增加PKMS.mVendorSettings 成员变量 和PKMS.mVendorPackages 成员变量
public class PackageManagerService extends IPackageManager.Stub {... ...+ final HashMap mVendorPackages =+ new HashMap();... ... final Settings mSettings;... ...+ final VendorSettings mVendorSettings; boolean mRestoredSettings;
3.2. PKMS 构造函数
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(); }
4. 扫描/system/third_app 相关的修改
4.1 增加PackageParser.PARSE_IS_VENDOR 这个flags
public final static int PARSE_IS_VENDOR = 1<<10;
代表 pkg 是 /system/third_app
4.2 scanPackageLI () 的修改
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;+ }+ + }+ }
4.2.1 在scanDirLI () 中,将有 PackageParser.PARSE_IS_VENDOR 标志位的放入PKMS.mVendorSettings.mVendorPakages中;
4.2.2 scanDirLI 正在扫描的third_pkg 也在mVendorPackages中,但是“uninstalled”状态,就返回return,结束安装 (/system/third_app 下的资源文件不可能被删除,当一个third_app 被删除时,VendorPackageSettings.mIntallStatus = false 代表被已经删除,放在 (VendorPackageSettings) PKMS.mVendorSettings.mPackages[i].mIntallStatus :
1.scanDirLI 在扫描/system/third_app/deleted_third_app.apk 时,就直接return null ,此时,而PKMS.mPackage 的更新是在scanPackageDirtyLI中,所以PKMS.mPackages 中是没有deleted_third_app.apk对应的PackageParser.package对象的,所以package-list.xml 中不会有记录;
2.
)
4.3 scanPackageDirtyLI ()
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 ,表示已经安装状态
5. 删除third_app 的处理:
在removePackageDataLI() 中处理:
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();+ }}
注意:PKMS.mPackages中本身是包含需要删除的deleting_third_app.apk 的,所以没有必要专门为PKMS.mVendorPackages 做特殊的删除流程!
在删除流程中,仅仅只需要设置PKMS.mVendorPackages[i].mInstalStatus = false;
=====================================================
***知识点***:
1. scanDirLI(file, PackageParser.PARSE_IS_VENDOR, scanFlags, 0) :
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。
2.这几个路径下是system_app
// /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
3. 几个存储信息的成员变量的比较:
3.1 PKMS.mPackages<PackageParser.package>
PKMS.mVendorPackages
3.2 PKMS.mSettings.mPackages<PackageSettings>
PKMS.mVendorSettings.mVendorPackages
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 文件中;
更多相关文章
- 一款常用的 Squid 日志分析工具
- GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
- RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
- Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
- 整合Plugin-X之后的ios项目配置
- Android培训---运行你的应用程序
- Android中LruCache的源码分析
- 玩转pandaboard之linaro对于Android的编译上的一些优化
- Android(安卓)GpioService从app到驱动