Unkillable Service--Java Layer Implementation

Unkillable service-Java Layer Implementation

introduce

To begin with, let's talk about how we can achieve this:

1. Improving process priority

2.java Layer Two-Process Daemon

3. Job Scheduler polling

4.native Layer Two-Process Guardian

Today we will only write about the first three ways, and the fourth native layer, two-process guardian, will be explained in the next article.

Let's first look at the priority of the process:

  1. Front-end process

    - Activity has invoked the onResume() method
     - Service service has invoked startForeground()
    - Service (onCreate(), onStart(), or onDestroy()) for life cycle callbacks
     - BroadcastReceiver executing its onReceive() method
    ,
    
  2. Visible process

    - Activity that is not in the foreground but is still visible to the user (such as calling its onPause() method)
    - Bind to Service for Visible (or Front Office) Activity
    
  3. Service process
    - The startService() method starts services that do not fall into the above two categories
  4. Background process

    - Activity's onStop() method has been invoked by a process that is not visible to the user
    
  5. Empty process

    - Process without any active application components
    

Let's see when the process will be killed:

1. Insufficient application memory, recycling process

Increase process priority and reduce process oom_adj value, such as setForeground() to boot process to increase process priority
 Release occupied resources when the application falls back to the background, because when oom_adj is the same, priority is given to releasing memory-intensive processes.
The process that has been running in the background must be light.

2. System third party cleaning software, killing process

Using aidl to realize two-process daemon
 White List

3. The major rom vendors will clean up the killing process when the application exits

Using NDK, poll to see if the specified process is killed, and if the fork process is killed, start
 Using JobScheduler, poll to see if the specified process has been killed, and if so, start

Two-process daemon (based on java layer)

Here we will use aidl, some students who do not know can understand for themselves, let's first code:

1. Writing aidl interface

    interface ProcessConnect {

    }

There is nothing in the interface. This is just to listen for disconnection. If disconnection occurs, the code starts the service.

2. Work Services

public class MessageService extends Service {

    private String TAG = "MessageService";

    private int ID=0X00022;
    private static Context mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Log.e(TAG, "MessageService====>print");

                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //assert uri
        String path = "file:///android_asset/xiaoxin.wav";
        Notification.Builder builder = new Notification.Builder(mContext);
        Notification notification = builder
                .setContentText("messageservice")
                .setSmallIcon(R.drawable.ting)
                .setSound(Uri.parse(path))
                .build();

        startForeground(ID,notification);


        bindService(new Intent(MessageService.this,GuardService.class),mServiceConnection,BIND_WAIVE_PRIORITY);

        return START_STICKY;
    }
    public ServiceConnection  mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "MessageService====>onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

            startService(new Intent(MessageService.this,GuardService.class));
            bindService(new Intent(MessageService.this,GuardService.class),mServiceConnection,BIND_WAIVE_PRIORITY);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new ProcessConnect.Stub() {

        };
    }
}

3. Guardian Service

    public class GuardService extends Service {
        private Context mContext;
        private int ID=0X00021;
        @Override
        public void onCreate() {
            super.onCreate();
            mContext = this;

        }

            @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
                //assert uri
                String path = "file:///android_asset/xiaoxin.wav";
                Notification.Builder builder = new Notification.Builder(mContext);
                Notification notification = builder
                        .setContentText("GuardService")
                        .setSmallIcon(R.drawable.ting)
                        .setSound(Uri.parse(path))
                        .build();

                startForeground(ID,notification);

                bindService(new Intent(GuardService.this,MessageService.class),mServiceConnection,BIND_WAIVE_PRIORITY);

                return START_STICKY;
        }

        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new ProcessConnect.Stub(){

            };
        }

        public ServiceConnection  mServiceConnection = new ServiceConnection() {

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("GuardService", "GuardService====>onServiceConnected");
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

                startService(new Intent(GuardService.this,MessageService.class));
                bindService(new Intent(GuardService.this,MessageService.class),mServiceConnection,BIND_WAIVE_PRIORITY);
            }
        };

    }

As you can see from the above two services, whenever one service ends, another service starts it to realize that the process is not shut down.

4.MainActivity Opens Services

    startService(new Intent(this,MessageService.class));
    startService(new Intent(this,GuardService.class));

5. Configuration

     <service android:name=".MessageService"></service>
    //Running in the new process
    <service android:name=".GuardService" android:process=":guardservice"></service>

The main five steps are done. It's very simple, but don't be too happy, because this two-process daemon method can only be effective below 4.0, but only partially useful for more than 4.0 models. Finally, let's look at using Job Scheduler to poll to start the killing process.

JobScheduler

In android development, there are scenarios where you need to perform a task at a later point or when a particular condition is met, such as when the device is connected to a power adapter or to WIFI. Fortunately, in API 21 (android 5.0, or Lollipop), google provides a new component called the Job Scheduler API to handle such scenarios.

When a set of preset conditions are met, the Job Scheduler API performs an operation for your application. Unlike Alarm Manager, this execution time is uncertain. In addition, the JobScheduler API allows multiple tasks to be performed simultaneously. This allows your application to perform certain specified tasks without considering the battery consumption caused by timing control.

Next we use Job Scheduler to start our killed service:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class JobWakeUpService extends JobService {

    private JobScheduler service;
    private int JobId=100;
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        JobInfo info = new JobInfo.Builder(JobId,new ComponentName(this,JobWakeUpService.class))
                .setPeriodic(2000)
                .build();

        service = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);

        service.schedule(info);
        return START_STICKY;
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.e("JobWakeUpService", "JobWakeUpService====>print");
        //Start a Timing Task
        if(!isServiceWork(this,MessageService.class.getName())){
            //
            startService(new Intent(this,MessageService.class));
        }

        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        //Stop it
        service.cancel(JobId);
//        service.cancelAll();
        return false;
    }

    private boolean isServiceWork(Context context,String serviceName){
        ActivityManager am= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(100);
        if(runningServices == null){
            return false;
        }
        for (ActivityManager.RunningServiceInfo service : runningServices) {
            String className = service.service.getClassName();
            if(className.equals(serviceName)){
                return true;
            }
        }
        return false;

    }
}

We see here the onStartJob of our JobWakeUpService is called in a loop using the JobScheduler service.

Let's look at the configuration next:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>

    <service android:name=".JobWakeUpService"
        android:enabled="true"
        android:permission="android.permission.BIND_JOB_SERVICE"
        ></service>

Call:

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        startService(new Intent(this, JobWakeUpService.class));
    }

This will enable polling to see if the specified process has been killed, if killed, start the function.

Maybe you want to ask if this way can solve the problem that processes above 5.0 are not killed? I can only regret to tell you that, no, my test on Huawei 7.0 failed.

We have seen so many ways that we can't solve the problem that the process is not killed. Is there a better way?

In the next article, we will explain how to use NDK to implement two-process daemon.

Download address: NoDieService-Demo

Keywords: Android Java Google

Added by soadlink on Sat, 22 Jun 2019 22:30:36 +0300