云手机内管理程序保活方案
管理程序形态分类
在云手机内的管理程序,基本分为两类:
Android APK Service 形态,即以Android无UI的Service的形式在云手机后台运行。
Android JNI Native 形态,即用Android JNI开发的Executable的二进制可执行程序。
管理程序保活方案
两种形态的管理程序保活方案都依赖一个关键点:extend_custom.sh,即在云手机可内置的钩子脚本。详细信息请参见开机回调脚本。
钩子脚本 extend_custom.sh 的机制说明:
云手机在系统 boot_complete 之后会检查 /data/local/tmp 目录下是否有 extend_custom.sh 脚本,如果有则会执行此脚本。客户可以在此脚本中执行自己文件的操作和移动,也可以启动和管理自己的程序等,但是有个限制条件是脚本执行超时时间是10s。
1. Service形态的管理程序保活
Service形态的程序由于系统机制限制,首次安装之后,如果不启动是属于处于停止状态的,而开机启动完成的广播使用了FLAG_EXCLUDE_STOPPED_PACKAGES ,会使得处于停止状态的应用收不到开机启动完成广播。因此,service管理程序的的安装和首次启动都需要依赖在云手机 /data/local/tmp 目录下内置 extend_custom.sh 脚本来实现,后续重启开机就可以通过接收开机启动完成广播来自启动。
自启动可以在AndroidManifest.xml里配置接收开机广播并在对应的广播处理里启动Service。
<!-- example code--> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <!-- 适配 8.0及以上版本 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <receiver android:name=".DemoServiceReceiver"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.BOOT_COMPLETED" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
开机广播处理并启动Service:
// example code public class DemoServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (!intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { return; } Intent serviceIntent = new Intent(context, DemoService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); } } }
service 程序的保活可以在对应的 Service 类里的 onStartCommand 函数里返回 START_STICKY,这样的话,当 Service 进程被系统 kill 后可以被重新拉起。
// example code public class DemoService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { //other todo... if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //适配8.0及以上 String channel_id = "MyTestService-id"; String channel_name = "MyTestService-name"; NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationChannel Channel = new NotificationChannel(channel_id, channel_name, NotificationManager.IMPORTANCE_HIGH); if (manager != null) { manager.createNotificationChannel(Channel); } Notification notification = new Notification.Builder(this).setChannelId(channel_id).setSmallIcon(R.mipmap.ic_launcher).build(); startForeground(100, notification); } return START_STICKY; } }
安装和首次启动依赖的 extend_custom.sh 脚本实现,示例如下(示例中默认apk文件也在 /data/local/tmp 目录下):
# example code # !/system/bin/sh # APK名称 ApkName=service.apk # APK包名 PackageName=com.huawei.myapplication # Service ServiceName=com.huawei.myapplication/.MyService InstallService() { count=`pm list packages | grep $PackageName |wc -l` if [ $count -le 0 ]; then echo "$ApkName is not installed, now install." ret=`pm install $ApkName` Succ="Success" if [ "$ret" == "$Succ" ]; then echo "install succeed." else echo "install failed." exit 1 fi else echo "$ApkName is already installed." echo "Service start by boot_complete broadcast." exit 1 fi return 0 } StartService() { ret=`am startservice -n $ServiceName` Err="Error: Not found; no service started." contain=$(echo $ret | grep "${Err}") if [[ "$contain" != "" ]]; then echo "Service start failed." exit 1 else echo "Service start succeed." fi return 0 } main() { # 安装 InstallService echo "To start when first installed." # 启动 StartService }
2. Native形态的管理程序保活
Native 管理程序需要被放在云手机 /system/bin 目录下,并给予可执行权限,Native程序依赖的二进制so库需要放在 system/lib 和 system/lib64 目录下。
Native程序、二进制so库文件等都可以通过共享存储或共享应用的方式推进手机的data目录,然后利用 extend_custom.sh 脚本将自己的文件放置在对应的目录下。针对使用诉求不同,有以下几种方式:
(1)系统级保活
可以实现 init_custom.rc 文件,并将文件移动到 /data/local/目录下,然后需要重启手机生效,重启手机后系统会扫描到 init_custom.rc 文件并在 boot_completed 之后启动 NativeDemo(示例中 Native 管理程序名),如果 NativeDemo 进程异常退出或被kill都会被系统再次拉起。
on property:sys.boot_completed=1 start NativeDemo service NativeDemo /system/bin/NativeDemo user root group root disabled writepid /dev/cpuset/system-background/tasks
系统级保活方式有以下优缺点:
优点:系统级的自启动和保活,可靠性高,拉起实时性高。
缺点:需要重启一次手机才可生效。
(2)用户级保活
如果既需要开机自启又需要保活管理程序,而且不希望重启手机生效,可以先在 extend_custom.sh 脚本中执行一个管理程序的检测保活脚本native_demo_monitor.sh,由管理程序的检测保活脚本真正实现 NativeDemo 管理程序的启动和保活。
extend_custom.sh 钩子脚本实现示例:
# example code # !/system/bin/sh [[ -f /data/local/tmp/native_demo_monitor.sh ]] && sh /data/local/tmp/native_demo_monitor.sh &
native_demo_monitor.sh 脚本实现示例:
# example code # !/system/bin/sh file_dir="/data/demo" CopyFile() { cp -rf $file_dir/NativeDemo /system/bin/ chmod 755 /system/bin/NativeDemo cp $file_dir/lib*.so /system/lib64/ } CheckIfCopyFile() { if [ -s $file_dir ]; then echo "file exist" CopyFile else echo "file not exist" fi } Start() { nohup NativeDemo & } KeepAlive() { while do echo "check NativeDemo proc" proc_count=`ps | grep NativeDemo | wc -l` if [ $proc_count -le 0 ]; then echo "start NativeDemo" Start else echo "NativeDemo already started" fi sleep 5 done } main() { CheckIfCopyFile KeepAlive } main
用户级保活方式有以下优缺点:
优点:不需要重启手机即可生效。
缺点:拉起的实时性不如系统级,依赖检查的循环间隔时间。