Android设备在第一次开机时需预装一些第三方应用,安装后用户可卸载。同时恢复出厂设置并清除系统数据后能够重新预装应用。
需求描述
PackageManagerService作为是Android系统中核心服务之一,管理着所有跟package相关的工作,常见的比如安装、卸载应用。我们可以在此实现该需求。
- 开启后需要自动安装应用,PMS服务扫描时需触发一次该流程。
- 预安装应用能够卸载,说明该应用的安装路径需要在可擦写的data目录下。
- 为了避免重复安装,由一个系统属性开关来控制应用安装逻辑。
- 安装过程需解决preinstall目录下的apk文件的权限问题。
源码实现
以Android 12系统为例,源码位置:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
预安装后的应用路径:/data/app
private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
/** Directory where installed applications are stored */
private static final File sAppInstallDir = new File(Environment.getDataDirectory(), "app");
源文件apk路径:/system/preinstall
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
File preinstallAppDir = new File(Environment.getRootDirectory(), "preinstall");
在PackageManagerService构造方法中,在scanDirTracedLI()扫描应用安装目录后再进行应用预安装工作。
//通过该系统属性:persist.sys.preinstalled控制预安装流程只执行一次
if (!SystemProperties.getBoolean("persist.sys.preinstalled", false)) {
//将/system/preinstall目录下的apk文件复制到/data/app下
copyPackagesToAppInstallDir(preinstallAppDir);
SystemProperties.set("persist.sys.preinstalled", "1");
} else {
handleOtaPreinstall(preinstallAppDir.getPath().toString(),sAppInstallDir.getPath().toString());
}
private void copyPackagesToAppInstallDir(File srcDir) {
Log.i(TAG,"copyPackagesToAppInstallDir");
String[] files = srcDir.list();
if (files == null) {
Log.d(TAG, "No files in app dir " + srcDir);
return;
}
//复制文件以及递归复制子文件夹文件,同时对apk文件设置权限:chmod 0644
if (copyDirectory(srcDir.getPath().toString(), sAppInstallDir.getPath().toString())){
Slog.d(TAG,"Directory "+srcDir.getPath()+" Copy Successfully!");
} else {
Slog.e(TAG,"Directory "+srcDir.getPath()+" Copy fail!");
}
}
handleOtaPreinstall():升级新版本时,要判断系统版本时间戳是否符合要求。
private void handleOtaPreinstall(String srcDirString, String desDirString){
long timestamp = -1;
Log.i(TAG,"handleOtaPreinstall,srcDirString:"+srcDirString);
Log.i(TAG,"handleOtaPreinstall,desDirString:"+desDirString);
String ota_handle = NoahFileUtil.read_file_string(srcDirString+"/ota_handle.txt");
Log.i(TAG,"handleOtaPreinstall,ota_handle:"+ota_handle);
if(ota_handle == null || ota_handle.isEmpty()) return;
String[] tmpStrings = ota_handle.split("\n");
SimpleDateFormat format=new SimpleDateFormat("yyyyMMdd");
try {
Date date = format.parse(tmpStrings[0]);
timestamp=date.getTime();
} catch (ParseException e) {
e.printStackTrace();
}
long oldVersionTime = readOldNoahVersionTime();
Log.i(TAG,"handleOtaPreinstall,timestamp:"+timestamp);
Log.i(TAG,"handleOtaPreinstall,oldVersionTime:"+oldVersionTime);
//升级新版本时,要判断系统版本时间戳是否符合要求
if (timestamp != -1 && oldVersionTime != -1 && oldVersionTime <= timestamp) {
for (int i = 1; i < tmpStrings.length; i++) {
Log.i(TAG,"handleOtaPreinstall,i="+i+",string:"+tmpStrings[i]);
if (tmpStrings[i] != null && tmpStrings[i].isEmpty() == false) {
copyOtaPreinstall(srcDirString+"/"+tmpStrings[i],desDirString+"/"+tmpStrings[i]);
}
}
}
}
效果验证
make services 编译后将生成的产物分别push到机器对应的目录。
在系统system目录下创建preinstall目录,同时赋予权限:mkdir -p -m 644 preinstall;
adb push xxx.apk到/system/preinstall目录;
重启机器后查看机器应用是否已经预装成功。
卸载应用后,恢复出厂设置后再次查看应用是否预装成功。