一.准备工作:

因为我们的工程会包含binder库和binder间接依赖的cutils库,但这些库都不在Ndk里面,直接在native层去编译会找不到,所以要放在源码环境下编译。

#include <binder/MemoryHeapBase.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>

在源码下的/packages/experimental/ 目录(这个目录是用来创建系统服务程序的,既可以创建android的app,也可以创建库、可执行文件等,都是用Android.mk来编译,源码里面也有一些例子,创建的目标文件都是基于android系统环境)下创建一个文件夹,比如叫‘nativeService26’,把代码和Android.mk都放进去,接着在aosp目录(源码要make过的)下执行:

source build/envsetup.sh
mmm  /packages/experimental/nativeService26

以创建一个可执行文件为例,创建成功后,会在下面路径生成一个二进制文件,拿过来用就是了。这里有个点额外说明一下,之前想在这里编译个so文件在应用层去用,后来发现不行,运行的时候会关联很多系统库文件,所以就编译一个二进制文件出来。

out/target/product/generic/system/bin/native_service26

二.代码:
主要是这样的一个流程:

int main(int argc, char* argv[])
{
    android::ProcessState::self()->startThreadPool();
    sp<IBinder> am = getActivityManagerService();
    startService(); 
    return 1;
}

①先启动一个binder线程池,因为我们执行这个二进制文件时是fork出来的子进程中执行的,还没有binder线程池,要自己先手动启动一个,相关的代码在binder/IPCThreadState.h 这里。 ②从native层拿到AMS,通过ServiceManager去拿到,相关的代码在binder/IServiceManager.h 里面。
③创建Parcel,通过AMS把数据传递给系统服务:

Parcel data , reply;
    String16 des = String16("android.app.IActivityManager");
    data.writeInterfaceToken(des);
    data.writeStrongBinder(NULL); 
    // intent.writeToParcel 
    data.writeString16(NULL, 0);  // action
    data.writeInt32(NULL_TYPE_ID); //uri 
    data.writeString16(NULL, 0);              /* type */
    data.writeInt32(0);    // flag
    data.writeString16(NULL, 0); // package
    data.writeString16(String16("com.example.myapplication"));  //component package
    data.writeString16(String16("com.example.myapplication.DemoService"));
    data.writeInt32(0);               /* source bound - size */
    data.writeInt32(0);               /* Categories - size */
    data.writeInt32(0);               /* selector - size */
    data.writeInt32(0);                /* ClipData */
    data.writeInt32(-1);          // UserHint           
    data.writeInt32(-1);      /* bundle(extras) size */
    // intent datas end
    data.writeString16(NULL, 0);  /* resolvedType */
    data.writeString16(String16("com.example.myapplication"));  // calling package
    data.writeInt32(false);       /* userid */
    status_t ret = am->transact(START_SERVICE_TRANSACTION, data, &reply);

这里和从java层去startService的数据写入过程是一一对应的,看上去比较多是因为intent的数据也直接写上去了,intent.writeToParcel其实写了很多数据进去:

public void writeToParcel(Parcel out, int flags) {
    out.writeString(mAction);
    Uri.writeToParcel(out, mData);
    out.writeString(mType);
    out.writeInt(mFlags);
    out.writeString(mPackage);
    ComponentName.writeToParcel(mComponent, out);
    if (mSourceBounds != null) {
        out.writeInt(1);
        mSourceBounds.writeToParcel(out, flags);
    } else {
        out.writeInt(0);
    }
    if (mCategories != null) {
        final int N = mCategories.size();
        out.writeInt(N);
        for (int i=0; i<N; i++) {
            out.writeString(mCategories.valueAt(i));
        }
    } else {
        out.writeInt(0);
    }
    if (mSelector != null) {
        out.writeInt(1);
        mSelector.writeToParcel(out, flags);
    } else {
        out.writeInt(0);
    }
    if (mClipData != null) {
        out.writeInt(1);
        mClipData.writeToParcel(out, flags);
    } else {
        out.writeInt(0);
    }
    out.writeInt(mContentUserHint);
    out.writeBundle(mExtras);
}

三.保活方案:
主要是参考了MarsDaemon (https://github.com/Marswin/MarsDaemon),这个保活模块还是非常强大的,主要是在进程之间使用文件锁的方法,一个进程死了之后,另外一个进程会马上得到文件锁,感知到死亡之后,马上去拉起那个进程,其实还是要跟系统争取时间。但是在初始化的时候要先把一些准备工作做好,比如他的代码里,先把广播的parcel的数据先填充好,在native层感知到进程死亡之后马上回调java层的方法,把广播或者是service发出去,这样就能节省很多时间。

@SuppressLint("Recycle")// when process dead, we should save time to restart and kill self, don`t take a waste of time to recycle
	private void initBroadcastParcel(Context context, String broadcastName){
		Intent intent = new Intent();
        ComponentName componentName = new ComponentName(context.getPackageName(), broadcastName);
        intent.setComponent(componentName);
        intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
		
		/*	 
//      Object contextImpl = ((Application)context.getApplicationContext()).getBaseContext();
        //this context is ContextImpl, get MainThread instance immediately
        Field mainThreadField = context.getClass().getDeclaredField("mMainThread");
        mainThreadField.setAccessible(true);
      	Object mainThread = mainThreadField.get(context);
      	//get ApplicationThread instance
      	Object applicationThread = mainThread.getClass().getMethod("getApplicationThread").invoke(mainThread);  
      	//get Binder
      	Binder callerBinder = (Binder) (applicationThread.getClass().getMethod("asBinder").invoke(applicationThread));
		 */
      
//      	UserHandle userHandle = android.os.Process.myUserHandle();
//      	int handle = (Integer) userHandle.getClass().getMethod("getIdentifier").invoke(userHandle);
		
      	mBroadcastData = Parcel.obtain();
      	mBroadcastData.writeInterfaceToken("android.app.IActivityManager");
//      	mBroadcastData.writeStrongBinder(callerBinder);
      	mBroadcastData.writeStrongBinder(null);
      	intent.writeToParcel(mBroadcastData, 0);
      	mBroadcastData.writeString(intent.resolveTypeIfNeeded(context.getContentResolver()));
      	mBroadcastData.writeStrongBinder(null);
      	mBroadcastData.writeInt(Activity.RESULT_OK);
      	mBroadcastData.writeString(null);
      	mBroadcastData.writeBundle(null);
      	mBroadcastData.writeString(null);
      	mBroadcastData.writeInt(-1);
      	mBroadcastData.writeInt(0);
      	mBroadcastData.writeInt(0);
//      	mBroadcastData.writeInt(handle);
      	mBroadcastData.writeInt(0);
	}

数据准备好,需要发送广播的时候随时发送:

private boolean sendBroadcastByAmsBinder(){	
		try {
			if(mRemote == null || mBroadcastData == null){
				Log.e("Daemon", "REMOTE IS NULL or PARCEL IS NULL !!!");
				return false;
			}
			mRemote.transact(14, mBroadcastData, null, 0);//BROADCAST_INTENT_TRANSACTION = 0x00000001 + 13
			return true;
		} catch (RemoteException e) {
			e.printStackTrace();
			return false;
		}
	}

MarsDaemon兼容到android6,在7和7以后,因为Android修改了killProcess的方法(具体可以去研究下源码,边幅有限,不在这里细说),7以后杀进程的速度会快很多,主要是killProcessQuiet和killProcessGroup这两个方法,7以前是同步的,7的时候就变成异步了,java进程很快就被干掉了,这个时候还去回调Java层的方法,时间上就来不及了。所以在native层也先把数据startService需要的parcel数据准备好,感知到进程挂掉的时候直接去startService,也就是执行一句:

am->transact(START_SERVICE_TRANSACTION, data, &reply);

把数据提交给远程的系统服务,这样就能够在极短的时间内把另外一个进程拉起,可以做到即便是force-stop也难以杀死:

android10 服务保活 android service 保活_startService

但是只能在原生系统上保活,厂商毕竟会修改一些源码,导致保活不成功,所以为了兼容不同厂商的机子,还需要做一些适配