内置APP在安卓系统不可或缺,客户有时需要内置一些厂商商店等应用是通过adb install去测试以及验证功能,当要求我们内置时会遇到代码混淆以及各种限制导致应用功能失效,这种时候我们需要写一个脚本去实现开机自动install来替代。
一.使用PRODUCT_COPY_FILES
+# define check-product-copy-files
+# $(if $(filter-out $(TARGET_COPY_OUT_SYSTEM_OTHER)/%,$(2)), \
+# $(if $(filter %.apk, $(2)),$(error \
+# Prebuilt apk found in PRODUCT_COPY_FILES: $(1), use BUILD_PREBUILT instead!))) \
+# $(if $(filter true,$(BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES)),, \
+# $(if $(filter $(TARGET_COPY_OUT_SYSTEM)/etc/vintf/% \
+# $(TARGET_COPY_OUT_SYSTEM)/manifest.xml \
+# $(TARGET_COPY_OUT_SYSTEM)/compatibility_matrix.xml,$(2)), \
+# $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), use vintf_fragments instead!)) \
+# $(if $(filter $(TARGET_COPY_OUT_PRODUCT)/etc/vintf/%,$(2)), \
+# $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \
+# use PRODUCT_MANIFEST_FILES / DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!)) \
+# $(if $(filter $(TARGET_COPY_OUT_SYSTEM_EXT)/etc/vintf/%,$(2)), \
+# $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \
+# use vintf_compatibility_matrix / vintf_fragments instead!)) \
+# $(if $(filter $(TARGET_COPY_OUT_VENDOR)/etc/vintf/% \
+# $(TARGET_COPY_OUT_VENDOR)/manifest.xml \
+# $(TARGET_COPY_OUT_VENDOR)/compatibility_matrix.xml,$(2)), \
+# $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \
+# use DEVICE_MANIFEST_FILE / DEVICE_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!)) \
+# $(if $(filter $(TARGET_COPY_OUT_ODM)/etc/vintf/% \
+# $(TARGET_COPY_OUT_ODM)/etc/manifest%,$(2)), \
+# $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \
+# use ODM_MANIFEST_FILES / vintf_fragments instead!)) \
+# )
+# endef
需要跳过这一部分检查
+PRODUCT_COPY_FILES += \
+ device/rockchip/rk356x/rk3568_r/apps/xxxx/xxxx.apk:odm/other_persist-app/xxxx.apk
客户要求恢复出厂设置后我用的RK平台,我试过放置到对应路径下install,结果可卸载跟不可卸载功能是用不了的,因此我们要重新写一套。但目前先尝试copy跟install可不可行
因此我们在init阶段读取on property:sys.boot_completed=1
+ exec u:r:shell:s0 root root -- /system/bin/pm install /odm/other_uninstall_back-app/xxxx.apk
本以为行得通,没想到客户要求用他们自己的Launcher,且他们的UI线程有点问题,应用install时如果不在他们的launcher主界面之后install是不会有新图标显示的,除非重启。为了减少扯皮步骤开始第二种方案。
二.脚本服务实现
diff --git a/device/rockchip/common/init.rk30board.rc b/device/rockchip/common/init.rk30board.rc
index f02c344b54..1d8b12aff5 100755
--- a/device/rockchip/common/init.rk30board.rc
+++ b/device/rockchip/common/init.rk30board.rc
@@ -425,3 +425,9 @@ service mcu_notify_service /system/bin/mcu_tool notify_power_on
user root
group root
oneshot
+
+service install_service /system/bin/install_service.sh
+ class main
+ user root
+ oneshot
+ disabled
diff --git a/device/rockchip/common/sepolicy/private/file_contexts b/device/rockchip/common/sepolicy/private/file_contexts
index 9c6f73427c..904ee95bce 100644
--- a/device/rockchip/common/sepolicy/private/file_contexts
+++ b/device/rockchip/common/sepolicy/private/file_contexts
@@ -2,3 +2,5 @@
/system/bin/move_widevine_data\.sh u:object_r:move-widevine-data-sh_exec:s0
/system/bin/can_init_bitrate\.sh u:object_r:can_init_bitrate-sh_exec:s0
+
+/system/bin/install_service\.sh u:object_r:install_service-sh_exec:s0
diff --git a/device/rockchip/common/sepolicy/private/install-service.te b/device/rockchip/common/sepolicy/private/install-service.te
new file mode 100644
index 0000000000..1c772d302a
--- /dev/null
+++ b/device/rockchip/common/sepolicy/private/install-service.te
@@ -0,0 +1,4 @@
+type install_service-sh, domain, coredomain;
+type install_service-sh_exec, exec_type, system_file_type, file_type;
+allow init install_service-sh_exec:file { getattr open read execute map };
+init_daemon_domain(install_service-sh);
\ No newline at end of file
diff --git a/device/rockchip/rk356x/init.rk356x.rc b/device/rockchip/rk356x/init.rk356x.rc
index a1222044cd..b55745abd3 100644
--- a/device/rockchip/rk356x/init.rk356x.rc
+++ b/device/rockchip/rk356x/init.rk356x.rc
@@ -11,10 +11,8 @@ on property:sys.boot_completed=1
start can_init_bitrate
+ start install_service
on boot
chown system system /sys/class/thermal/thermal_zone0/policy
chown system system /sys/class/thermal/thermal_zone1/policy
@@ -56,3 +54,9 @@ service can_init_bitrate /system/bin/can_init_bitrate.sh
oneshot
disabled
+service install_service /system/bin/install_service.sh
+ class main
+ user root
+ oneshot
+ disabled
+
需要注意的是oneshot执行一次,以及selinux权限相关
之后添加我们的不可卸载框架
diff --git a/frameworks/base/core/java/android/content/pm/PackageParser.java b/frameworks/base/core/java/android/content/pm/PackageParser.java
index 427fe2820b..0adadf65b4 100644
--- a/frameworks/base/core/java/android/content/pm/PackageParser.java
+++ b/frameworks/base/core/java/android/content/pm/PackageParser.java
@@ -130,6 +130,9 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
+import java.io.BufferedReader;
+import java.io.FileReader;
+
/**
* Parser for package files (APKs) on disk. This supports apps packaged either
* as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
@@ -214,6 +217,17 @@ public class PackageParser {
public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
"android.activity_window_layout_affinity";
+ /**
+ * Profile file path
+ */
+ private static final String PROFILE_FILE_PATH = "/data/local/config/";
+
+ /**
+ * Profile file name
+ */
+ private static final String PROFILE_FILE_PATH_NAME = "NoDeleteApplist";
+
+
/**
* Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
* @hide
@@ -2489,10 +2503,52 @@ public class PackageParser {
if (pkg.applicationInfo.usesCompatibilityMode()) {
adjustPackageToBeUnresizeableAndUnpipable(pkg);
}
+ if (isProfileNoDeleteApp(pkg.packageName)) {
+ Slog.w(TAG, "parser not removing packages " );
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ }
return pkg;
}
+ /**
+ * profile no delete app file
+ * @param pkgName
+ * @return
+ */
+ private boolean isProfileNoDeleteApp(String pkgName) {
+ final File systemDir;
+ final File blackListFile;
+ final ArrayList<String> blackListApps = new ArrayList<String>();
+ systemDir = new File(PROFILE_FILE_PATH);
+ blackListFile = new File(systemDir, PROFILE_FILE_PATH_NAME);
+ if (!blackListFile.exists()) {
+ return false;
+ }
+ try {
+ blackListApps.clear();
+ BufferedReader br = new BufferedReader(new FileReader(blackListFile));
+ String line = br.readLine();
+ while (line != null) {
+ blackListApps.add(line);
+ line = br.readLine();
+ }
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ Iterator<String> it = blackListApps.iterator();
+ while (it.hasNext()) {
+ String blacklistItem = it.next();
+ if (pkgName.equals(blacklistItem)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
/**
* Returns {@code true} if both the property name and value are empty or if the given system
* property is set to the specified value. Properties can be one or more, and if properties are
diff --git a/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java b/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
index 22c73d2f77..e9dcbdfb99 100644
--- a/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -450,6 +450,10 @@ import java.util.function.Supplier;
import android.app.smdt.SmdtManagerNew;
import java.lang.reflect.Method;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import android.widget.Toast;
+
/**
* Keep track of all those APKs everywhere.
* <p>
@@ -809,6 +813,16 @@ public class PackageManagerService extends IPackageManager.Stub
*/
boolean mPromoteSystemApps;
+ /**
+ * Profile file path
+ */
+ private static final String PROFILE_FILE_PATH = "/data/local/config/";
+
+ /**
+ * Profile file name
+ */
+ private static final String PROFILE_FILE_PATH_NAME = "NoDeleteApplist";
+
private final PackageManagerInternal mPmInternal;
@@ -3189,6 +3203,9 @@ public class PackageManagerService extends IPackageManager.Stub
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
+ if (isProfileNoDeleteApp(ps.name)) {
+ continue;
+ }
/*
* If the package is scanned, it's not erased.
@@ -9167,6 +9184,9 @@ public class PackageManagerService extends IPackageManager.Stub
// Ignore entries which are not packages
continue;
}
+ if (!isPackage && !isProfileNoDeleteApp(file.getAbsolutePath())) {
+ continue;
+ }
if (file.getAbsolutePath().contains(BUNDLED_UNINSTALL_GONE_DIR)) {
if (list != null && list.size() > 0) {
final boolean isdeleteApk = isDeleteApk(file,parseFlags,list);
@@ -10458,6 +10478,10 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mInstallLock")
void removeCodePathLI(File codePath) {
+ if (isProfileNoDeleteApp(codePath.getAbsolutePath())) {
+ Slog.w(TAG, "profile app file removeCodePathLI error");
+ return;
+ }
if (codePath.isDirectory()) {
File codePathParent = codePath.getParentFile();
try {
@@ -12741,6 +12765,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
private void removePackageLI(AndroidPackage pkg, boolean chatty) {
+ if (isProfileNoDeleteApp(pkg.getPackageName())) {
+ return;
+ }
// Remove the parent package setting
PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps != null) {
@@ -18739,6 +18766,43 @@ public class PackageManagerService extends IPackageManager.Stub
return mKeepUninstalledPackages != null && mKeepUninstalledPackages.contains(packageName);
}
+ /**
+ * profile no delete app file
+ * @param pkgName
+ * @return
+ */
+ private boolean isProfileNoDeleteApp(String pkgName) {
+ final File systemDir;
+ final File blackListFile;
+ final ArrayList<String> blackListApps = new ArrayList<String>();
+ systemDir = new File(PROFILE_FILE_PATH);
+ blackListFile = new File(systemDir, PROFILE_FILE_PATH_NAME);
+ if (!blackListFile.exists()) {
+ return false;
+ }
+ try {
+ blackListApps.clear();
+ BufferedReader br = new BufferedReader(new FileReader(blackListFile));
+ String line = br.readLine();
+ while (line != null) {
+ blackListApps.add(line);
+ line = br.readLine();
+ }
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ Iterator<String> it = blackListApps.iterator();
+ while (it.hasNext()) {
+ String blacklistItem = it.next();
+ if (pkgName.contains(blacklistItem)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* This method is an internal method that could be get invoked either
* to delete an installed package or to clean up a failed installation.
@@ -18765,6 +18829,12 @@ public class PackageManagerService extends IPackageManager.Stub
return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
}
+ if (isProfileNoDeleteApp(packageName)) {
+ Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
+ Toast.makeText(mContext, "该应用无法删除", Toast.LENGTH_SHORT).show();
+ return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
+ }
+
boolean canUninstall = true;
//xlr add for uninstall black/white list 22.6.16 start
SmdtManagerNew smdtManagerNew = SmdtManagerNew.getInstance(mContext);
@@ -19085,6 +19155,9 @@ public class PackageManagerService extends IPackageManager.Stub
private void removePackageDataLIF(final PackageSetting deletedPs, int[] allUserHandles,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = deletedPs.name;
+ if (isProfileNoDeleteApp(packageName)) {
+ return;
+ }
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
// Retrieve object to delete permissions for shared user later on
final AndroidPackage deletedPkg = deletedPs.pkg;
@@ -19222,6 +19295,10 @@ public class PackageManagerService extends IPackageManager.Stub
int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
boolean writeSettings)
throws SystemDeleteException {
+ if (isProfileNoDeleteApp(deletedPs.name)) {
+ Slog.w(TAG, "deleteSystemPackageLIF packageName = " + deletedPs.name);
+ return;
+ }
final boolean applyUserRestrictions =
(allUserHandles != null) && outInfo != null && (outInfo.origUsers != null);
final AndroidPackage deletedPkg = deletedPs.pkg;
diff --git a/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java b/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
index c3a930339e..96d7519541 100755
--- a/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -338,6 +338,8 @@ import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
/**
* An entry in the history stack, representing an activity.
@@ -5387,10 +5389,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
//Launcher is drawn completed,box can exit bootanim
//if ("box".equals(SystemProperties.get("ro.target.product"))){
- if(shortComponentName!=null && !shortComponentName.contains(".FallbackHome")
- && !"1".equals(SystemProperties.get("service.bootanim.exit"))){
- stopBootanim();
+ if(shortComponentName!=null && !shortComponentName.contains(".FallbackHome")
+ && !"1".equals(SystemProperties.get("service.bootanim.exit"))){
+ if ("true".equals(SystemProperties.get("persist.sys.firstboot"))) {
+ Log.e("xx","install appstore");
+ SystemProperties.set("ctl.start","install_service");
+ try{
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ Log.e("xx","线程被中断");
+ }
}
+ stopBootanim();
+ }
//}
实现是通过包名写进一个文件,如果识别到就不可删除。我们把内置的包名也写进脚本里吧
diff --git a/vendor/rockchip/common/bin/Android.mk b/vendor/rockchip/common/bin/Android.mk
index 06f79be443..7aaef01264 100644
--- a/vendor/rockchip/common/bin/Android.mk
+++ b/vendor/rockchip/common/bin/Android.mk
@@ -53,4 +53,12 @@ LOCAL_MODULE_STEM := $(LOCAL_MODULE)
LOCAL_SRC_FILES := $(TARGET_ARCH)/$(LOCAL_MODULE)
include $(BUILD_PREBUILT)
+###############################################################################
+include $(CLEAR_VARS)
+LOCAL_MODULE := install_service.sh
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_SRC_FILES := $(TARGET_ARCH)/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
diff --git a/vendor/rockchip/common/bin/arm64/install_service.sh b/vendor/rockchip/common/bin/arm64/install_service.sh
new file mode 100644
index 0000000000..82637f4f4a
--- /dev/null
+++ b/vendor/rockchip/common/bin/arm64/install_service.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+mkdir /data/local/config/
+chmod 775 /data/local/config/
+echo "com.bbk.appstore" > /data/local/config/NoDeleteApplist
+pm install -r /odm/other_persist-app/AppStore/AppStore.apk
\ No newline at end of file
diff --git a/vendor/rockchip/common/bin/bin.mk b/vendor/rockchip/common/bin/bin.mk
index c8981bfb67..017a6c2651 100644
--- a/vendor/rockchip/common/bin/bin.mk
+++ b/vendor/rockchip/common/bin/bin.mk
@@ -22,5 +22,6 @@ PRODUCT_PACKAGES += \
ifneq ($(filter rk356x, $(TARGET_BOARD_PLATFORM)), )
PRODUCT_PACKAGES += \
- can_init_bitrate.sh
+ can_init_bitrate.sh \
+ install_service.sh
在这里,理应就大功告成了。既在开机动画停止前install,保证在客户launcher显示前我们就安装好了。但是init阶段太早了,导致pms还没起来就进行了脚本操作。因此还是不成功,我们换个时间来进行脚本操作。
diff --git a/device/rockchip/rk356x/init.rk356x.rc b/device/rockchip/rk356x/init.rk356x.rc
index b55745abd3..a1ec2b65ab 100644
--- a/device/rockchip/rk356x/init.rk356x.rc
+++ b/device/rockchip/rk356x/init.rk356x.rc
@@ -11,7 +11,6 @@ on property:sys.boot_completed=1
start can_init_bitrate
- start install_service
on boot
chown system system /sys/class/thermal/thermal_zone0/policy
diff --git a/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java b/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
index 96d7519541..501e9626ca 100755
--- a/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5391,15 +5391,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
//if ("box".equals(SystemProperties.get("ro.target.product"))){
if(shortComponentName!=null && !shortComponentName.contains(".FallbackHome")
&& !"1".equals(SystemProperties.get("service.bootanim.exit"))){
- if ("true".equals(SystemProperties.get("persist.sys.firstboot"))) {
- Log.e("xx","install appstore");
- SystemProperties.set("ctl.start","install_service");
- try{
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- Log.e("xx","线程被中断");
- }
- }
stopBootanim();
}
//}
diff --git a/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java b/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
index 6d98bfbc10..c76e6a4dd0 100644
--- a/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3423,6 +3423,9 @@ public class WindowManagerService extends IWindowManager.Stub
private void performEnableScreen() {
synchronized (mGlobalLock) {
+ if ("true".equals(SystemProperties.get("persist.sys.firstboot"))) {
+ SystemProperties.set("ctl.start","install_service");
+ }
ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b"
+ " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b"
+ " mSystemBooted=%b mOnlyCore=%b. %s", mDisplayEnabled,
最终我们放弃了init阶段去执行脚本,而且在wms中通过属性来启动脚本。这里是绘制屏幕的功能,上层该启动的服务应该都起来了,经过测试OK!!!