Andrioid process protection

Posted by php_dev_101 on Fri, 07 Jun 2019 01:20:14 +0200

For reprinting, please indicate the source: z_zhaojun's blog       Original address

This blog is mainly about the application of process lifesaving. Through learning various ways of process lifesaving on the internet, I will summarize, optimize and simply encapsulate them after I understand them.

The main purpose of process maintenance is to enhance the survivability of apps you write on mobile phones and reduce the probability of being killed by mobile phones. Of course, if you want to make sure that apps are not killed, it's impossible (because even if you're strong, you're not as good as the mobile phone manufacturer), so you can only try to improve the survival rate of apps. This goal will be achieved through `AIDL + JobService'(Job Scheduler).

The first step is to achieve two-process guardianship through AIDL, so as to initially enhance the survivability of app.
It shows that at the beginning you have to build a new AIDL service (Baidu is your own way to play), and first you have to add the relevant code:

LocalService.java

//LocalService is a service running in the current app process, where you can implement the functions you want to always perform in the background. The Service Connection interface is implemented to monitor the status of the brotherly service RemoteService (see the code below) and restart the brotherly service RemoteSerivce after listening to the killing of the RemoteService.
class LocalService extends Service implements ServiceConnection {

    private static final String TAG = "LocalService";

    private LocalServiceBinder localBinder;

    private Notification notification;

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        if (null == localBinder) {
            //Need to bind to RemoteService
            localBinder = new LocalServiceBinder();
        }
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        startJob();
        //Increase service priority and reduce the probability of being killed
        startForeground(startId, getNotification());
        //Binding to brother service RemoteService allows you to monitor the status of RemoteService
        bindService(new Intent(LocalService.this, RemoteService.class),
                this, Context.BIND_IMPORTANT);
        return START_STICKY;
    }

    //Here you start the functions you want to achieve, have done a simple encapsulation, there will be mode of operation at the end of the blog.
    private boolean startJob() {
        return AliveServiceManagerImpl.getInstance().startJob();
    }

    @NonNull
    private Notification getNotification() {
        if (null == notification) {
            Notification.Builder builder = new Notification.Builder(this);
            builder.setDefaults(NotificationCompat.FLAG_AUTO_CANCEL);
            builder.setContentTitle(TAG);
            builder.setSmallIcon(R.drawable.logonews);
            builder.setSubText(TAG);
            builder.setContentText(TAG);
            builder.setWhen(System.currentTimeMillis());
            /*PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
            builder.setContentIntent(pi);*/
            notification = builder.build();
        }
        return notification;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return localBinder;
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(TAG, "onServiceConnected");
    }

    //When the brother service RemoteService is killed, the onService Disconnected method is triggered, where the RemoteService is reopened and bound.
    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(TAG, "onServiceDisconnected");
        startService(new Intent(this, RemoteService.class));
        bindService(new Intent(this, RemoteService.class), this, Context.BIND_IMPORTANT);
    }

    //AIDL Service
    private class LocalServiceBinder extends IServiceAidlInterface.Stub {

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.d(TAG, "basicTypes");
        }
    }
}

RemoteService .java

//RemoteService is a service running in a process independent of the current app process, and implements the Service Connection interface, which is used to monitor the status of the brothers'service LocalService and restart the brothers' service LocalService after listening to the killing of the local service.
class RemoteService extends Service implements ServiceConnection {

    private static final String TAG = "RemoteService";

    private RemoteServiceBinder remoteBinder;

    private Notification notification;

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        if (null == remoteBinder) {
            remoteBinder = new RemoteServiceBinder();
        }
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        //Increase service priority and reduce the probability of being killed
        startForeground(startId, getNotification());
        //Binding to the sibling service LocalService allows you to monitor the status of the LocalService
        bindService(new Intent(this, LocalService.class), this, Context.BIND_IMPORTANT);
        return START_STICKY;
    }

    @NonNull
    private Notification getNotification() {
        if (null == notification) {
            Notification.Builder builder = new Notification.Builder(this);
            builder.setDefaults(NotificationCompat.FLAG_AUTO_CANCEL);
            builder.setContentTitle(TAG);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            builder.setSubText(TAG);
            builder.setContentText(TAG);
            builder.setWhen(System.currentTimeMillis());
            /*PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
            builder.setContentIntent(pi);*/
            notification = builder.build();
        }
        return notification;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return remoteBinder;
    }

    //When the sibling service LocalService is killed, the onService Disconnected method is triggered, where the LocalService is reopened and bound.
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(TAG, "onServiceConnected");
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(TAG, "onServiceDisconnected");
        startService(new Intent(this, LocalService.class));
        bindService(new Intent(this, LocalService.class), this, Context.BIND_IMPORTANT);
    }

    private class RemoteServiceBinder extends IServiceAidlInterface.Stub {

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.d(TAG, "basicTypes");
        }
    }
}

Local Service and RemoteService are added in Android Manifest.xml:

<service
            android:name=".aliveservice.LocalService"
            android:enabled="true"
            android:exported="true">
        </service>
        <service
            android:name=".aliveservice.RemoteService"
            android:process=":remote"//To start a new process: package name+“:remote"
            android:enabled="true"
            android:exported="true">//Represents visibility to other processes for true
        </service>

The second step, using JobService
JobService+Jobschedule is somewhat similar to alert manager, a mechanism provided after Android 5.0 to enable the system to perform asynchronous operations in certain environments or at fixed intervals, the main purpose of which is to optimize the battery. Of course, the main purpose here is to protect the brotherly service LocalService+RemoteService.

Specific code:

LocalJobService.java

<service
            android:name=".aliveservice.LocalJobService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:enabled="true"
            android:exported="true">
        </service>
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class LocalJobService extends JobService {
    private static final String TAG = "LocalJobService";
    private static final String KEY_LOCAL_SERVICE_NAME = LocalService.class.getName();
    private static final String KEY_REMOTE_SERVICE_NAME = RemoteService.class.getName();

    private JobInfo jobInfo;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand");
        scheduleJob(getJobInfo());
        return START_NOT_STICKY;
    }

    //Once the current service is opened, the onStartJob method is invoked periodically to determine the survival of the brotherly service LocalService+RemoteService and restart the corresponding service if it is killed.
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i(TAG, "onStartJob");
        boolean isLocalServiceWork = isServiceWork(this, KEY_LOCAL_SERVICE_NAME);
        boolean isRemoteServiceWork = isServiceWork(this, KEY_REMOTE_SERVICE_NAME);
        if (!isLocalServiceWork) {
            this.startService(new Intent(this, LocalService.class));
        }
        if (!isRemoteServiceWork) {
            this.startService(new Intent(this, RemoteService.class));
        }
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.i(TAG, "onStopJob");
        scheduleJob(getJobInfo());
        return true;
    }

    /**
     * Send task jobs to job scheduling
     */
    public void scheduleJob(JobInfo info) {
        Log.i(TAG, "scheduleJob");
        JobScheduler js = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        js.schedule(info);
    }

    public JobInfo getJobInfo() {
        Log.i(TAG, "getJobInfo");
        if (null == jobInfo) {
            JobInfo.Builder builder = new JobInfo.Builder(0, new ComponentName(this, LocalJobService.class));
            builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
            builder.setPersisted(true);
            builder.setRequiresCharging(false);
            builder.setRequiresDeviceIdle(false);
            //100 milliseconds apart
            builder.setPeriodic(100);
            jobInfo = builder.build();
        }
        return jobInfo;
    }

    /**
     * Determine whether the service is running
     */
    public boolean isServiceWork(Context mContext, String serviceName) {
        if (TextUtils.isEmpty(serviceName)) {
            return false;
        }

        boolean isWorking = false;
        ActivityManager manager = (ActivityManager) mContext
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> list = manager.getRunningServices(100);
        for (ActivityManager.RunningServiceInfo info : list) {
            String name = info.service.getClassName();
            if (serviceName.equals(name)) {
                isWorking = true;
                break;
            }
        }
        return isWorking;
    }
}

Step 3: Simple Encapsulation
See the code for the implementation.
AliveServiceManagerImpl.java

/**
 * package: com.zzj.chris.processalivedemo.aliveservice
 * <p>
 * description: Living service Management
 * <p>
 * Created by chris on 2017/8/7.
 */

public class AliveServiceManagerImpl implements AliveServiceManager {
    private static final Byte[] LOCK = new Byte[0];

    private static AliveServiceManager mInstance;

    private List<JobSchedule> mJobObservers;

    private AliveServiceManagerImpl() {}

    //Single case
    public static AliveServiceManager getInstance() {
        if (null == mInstance){
            synchronized (LOCK) {
                if (null == mInstance){
                    mInstance = new AliveServiceManagerImpl();
                }
            }
        }
        return mInstance;
    }

    //Turn on the Process Live function here
    @Override
    public void start(Context context) {//app.getContext
        context.startService(new Intent(context, LocalService.class));
        context.startService(new Intent(context, RemoteService.class));
        if (Build.VERSION.SDK_INT >= 21) {
            context.startService(new Intent(context, LocalJobService.class));
        }
    }

    @Override
    public void stop(Context context) {

    }

    @Override
    public void destroy() {
        if (null != mJobObservers) {
            mJobObservers.clear();
            mJobObservers = null;
        }
        mInstance = null;
    }

    //Start implementing the functions you want to implement in the background.
    @Override
    public boolean startJob() {
        boolean isWorking = false;
        getObservers();
        for (JobSchedule schedule : mJobObservers) {
            if (!schedule.isWorking()) {
                schedule.start();
                isWorking = true;
            }
        }
        return isWorking;
    }

    private void getObservers() {
        if (null == mJobObservers) {
            mJobObservers = new ArrayList<>();
        }
        if (mJobObservers.size() <= 0) {
            //Here you need to add it manually
            mJobObservers.add(NotificationHelper.getInstance());
            mJobObservers.add(*******);
            ......
        }
    }
}

Use examples:
New NotificationHelper class to implement the AliveService Manager. JobSchedule interface (see demo source code, attached at the end of the download address):

NotificationHelper. java

public class NotificationHelper implements AliveServiceManager.JobSchedule {
    private static final String TAG = "NotificationHelper";

    private static NotificationHelper mHelper;

    private boolean isWork;

    private NotificationHelper() {}

    public static NotificationHelper getInstance() {
        if (null == mHelper) {
            mHelper = new NotificationHelper();
        }
        return mHelper;
    }

    @Override
    public void start() {//Execute what needs to be done here
        if (!isWork) {
//            startTimer();
            isWork = true;
        }
    }

    @Override
    public void stop() {
//        stopTimer();
        isWork = false;
    }

    @Override
    public boolean isWorking() {
        return isWork;
    }

Finally, the specific usage:

AliveServiceManagerImpl.getInstance().start(this);

Download address:
      CSDN
      GitHub

Topics: Android Java Mobile xml