Android implements daemon through JNI _android

Source: Internet
Author: User
Tags sprintf stringbuffer

The development of a need to live in the background of the app is actually a very headache, not only to deal with the major domestic manufacturers ROM, but also need to deal with various types of safety butler ... Although the continuous research of various methods, but the effect is not good, such as task Manager to kill the app, the service can not get up ...

After a search on the Internet, the main methods are the following methods, but the symptoms are not the root causes:

1, Improve service priority: this, can only say that in the system is not enough memory to recover resources, higher priority, not easy to be recycled, but also eggs ...

2, improve the service in the process of priority: the effect is not very obvious

3, in the OnDestroy method to restart service: This is quite effective a method, but, directly kill the process, OnDestroy methods are not in, not to restart the

4, broadcast broadcast: And the 3rd kind, did not enter the OnDestroy, did not know when sends the broadcast, moreover, in Android4.4 above, after the program completely exits, is not good to receive the broadcast, needs in the broadcast place specific processing

5, put under the System/app as a system application: this is usually play, not much practical significance.

6, Service Onstartcommand method, return to Start_sticky, this is mainly against the system resources caused by the lack of service is closed, there is a certain reason.

The way to deal with it is that it is more cumbersome to achieve. If you can customize the ROM yourself, there are many ways to do it, such as adding your application to the whitelist, or installing an app with no icon as the daemon ... But, how can everything be customized, for Android developers, the problem must be compromised ~

Then, there is no way in an app, open a child thread, after the main thread has been killed, the child thread through the monitoring, polling and other ways to determine whether the service exists, does not exist to open the service. The answer naturally is yes, through the JNI Way (NDK programming), fork () out a child thread as a daemon, polling the listening service status. A daemon (Daemon) is a special process running in the background. It is independent of the control terminal and periodically performs a task or waits to handle certain occurrences. The session group of the daemon and the current directory, the file descriptor is independent. Background running is only a terminal for a fork, let the program in the background to perform, these have not changed.

Then we first look at Android4.4 source code, Activitymanagerservice (source/frameworks/base/services/core/java/com/android/server/am/ Activitymanagerservice.java) is how to turn off cleaning up memory after the application exits:

Process.killprocessquiet (PID);

When the application exits, Activitymanagerservice kills the main process, but in Android5.0, Activitymanagerservice does this:

Process.killprocessquiet (APP.PID);
Process.killprocessgroup (App.info.uid, app.pid);

Just a word, but the difference is very big. Android5.0 after the application exits, the Activitymanagerservice not only kills the main process, but also kills the process group that the main process belongs to, so that as the subprocess and the main process are in the same process group, what the child process is doing stops ... How else to say Android5.0 has made a lot of updates on security ...

Then, there is no way to let the child process out of the main process is not affected, of course, it is also possible. So, how do you do it in the C + + layer? First on the key code:

/** * srvname Process Name * SD file path to create the PID written by the child process/int start (int argc, char* srvname, char* SD) {pthread_t ID;
 int ret;

 struct Rlimit R;
 int pid = fork ();
 Logi ("Fork pid:%d", PID);
 if (PID < 0) {Logi ("the fork () error PID%d,so exit", PID);
 Exit (0);
 else if (PID!= 0) {Logi ("the" A (): I ' am Father pid=%d ", Getpid ());
 Exit (0);
 else {//the first subprocess Logi ("One fork (): I ' am Child pid=%d", Getpid ());
 Setsid ();
 Logi ("The Fork (): setsid=%d", Setsid ()); Umask (0);
 Give the file more permissions because the inherited file may have certain permissions that are masked int pid = fork ();
 if (PID = = 0) {//second subprocess//here in fact, in order to prevent the repetition of opening the thread, should have the corresponding processing logi ("I am child-child pid=%d", Getpid ()); ChDir ("/"); <span style= "font-family:arial, Helvetica, Sans-serif;"
 > Modify the process working directory to the root directory, chdir ("/") </span>//Close the unwanted file descriptor inherited from the parent process.
 if (R.rlim_max = = rlim_infinity) {R.rlim_max = 1024;
 } int i;
 for (i = 0; i < R.rlim_max. i++) {close (i);
 } umask (0); ret = Pthread_create (&id, NULL, (void *) thread, srvname); Open thread, polling to listen for startup service IF (Ret!= 0) {printf ("Create pthread error!\n");
 Exit (1);
 int STDFD = open ("/dev/null", O_RDWR);
 Dup2 (STDFD, Stdout_fileno);
 Dup2 (STDFD, Stderr_fileno);
 else {exit (0);
} return 0; /** * Start service/void Java_com_yyh_fork_nativeruntime_startservice (jnienv* env, Jobject thiz, jstring Cchrptr_proc Essname, jstring sdpath) {char * RTN = jstringtostring (env, cchrptr_processname);//Get Process name char * SD = jstringtostring
 (env, Sdpath); Logi ("Java_com_yyh_fork_nativeruntime_startservice run ....")
 Processname:%s ", RTN);
 A = RTN;
Start (1, RTN, SD);

 }

Here are a few key points to understand:

1, why to fork two times? The first fork function is for the back Setsid service. The Setsid caller cannot be the process group leader (group leader), and the parent process is the process group leader the first time it is invoked. After the second call, the first fork out of the child process exit, so that the second fork out of the child process, and they out of the relationship.

2, Setsid () What is the role? Setsid () makes the second subprocess the session leader (SID==PID), the process group leader (Pgid = pid), and is detached from the original control terminal. So no matter how the control terminal operates, the new process will normally not receive the signals he sends.

3, the role of Umask (0): Because the child process inherited from the parent process of something, may not inherit permissions, so to give him higher permissions to facilitate child process operations.

4, ChDir ("/"); function: When the process is active, the file system where the working directory resides cannot be removed, and the working directory needs to be changed to the root directory.

5, the process inherits the open file descriptor from the parent process that created it. Without shutting down, system resources will be wasted, causing the file system in which the process is located to fail to unload and cause unexpected errors. So in the end, remember to close the file descriptor that inherits from the parent process.

Then, the thing to do after opening the thread in the above code is to loop to StartService (), the code is as follows:

 void thread (char* srvname) {while (1) {check_and_restart_service (srvname);//should be judged by SE
 Rvice state, here has been restart is the deficiency of sleep (4);
 }/** * Detection service, starting if no service exists. * Through the AM command to start a laucher service, the Laucher service is responsible for the main service detection, Laucher service after detection automatically exit * * void Check_and_restart_service (char* service) {Logi (
 "Current Process pid=", getpid ());
 Char cmdline[200];
 sprintf (CmdLine, "AM startservice--user 0-n%s", service);
 Char tmp[200];
 sprintf (tmp, "cmd=%s", cmdline);
 Executecommandwithpopen (CmdLine, TMP, 200);
Logi (TMP, LOG);
 /** * Execute command/void Executecommandwithpopen (char* command, char* out_result, int resultbuffersize) {FILE * FP;
 Out_result[resultbuffersize-1] = ' the ';
 fp = popen (Command, "R");
 if (FP) {fgets (Out_result, resultBufferSize-1, FP);
 Out_result[resultbuffersize-1] = ' the ';
 Pclose (FP);
 else {logi ("Popen null,so exit");
 Exit (0); }
}

These two functions to start the service, which involves some Android and Linux commands, here I will not elaborate. Especially am, very powerful features, not only can open the service, you can also open the radio and so on ... Then call Ndk-build command to compile, generate so library, NDK will not, its own Baidu slightly ~

The key part of the C + + terminal is the above, and naturally, the Java side has to be executed together.

First, take a look at the loading classes of the so library that the C + + code compiles and the native calls:

Package com.yyh.fork;
Import Java.io.DataInputStream;
Import Java.io.DataOutputStream;

Import Java.io.File;

 public class Nativeruntime {private static nativeruntime theinstance = null; Private Nativeruntime () {} public static Nativeruntime getinstance () {if (theinstance = = null) Theinstance = new
 Nativeruntime ();
 return theinstance;  /** * runexecutable to start a lib*.so file * * @date 2016-1-18 pm 8:22:28 * @param pacaagename * @param filename * @param  Alias alias * @param args parameter * @return/public string runexecutable (string pacaagename, string filename, string alias,
 String args) {string path = '/data/data/' + pacaagename;
 String cmd1 = path + "/lib/" + filename;
 String cmd2 = path + "/" + alias;
 String cmd2_a1 = path + "/" + alias + "" + args;
 String cmd3 = "chmod 777" + cmd2;
 String CMD4 = "dd if=" + cmd1 + "of=" + cmd2;

 StringBuffer Sb_result = new StringBuffer (); if (!new File ("/data/data/" + alias). Exists ()) {Runlocalusercommand (Pacaagename, CMD4, Sb_result);
 Copy lib/libtest.so to the previous level directory and named Test.
 Sb_result.append (";"); } runlocalusercommand (Pacaagename, Cmd3, Sb_result);
 Change the properties of test to become executable sb_result.append (";"); Runlocalusercommand (Pacaagename, CMD2_A1, Sb_result);
 Execute the test program.
 Sb_result.append (";");
 return sb_result.tostring (); /** * Execute local User command * @date 2016-1-18 afternoon 8:23:01 * @param pacaagename * @param command * @param sb_out_result * @retur n */Public boolean Runlocalusercommand (string pacaagename, String command, StringBuffer sb_out_result) {Process proce
 SS = NULL; try {process = Runtime.getruntime (). EXEC ("sh");//Get the shell process datainputstream InputStream = new DataInputStream (process.
 getInputStream ());
 DataOutputStream outputstream = new DataOutputStream (Process.getoutputstream ()); Outputstream.writebytes ("cd/data/data/" + pacaagename + "\ n"); Ensure that the command is executed in its own data directory to have permission to write files to the current directory outputstream.writebytes (Command + "&\n");
 Let the program run in the background, the front desk immediately return to Outputstream.writebytes ("exit\n"); OutputstreaM.flush ();
 Process.waitfor ();
 byte[] buffer = new byte[inputstream.available ()];
 Inputstream.read (buffer);
 string s = new string (buffer);
 if (Sb_out_result!= null) sb_out_result.append ("CMD result:\n" + s);
 The catch (Exception e) {if (Sb_out_result!= null) sb_out_result.append ("Exception:" + e.getmessage ());
 return false;
 return true;

 public native void StartActivity (String compname);

 Public native String Stringfromjni ();

 public native void StartService (string srvname, string sdpath);

 public native int findprocess (String packname);

 public native int StopService ();
 static {try {system.loadlibrary ("helper");//load So library} catch (Exception e) {e.printstacktrace ();

 }
 }

}

Then, we start the service after we receive the power-on broadcast.

Package com.yyh.activity;

Import Android.content.BroadcastReceiver;
Import Android.content.Context;
Import android.content.Intent;
Import Android.util.Log;

Import Com.yyh.fork.NativeRuntime;
Import Com.yyh.utils.FileUtils;
public class Phonestatreceiver extends Broadcastreceiver {

 private String tag = "tag";

 @Override public
 void OnReceive (context context, Intent Intent) {
 if (Intent.ACTION_BOOT_COMPLETED.equals ( Intent.getaction ()) {
 log.i (TAG, "mobile phone switched on ~ ~");
 Nativeruntime.getinstance (). StartService (Context.getpackagename () + "/com.yyh.service.hostmonitor", Fileutils.createrootpath ());
 } else if (Intent.ACTION_USER_PRESENT.equals (Intent.getaction ()))}

 
}

Service, you can do what you have to do.

Package com.yyh.service;

Import Android.app.Service;
Import android.content.Intent;
Import Android.os.IBinder;
Import Android.util.Log;

public class Hostmonitor extends Service {

 @Override public
 void OnCreate () {
 super.oncreate ();
 LOG.I ("Daemon_java", "hostmonitor:oncreate! I can not be killed! ");
 }

 @Override public
 int Onstartcommand (Intent Intent, int flags, int startid) {
 log.i ("Daemon_java", " hostmonitor:onstartcommand! I can not be killed! ");
 Return Super.onstartcommand (Intent, flags, Startid);
 }

 @Override public
 IBinder onbind (Intent arg0) {return
 null;
 }
}

Of course, do not forget to configure receiver and service in Manifest.xml files:

<receiver
   android:name= "com.yyh.activity.PhoneStatReceiver"
   android:enabled= "true"
   Android: permission= "Android.permission.RECEIVE_BOOT_COMPLETED" >
   <intent-filter>
    <action android: Name= "Android.intent.action.BOOT_COMPLETED"/>
    <action android:name= "Android.intent.action.USER_" PRESENT "/>
   </intent-filter>
  </receiver>
  
  <service android:name=" Com.yyh.service.HostMonitor "
    android:enabled=" true "
    android:exported=" true ">
   </service >

Run up, in the program application, the end of this process, will be, and automatically up to ~ ~ ~ ~ ~ ~ ~ ~ with rogue software, yes, is so cheap, is so overbearing!!

This side is running on Google's native system, the Android version is 5.0 ... To sum up: the service resident to deal with is not a variety of difficult technology, but the large ROM. QQ Why not be killed, is because the domestic big ROM do not want to let him die ...

This article is mainly to provide a train of thought, realize there are many deficiencies, rookie of the work, do not like to spray.

Finally, attach the source code of this example: Android realizes dual daemon through JNI

The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.

Related Article

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.