Android for dual process daemon

Source: Internet
Author: User

People who have done Android development should know that the application will be killed by the system in the absence of system resources! How do I restore a background app after it's been reclaimed by the system? There is a lot of discussion on this issue online. Here is a summary of the online spread of the various solutions to see if these methods are really feasible.
1. Increase the priority level
This approach for normal applications should only reduce the probability that the application will be killed, but if it is really being reclaimed by the system, it won't be able to automatically restart the app!

2. Let Service.onstartcommand return to Start_sticky
The experiment found that if the kill process in the ADB shell kills the application accidentally (or with 360 mobile phone defender to clean up the operation), if the service's Onstartcommand return Start_sticky, In the eclipse's process manager, a process that has been killed after a small session does appear in the Task manager, which seems to be a viable option. However, if you choose to forcibly close the app in the app management of your system settings, you'll find that even if Onstartcommand returns to Start_sticky, the app will not restart!

3.android:persistent= "true"
The method of setting this attribute is also presented on the Internet, and it is found that even if this property is set, the application cannot be restarted after kill!

4. Make the application a system application
The experiment found that even if it became a system application, it was not automatically restarted after being killed. However, if you set the persistent= "true" for a system application, the situation is different. Experiments show that a system application that has the persistent attribute set will be restarted even if kill is killed. A system application with persistent= "true", with core service priority in Android, is immune to the low memory killer of the system!

OK, said for a long while, only the core service priority application can guarantee to be immediately full of blood resurrection after accidental killing. In order to become a system application, the common application must be signed with the signature file of the target machine, but this has resulted in the application cannot guarantee compatible with all the different vendors ' products. So what should we do? Here is a talk about double-process guard. People on the Internet have also mentioned the method of dual-process protection, but very few can search for similar source code! If you look from the process manager to find that Sina Weibo or 360 satellite TV has two related processes, one of which is the daemon, it can be guessed that these business-grade software also adopted a dual-process daemon.

What is a two-process daemon? As the name implies is two processes to monitor each other, found that the other side hung up immediately restart! Do not know should be such a pair of process is called each other or same boat good, but in short, double-process guard is indeed a solution to the problem! I believe that here, many people are eager to know how to achieve the dual-process guard. This article introduces a two-process protection using the NDK approach, but first of all, the following methods to be introduced, will lose a lot of efficiency, the reaction to the reality is to make the phone's power consumption become larger! But this article is only a hint, I believe that after the reading will be more expert to point out a better way to achieve.

What do you need to know?
The approach to double-process protection in this article is basically a pure NDK development, or it's all implemented in C + + and requires a two-process protection program, just call the Java interface from anywhere in the program. Here are a few things to know:
1.linux in multi-process;
2.unix domain sockets for cross-process communication;
3.linux signal processing;
The use of 4.exec function families;

In fact, these things in itself is not a complex technology, but we put them together to achieve a two-process guardian, not as mysterious as the imagination! Before you formally post the code, here are a few key points for implementing a two-process daemon:
1. How does the parent process monitor the death of the child process (monitoring process)?
Very simply, in Linux, when the child process is terminated, the SIG_CHLD signal is sent to the parent process, so we can install the signal processing function and restart the creation of the monitoring process in this signal processing function;
2. How do child processes (monitoring processes) monitor the death of a parent process?
When the parent process dies, the child process becomes an orphan process adopted by the INIT process, so we can read the parent process PID of the child process in a loop, and when it becomes 1 it means that its parent process is dead, so the parent process can be restarted. This is because of the use of loops, so it leads to the problem of power consumption mentioned earlier.
3. Communication between parent and child processes
There is a way to establish a communication channel between the parent-child process, and then by monitoring this channel to perceive the other side of the existence, so that there is no previous mention of the power consumption problem, in the implementation of this article, in order to simple, or the use of polling the parent process PID method, but still set aside a parent-child process communication channel, although But there is a contingency!

OK, just paste the code below! The first is the Java section, which is too simplistic, just a class that provides API interfaces for external calls to create daemons, and all implementations are done in C + + through the native Method!

Package com.example.dameonservice;
Import java.util.ArrayList;
Import Android.app.ActivityManager;
Import Android.app.ActivityManager.RunningServiceInfo;
Import Android.content.Context;
Import Android.os.Handler;
Import Android.os.HandlerThread;
Import Android.os.Message;
Import Android.util.Log;

/**
* Monitor class, when constructed, will create child processes in native to monitor the current process.
* @author Wangqiang
* @date 2014-04-24
*/
public class Watcher
{
TODO Fix This according to your service
Private static final String package = "com.example.dameonservice/";
Private String Mmonitoredservice = "";
Private volatile Boolean bheartbreak = false;
Private Context Mcontext;
Private Boolean mrunning = true;

public void Createappmonitor (String userId)
{
if (!createwatcher (userId))
{
LOG.E ("Watcher", "<<monitor created failed>>");
}
}

Public Watcher (context context)
{
Mcontext = context;
}

private int isservicerunning ()
{
Activitymanager am= (Activitymanager) Mcontext.getsystemservice (Context.activity_service);
Arraylist<runningserviceinfo> Runningservice = (arraylist<runningserviceinfo>) am.getRunningServices ( 1024);
for (int i = 0; i < runningservice.size (); ++i)
{
if (Mmonitoredservice.equals (Runningservice.get (i). Service.getclassname (). toString ()))
{
return 1;
}
}
return 0;
}

/**
* Native method to create a monitor child process.
* @param userId The user ID of the current process, and the user ID of the current process is required for the child process to restart the current process.
* @return Returns TRUE if child process creation succeeds, otherwise false
*/
Private native Boolean createwatcher (String userId);

/**
* Native method to allow the current process to connect to the monitoring process.
* @return Connection successfully returns TRUE, otherwise false
*/
Private native Boolean connecttomonitor ();

/**
* Native method to send arbitrary information to the monitoring process
* @param information sent to monitor
* @return bytes actually sent
*/
Private native int sendmsgtomonitor (String msg);

Static
{
System.loadlibrary ("Monitor");
}
}


Many of the code's properties are used in testing, too lazy to remove, in fact, some are useless. Just need to care createappmonitor this external interface is available, it requires the user ID of the current process to be passed in, and then calls the Createwatcher local method to create the daemon. There are also two ways to connecttomonitor the socket channel used to create and monitor processes, and sendmsgtomonitor to send data through the socket to the child process. Because there is no need for data interaction with child processes, these two methods do not add external Java interface, but to add is simply a breeze!


Ok,java is just a shell, the internal implementation is also C + +, in order to make the program more object-oriented, in the implementation of native, we use a processbase base class to the parent-child process of an abstraction, the parent-child process will have the behavior of abstraction, and the parent-child process can be implemented in its own way to implement the interface, first to look at this abstract parent-child process common behavior of the Processbase base class:
#ifndef _process_h
#define _process_h


#include <jni.h>
#include <sys/select.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
#include <android/log.h>
#include <sys/types.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include "Constants.h"


#define LOG_TAG "Native"


#define LOGE (...) __android_log_print (android_log_error,log_tag,__va_args__)


/**
* Function: An abstraction of the parent-child process
* @author Wangqiang
* @date 2014-03-14
*/
Class Processbase
{
Public

Processbase ();

/**
* Parent-child processes do not work the same, leaving an abstract interface by the parent-child process
* To achieve it yourself.
*/
virtual void do_work () = 0;

/**
* Processes can create child processes as needed, and if you do not need to create child processes, you can
* This interface is an empty implementation.
*/
virtual bool Create_child () = 0;

/**
* Capture the signal of the child process death, if there is no child process this method can give an empty implementation.
*/
virtual void catch_child_dead_signal () = 0;

/**
* Do anything after the child process dies.
*/
virtual void on_child_end () = 0;

/**
* Create a parent-child process communication channel.
*/
BOOL Create_channel ();

/**
* Set up a communication channel for the process.
* @param file description of the CHANNEL_FD channel
*/
void Set_channel (int channel_fd);

/**
* Write data to the channel.
* @param data written to the channel
* Number of bytes written by @param len
* @return The number of bytes actually written to the channel
*/
int Write_to_channel (void* data, int len);

/**
* Read data from the channel.
* @param data is saved from the channel read-in
* @param the number of bytes read into Len from the channel
* @return The number of bytes actually read
*/
int Read_from_channel (void* data, int len);

/**
* Get the file descriptor for the channel
*/
int Get_channel () const;

Virtual ~processbase ();

Protected

int M_channel;
};


Just a very simple class, I believe the note to see what it means, such as the father and son process may need to capture the signal of his descendants death, so give a catch_child_dead_signal function, if the child process is not interested in the dead and alive, you can give an empty implementation, ignore it, Who calls him heresy? As a result of pure virtual function, so processbase is an abstract class, that is, it can not have its own instance, just to inherit, its descendants may be in different ways to implement its interface in order to show the behavior of the same, where the behavior of the parent process and the child process is different, The following is the first implementation of the parent process for gentlemen:
/**
* Function: implementation of parent process
* @author Wangqiang
* @date 2014-03-14
*/
Class Parent:public Processbase
{
Public

Parent (jnienv* env, Jobject jobj);

virtual bool Create_child ();

virtual void do_work ();

virtual void catch_child_dead_signal ();

virtual void on_child_end ();

Virtual ~parent ();

BOOL Create_channel ();

/**
* Get jnienv of parent process
*/
JNIEnv *get_jni_env () const;

/**
* Get the Java Layer object
*/
Jobject get_jobj () const;

Private

JNIEnv *m_env;

Jobject m_jobj;

};

The above is the definition part, in fact jnienv and jobject basically useless, completely can give shave off, we all when these two attributes do not exist is! The implementation section is as follows:
#include "Process.h"
#include "Utils.h"

extern processbase *g_process;

extern const char* G_userid;

extern jnienv* g_env;

The child process has permission to access the private directory of the parent process, where the socket file for cross-process communication is established
static const char* PATH = "/data/data/com.example.dameonservice/my.sock";

Service Name
static const char* service_name = "Com.example.dameonservice/com.example.dameonservice.myservice";

BOOL Processbase::create_channel ()
{
}

int Processbase::write_to_channel (void* data, int len)
{
Return write (M_channel, data, Len);
}

int Processbase::read_from_channel (void* data, int len)
{
Return read (M_channel, data, Len);
}

int Processbase::get_channel () const
{
return m_channel;
}

void Processbase::set_channel (int channel_fd)
{
M_channel = CHANNEL_FD;
}

Processbase::P rocessbase ()
{

}

Processbase::~processbase ()
{
Close (M_channel);
}

Parent::P arent (jnienv *env, Jobject jobj): m_env (env)
{
LOGE ("<<new parent instance>>");

M_jobj = Env->newglobalref (jobj);
}

Parent::~parent ()
{
LOGE ("<<parent::~parent () >>");

g_process = NULL;
}

void Parent::d o_work ()
{
}

jnienv* parent::get_jni_env () const
{
return m_env;
}

Jobject parent::get_jobj () const
{
return m_jobj;
}

/**
* The parent process creates a channel, which is actually creating a client and trying to
* Connection Server (sub-process)
*/
BOOL Parent::create_channel ()
{
int sockfd;

Sockaddr_un addr;

while (1)
{
SOCKFD = socket (af_local, sock_stream, 0);

if (SOCKFD < 0)
{
LOGE ("<<parent Create Channel failed>>");

return false;
}

memset (&addr, 0, sizeof (addr));

addr.sun_family = af_local;

strcpy (Addr.sun_path, path);

if (Connect (SOCKFD, (sockaddr*) &addr, sizeof (addr)) < 0)
{
Close (SOCKFD);

Sleep (1);

Continue
}

Set_channel (SOCKFD);

LOGE ("<<parent channel FD%d>>", M_channel);

Break
}

return true;
}

/**
* Child process death will emit a sigchld signal, by capturing this signal the parent process can
* Knowing that the child process has died, this function is the processing function of the SIGCHLD signal.
*/
static void Sig_handler (int signo)
{
pid_t pid;

int status;

SIGCHLD called wait waits for the child process to die
Signal to bury the child process and prevent it from becoming a zombie process
PID = Wait (&status);

if (g_process! = NULL)
{
G_process->on_child_end ();
}
}

void Parent::catch_child_dead_signal ()
{
LOGE ("<<process%d install child dead signal detector!>>", getpid ());

struct Sigaction sa;

Sigemptyset (&sa.sa_mask);

sa.sa_flags = 0;

Sa.sa_handler = Sig_handler;

Sigaction (SIGCHLD, &sa, NULL);
}

void Parent::on_child_end ()
{
LOGE ("<<on_child_end:create A new Child process>>");

Create_child ();
}

BOOL Parent::create_child ()
{
pid_t pid;

if ((PID = fork ()) < 0)
{
return false;
}
else if (PID = = 0)//Sub-process
{
LOGE ("<<in child process,pid=%d>>", Getpid ());

Child child;

processbase& ref_child = child;

Ref_child.do_work ();
}
else if (PID > 0)//parent process
{
LOGE ("<<in parent process,pid=%d>>", Getpid ());
}

return true;
}
Here's a description of the three global variables:
G_process is a pointer to the parent process;
G_userid is the parent process user ID, passed by the Java side, we need to save it with a global variable, because the child process to restart the parent process needs to use the user ID, otherwise there will be problems, of course, this is also due to the child process can inherit the global variables of the parent process the fact!
G_env is a pointer to JNIEnv, which also takes this variable as a global variable, which is reserved for the child process;


The parent process creates a child process with fork in the Create_child, which is actually a fork call, and then the parent process does nothing, and the child process creates a children object and calls its do_work to start doing what it has to do!


The parent process implements the Catch_child_dead_signal, where the SIG_CHLD signal processing function is installed, because he loves his son and cares for him all the time. In the signal processing function Sig_handler, we notice the wait call, which is to prevent the child process to become a zombie process after death, because we already know that the parent process will only create a child monitoring process, so wait is enough, do not need to waitpid function in person! and the signal processing function is very simple, recall on_child_end, in which again Create_child and his Dear lady make a little baby on it!


Finally, the Create_channel function, which he used to create and sub-process socket channels, is very kind and familiar to people with network programming experience, and he follows standard network programming client steps: Creating Socket,connect, Then the data is OK, but the protocol here is af_local, indicating that we are going to communicate across the process. Because the domain socket is not an IP address, but a specified file to communicate with the target process, the parent-child process needs this file, so the location of this file is specified where also need to note: On a phone without root, almost all files are not write permission, Fortunately, however, the child process of Linux shares the parent process's directory, so assigning this location to a private directory applied under/data/data/allows the parent-child process to access the file!

The next step is the implementation of the child process, which is defined as follows:
/**
* Implementation of child processes
* @author Wangqiang
* @date 2014-03-14
*/
Class Child:public Processbase
{
Public

Child ();

Virtual ~child ();

virtual void do_work ();

virtual bool Create_child ();

virtual void catch_child_dead_signal ();

virtual void on_child_end ();

BOOL Create_channel ();

Private

/**
* Handling Parent process death events
*/
void Handle_parent_die ();

/**
* Listen for messages sent by the parent process
*/
void Listen_msg ();

/**
* Restart the parent process.
*/
void Restart_parent ();

/**
* Processing messages from the parent process
*/
void handle_msg (const char* msg);

/**
* Thread function to detect if the parent process is dead
*/
void* Parent_monitor ();

void Start_parent_monitor ();

/**
* The function of this consortium is to help use the member functions of the class as thread functions
*/
Union
{
void* (*THREAD_RTN) (void*);

void* (Child::* Member_rtn) ();
}rtn_map;
};
#endif
Notice that there is a union in it that is intended to assist in passing a class member function as a thread function to pthread_create, and many times we want the thread to be able to access the private members of the class as if it were a member function. With friend Although can do this, but always feel not graceful enough, because the member function implies the this pointer, so that we can completely use a member function as a thread function. Just because the compiler blocked the type conversion of the function pointer, we had to use a struct here.

Nonsense not much to say, look at the implementation of the child process:
BOOL Child::create_child ()
{
The child process does not need to create the child process again, this function is left blank
return false;
}

Child::child ()
{
Rtn_map.member_rtn = &child::p arent_monitor;
}

Child::~child ()
{
LOGE ("<<~child (), unlink%s>>", PATH);

Unlink (PATH);
}

void Child::catch_child_dead_signal ()
{
Child processes do not need to capture SIGCHLD signals
Return
}

void Child::on_child_end ()
{
Child processes do not need to be processed
Return
}

void Child::handle_parent_die ()
{
The child process becomes the orphan process and waits for subsequent processing after being adopted by the INIT process
while (Getppid ()! = 1)
{
Usleep (500); Sleep 0.5ms
}

Close (M_channel);

Restarting the parent Process service
LOGE ("<<parent died,restart now>>");

Restart_parent ();
}

void Child::restart_parent ()
{
LOGE ("<<restart_parent enter>>");

/**
* TODO Restarts the parent process, allowing the app to be restarted by launching any component of Java Space (service or activity, etc.) through AM
*/
EXECLP ("AM",
"AM",
"StartService",
"--user",
G_userid,
"-N",
SERVICE_NAME,//Note the name here
(char *) NULL);
}

void* Child::p arent_monitor ()
{
Handle_parent_die ();
}

void Child::start_parent_monitor ()
{
pthread_t Tid;

Pthread_create (&tid, NULL, Rtn_map.thread_rtn, this);
}

BOOL Child::create_channel ()
{
int LISTENFD, CONNFD;

struct Sockaddr_un addr;

LISTENFD = socket (af_local, sock_stream, 0);

Unlink (PATH);

memset (&addr, 0, sizeof (addr));

addr.sun_family = af_local;

strcpy (Addr.sun_path, path);

if (Bind (LISTENFD, (sockaddr*) &addr, sizeof (addr)) < 0)
{
LOGE ("<<bind Error,errno (%d) >>", errno);

return false;
}

Listen (LISTENFD, 5);

while (true)
{
if ((CONNFD = accept (LISTENFD, NULL, NULL)) < 0)
{
if (errno = = eintr)
Continue
Else
{
LOGE ("<<accept error>>");

return false;
}
}

Set_channel (CONNFD);

Break
}

LOGE ("<<child channel FD%d>>", M_channel);

return true;
}

void child::handle_msg (const char* msg)
{
TODO How to handle message was decided by you.
}

void Child::listen_msg ()
{
Fd_set RfDs;

int retry = 0;

while (1)
{
Fd_zero (&rfds);

Fd_set (M_channel, &rfds);

Timeval Timeout = {3, 0};

int r = Select (M_channel + 1, &rfds, NULL, NULL, &timeout);

if (R > 0)
{
Char pkg[256] = {0};

if (Fd_isset (M_channel, &rfds))
{
Read_from_channel (pkg, sizeof (pkg));

LOGE ("<<a message comes:%s>>", pkg);

Handle_msg ((const char*) pkg);
}
}
}
}

void Child::d o_work ()
{
Start_parent_monitor (); Start the Monitoring thread

if (Create_channel ())//Waits and processes messages sent from the parent process
{
Listen_msg ();
}
}


In his do_work, the child process first creates a thread that polls the PID of its parent process, and if found to be 1, calls Restart_parent, where EXECLP is invoked, executing a component on the Java side of the AM instruction to enable the restart of the parent process! Here please pay attention to the EXECLP in the parameters, with--user and add the previous we saved in the global variables of the user ID, if not add this option, you can not restart the parent process, I spent a long time oh!


The remaining work of the child process is simple, create the channel, listen to the message from the parent process, here we use Select to listen, because there is actually only one client (parent process), so with select a bit off the pants fart, the simple problem complicated suspicion, but in fact there is not much impact!


With the above implementations, the implementation of JNI is quite simple:
#include "Process.h"
#include "Utils.h"


/**
* global variable that represents the application process.
*/
Processbase *g_process = NULL;

/**
* The UID of the application process.
*/
Const char* G_userid = NULL;

/**
* Global jnienv, which sometimes is used by sub-processes.
*/
jnienv* g_env = NULL;

extern "C"
{
Jniexport Jboolean jnicall java_com_example_dameonservice_watcher_createwatcher (JNIEnv*, Jobject, jstring);

Jniexport Jboolean jnicall java_com_example_dameonservice_watcher_connecttomonitor (JNIEnv*, jobject);

Jniexport jint jnicall java_com_example_dameonservice_watcher_sendmsgtomonitor (jnienv*, Jobject, jstring);

Jniexport jint jnicall jni_onload (javavm*, void*);
};

Jniexport Jboolean jnicall java_com_example_dameonservice_watcher_createwatcher (JNIEnv* env, Jobject thiz, jstring User)
{
g_process = new Parent (env, thiz);

G_userid = (const char*) jstringtostr (env, user);

G_process->catch_child_dead_signal ();

if (!g_process->create_child ())
{
LOGE ("<<create child error!>>");

return jni_false;
}

return jni_true;
}


Jniexport Jboolean jnicall java_com_example_dameonservice_watcher_connecttomonitor (JNIEnv* env, Jobject thiz)
{
if (g_process! = NULL)
{
if (G_process->create_channel ())
{
return jni_true;
}

return jni_false;
}
}

To integrate the above code, a two-process daemon implementation is complete, only need to call Watcher.java Createappmonitor, your application will have a daemon to monitor, killed after death will be restarted immediately! Isn't it interesting?

Android for dual process daemon (GO)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.