Updated on 2023-12-25 GMT+08:00

Keeping Management Programs in the Cloud Phone Alive

Management Programs

The management programs in a cloud phone are classified into the following types:

Android APK Service programs: Android services running in the background of the cloud phone without a UI

Android JNI Native programs: executable binary program developed using Android Java Native Interface (JNI)

Methods for Keeping Management Programs Alive

The extend_custom.sh hook script needs to be built into the cloud phone to keep its management problems alive.

When the cloud phone changes to the boot_complete state, it checks whether the extend_custom.sh script exists in the /data/local/tmp directory. If it does, the cloud phone executes the script. You can use this script to operate and move your own files, and start and manage your own programs. The script execution timeout interval is 10s.

  • Keeping Android APK Service programs alive

Due to system mechanism restrictions, management programs of the Android APK Service type are in the stopped state if they are not started after the first installation. The broadcast indicating that the startup is complete uses FLAG_EXCLUDE_STOPPED_PACKAGES. As a result, the stopped applications cannot receive the broadcast. The installation and initial startup of the Android APK Service management programs depend on the built-in extend_custom.sh script in the /data/local/tmp directory of the cloud phone. After the cloud phone is restarted, the programs can be started by receiving the startup completion broadcast.

You can configure the AndroidManifest.xml file to receive the startup broadcast and start the Android APK Service programs in the corresponding broadcast processing.

<!-- example code-->

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!--Applicable to 8.0 and later versions-->
<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>

Process the startup broadcast and start an Android APK Service management program.

// 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);
        }
    }
}

The keepalive of the Android APK Service management programs can return START_STICKY in the onStartCommand function of the corresponding Service type. In this way, the Android APK Service management programs can be restarted after being killed by the system.

// 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 or later
            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;
    }
}

The following is an example of the installation and initial startup of Android APK Service management programs that depend on the extend_custom.sh script. (The default APK file is also stored in the /data/local/tmp directory.)

#  example code 

# !/system/bin/sh

# APK name
ApkName=service.apk
# APK package name
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() {
        # Installation
        InstallService
        
        echo "To start when first installed."
        # Start
        StartService
}
  • Keepalive scheme of management programs of the Android JNI Native type

The Android JNI Native management programs must be stored in the /system/bin directory of the cloud phone and granted the execute permissions. The binary .so libraries on which the Android JNI Native management programs depend must be stored in the system/lib and system/lib64 directories.

The Android JNI Native management programs and binary .so libraries can be pushed to the data directory of cloud phones by shared storage or application. You can use the extend_custom.sh script to place your files in the corresponding directory. The following methods are available to meet different keepalive requirements:

- System-level keepalive

System-level keepalive can edit and move the init_custom.rc file to the /data/local/ directory. You need to restart the cloud phone, then the system will scan the init_custom.rc file. After the system is in the boot_completed state, it starts NativeDemo (the name of the Android JNI Native management program in the example). If the NativeDemo process exits abnormally or is killed, the system restarts it again.

on property:sys.boot_completed=1
    start NativeDemo

service NativeDemo /system/bin/NativeDemo
    user root
    group root
    disabled
writepid /dev/cpuset/system-background/tasks

The system-level keepalive has the following advantages and disadvantage:

Advantages: System-level automatic startup and keepalive is highly reliable and of high real-time performance.

Disadvantage: The setting takes effect only after the cloud phone is restarted.

- User-level keepalive

If you need both automatic startup and management program keepalive, and you do not want to restart the cloud phones for the settings to take effect, you can run the native_demo_monitor.sh script in extend_custom.sh to check whether the management program is running. The native_demo_monitor.sh script implements the startup and keepalive of the NativeDemo management program.

Example of the extend_custom.sh hook script

#  example code 

# !/system/bin/sh

[[ -f /data/local/tmp/native_demo_monitor.sh ]] && sh /data/local/tmp/native_demo_monitor.sh &

Example of the native_demo_monitor.sh script

#  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

The user-level keepalive has the following advantage and disadvantage:

Advantage: You do not need to restart your cloud phone for the settings to take effect.

Disadvantage: The real-time performance of the startup is not as good as that at the system level. The startup depends on the check interval.