前言:这个实例源于读写serial串口设备数据的需求,开机后开启一个系统服务SystemService,里面含有Handler handleMessage、发送和接收Broadcast、Thread、静态注册jni 实例,对理解service和jni有所帮助。
一、添加 SystemService文件,里面含有Handler handleMessage、发送和接收Broadcast、Thread、jni静态注册和service连接frameworks\base\services\core\java\com\android\server\power\GiadaService.java
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.power;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.OsProtoEnums;
import android.os.PowerManager;
import android.util.Slog;
import android.util.Log;
import com.android.internal.os.CachedDeviceState;
import com.android.server.SystemService;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.*;
import android.net.wifi.WifiManager;
import android.os.SystemProperties;
import android.os.Handler;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Looper;
import android.os.Message;
import android.annotation.NonNull;
public final class GiadaService extends SystemService {
private static final String TAG = "GiadaService";
private static final int TIME_MESSAGE = 100;
private ReadThread mReadThread;
int timeCount = 0;
private Handler timeHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case TIME_MESSAGE:
final int number = (int) msg.obj;
Slog.d(TAG, "Received message-TIME_MESSAGE number="+number);
if(number == 100){
Intent tempwarning = new Intent("PNW.clickedTempWarning");
getContext().sendBroadcast(tempwarning);
}
break;
default:
break;
}
}
};
private class ReadThread extends Thread {
@Override
public void run() {
super.run();
while(!isInterrupted()) {
try {
Thread.sleep(1000);
timeCount++;
Message msg = timeHandler.obtainMessage(TIME_MESSAGE, timeCount);
msg.setAsynchronous(true);
msg.sendToTarget();
Slog.d(TAG, "obtainMessage sendToTarget,timeCount:" + timeCount);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
}
//final EthernetServiceImpl mImpl;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Slog.wtf(TAG, "BroadcastReceiver onReceive action:"+intent.getAction());
switch (intent.getAction()) {
case "android.intent.giada.sendbroadcast":
Intent tempwarning = new Intent("PNW.clickedTempWarning");
getContext().sendBroadcast(tempwarning);
break;
case "android.intent.giada.stopthread":
if (mReadThread != null)
mReadThread.interrupt();
Slog.wtf(TAG, "BroadcastReceiver getString():"+getString());
Slog.wtf(TAG, "BroadcastReceiver setString():"+setString("SHIT"));
break;
}
}
};
public GiadaService(Context context) {
super(context);
Log.i(TAG, "GiadaService context!" );
//mImpl = new EthernetServiceImpl(context);
}
@Override
public void onStart() {
Log.i(TAG, "onStart,creat thread!" );
mReadThread = new ReadThread();
mReadThread.start();
//publishBinderService(Context.ETHERNET_SERVICE, mImpl);
}
@Override
public void onBootPhase(int phase) {
Log.i(TAG, "onBootPhase" );
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY){
Log.i(TAG, "onBootPhase PHASE_SYSTEM_SERVICES_READY" );
final IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.giada.sendbroadcast");
filter.addAction("android.intent.giada.stopthread");
getContext().registerReceiver(mBroadcastReceiver, filter);
}
}
public static native String getString();
public static native String setString(String str);
}
二、开启服务,在frameworks\base\services\java\com\android\server\SystemServer.java startBootstrapServices函数里面添加下面内容开机服务
t.traceBegin("StartGiadaService");
mSystemServiceManager.startService(GiadaService.class);
t.traceEnd();
?三、JNI 部分,在 frameworks/base/services/core/jni/下面添加 两个文件com_android_server_power_GiadaService.c和com_android_server_power_GiadaService.h
? ? ? ?3.1、com_android_server_power_GiadaService.c
/*
* Copyright 2009-2011 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>
#include "android/log.h"
#include "com_android_server_power_GiadaService.h"
static const char *TAG="SERIAL-PORT_JNI";
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_getString(JNIEnv *env, jclass cls)
{
LOGD("Java_com_android_server_power_GiadaService_getString\r\n");
return (*env)->NewStringUTF(env, "getString,return hello from jni");
}
JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_setString(JNIEnv *env, jclass cls, jstring str)
{
const char *g_str = (*env)->GetStringUTFChars(env,str, NULL);
LOGD("Java_com_android_server_power_GiadaService_setString jni string=%s \r\n",g_str);
if (g_str != NULL) {
(*env)->ReleaseStringUTFChars(env, str, g_str);
}
return (*env)->NewStringUTF(env, "setString,return hi from jni");
}
? ? ? ?3.2、com_android_server_power_GiadaService.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_android_server_power_GiadaService */
#ifndef _Included_com_android_server_power_GiadaService
#define _Included_com_android_server_power_GiadaService
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_android_server_power_GiadaService
* Method: getString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_getString
(JNIEnv *, jclass);
/*
* Class: com_android_server_power_GiadaService
* Method: setString
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_setString
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
3、在 frameworks/base/services/core/jni/Android.mk 里面添加"com_android_server_power_GiadaService.c" 去编译编译com_android_server_power_GiadaService.c
4、这里jni中的写法是
JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_getString ? (JNIEnv *, jclass);这种写法不需要再手动注册native方法,如果是其他写法,需要在.c中添加手动注册方法,并在onload.cpp中注册。
四、烧录真机测试,通过adb shell am broadcast -a "android.intent.giada.sendbroadcast"命令发从广播,服务里面接受到该广播后再发一个广播调出提示窗口;通过adb shell am broadcast -a "android.intent.giada.stopthread" 发送一个广播,服务里面接收到该广播后会通过interrupt退出线程,并且调用jni接口getString和setString,通过log上看有SERIAL-PORT_JNI的相关打印,说明已经成功调用到jni接口,这种事静态注册jni,相对动态注册jni比较简单。
?
五、整个过程中涉及到的文件
六、非常有价值的参考文章
1、SystemService与jni的静态注册连接,用于串口通信
android 系统添加jni,注册本地方法_王不六的博客-CSDN博客_android 系统服务jni
2、SystemService里面publishBinderService的栗子
android 10 添加系统服务步骤_青春给了狗的博客-CSDN博客_android 系统服务
1.编写.aidl文件
存放位置:frameworks/base/core/java/android/os
package android.os;
interface ITEST {
//此处注意 如果参数中有数组类型记得加入out 和in关键字
// int test(in byte【】 a)
void test();
}
---------------------------------------------------------------------------------------------------------------------------------
2、将.aidl文件添加到 frameworks/base/Android.mk下的 LOCAL_SRC_FILES
(此处先make update-api生成对应的文件 Frameworks/base/api/current.txt中查看是否存在对应ITEST接口)
注意:Android 9以上是添加到frameworks/base/ Android.bp
存放位置:framework/base
java_defaults {
name: "framework-defaults",
installable: true,
srcs: [
...省略
"core/java/android/os/ITEST.aidl",
...省略
---------------------------------------------------------------------------------------------------------------------------------
3、Context.java添加服务注册名称, 添加该服务名称, 用于快捷注册和快捷引用
修改位置:frameworks/base/core/java/android/content/
/**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.TestManager} for controlling power management,
* including "wake locks," which let you keep the device on while
* you're running long tasks.
*/
public static final String ITEST_SERVICE = "itest";
/** @hide */
@StringDef(suffix = { "_SERVICE" }, value = {
POWER_SERVICE,
ITEST_SERVICE
....,}
---------------------------------------------------------------------------------------------------------------------------------
4、新建TestService.java和TestManager.java
存放位置:frameworks\base\services\core\java\com\android\server\TestService.java
frameworks\base\core\java\android\app\TestManager.java
//TestManager 类
package android.app;
import android.content.Context;
import android.os.IFan;
import android.os.RemoteException;
import android.annotation.SystemService;
@SystemService(Context.ITEST_SERVICE )
public class TestManager {
public static final String TAG = "TestManager";
private Context mContext;
private TestService mService;
public FanManager(Context mContext, TestService mService) {
this.mContext = mContext;
this.mService = mService;
}
pubic void test(){
try {
mService.test();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
//TestService 类
package com.android.server;
import android.app.TestManager;
import android.content.Context;
import android.os.ITEST;
import android.os.RemoteException;
import com.android.server.SystemService;
import com.android.internal.app.IAppOpsService;
public class TestService extends SystemService {
private Context mContext;
private IAppOpsService mAppOps;
private TestManager mManager;
public TestService(Context context) {
super(context);
this.mContext = context;
}
public void systemReady(IAppOpsService appOps) {
//TODO
}
@Override
public void onStart() {
publishBinderService(Context.ITEST_SERVICE, new BinderService());
}
private final class BinderService extends ITEST.Stub {
@Override
public void test() throws RemoteException {
//TODO 具体操作在这里面实现
}
}
}
---------------------------------------------------------------------------------------------------------------------------------
5、SystemServer.java 中注册该service
修改位置: frameworks\base\services\java\com\android\server
import com.android.server.TestService ;//导包
private TestService mTestService; //定义
在SystemServer.java 中的startBootstrapServices()方法添加
traceBeginAndSlog("StartTestManager");
mTestService= mSystemServiceManager.startService(TestService.class);
traceEnd();
traceBeginAndSlog("MakeTestManagerServiceReady");
try {
// TODO: use boot phase
mTestService.systemReady(mActivityManagerService.getAppOpsService());
} catch (Throwable e) {
reportWtf("making Test Manager Service ready", e);
}
traceEnd();
---------------------------------------------------------------------------------------------------------------------------------
6、SystemServiceRegistry的static{}, 并在其中注册该service
修改位置:frameworks\base\core\java\android\app
import android.os.ITEST;//导包
//TestManager在同一目录下所以不用导包
registerService(Context.ITEST_SERVICE, TestManager.class,ew CachedServiceFetcher() {
@Override
public I2CManager createService(ContextImpl ctx)throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow( Context.ITEST_SERVICE);
return new TestManager(
ctx.getOuterContext(),ITEST.Stub.asInterface(b));
}});
以上步骤完成我们的自定义系统服务就完成了90% 但是我们还有最后一步,也是最重要的一步:
---------------------------------------------------------------------------------------------------------------------------------
7、 设置selinux规则
然后我们只需要添加这个自定义服务TestService相关的 SELinux 规则。为了方便之后验证,打开selinux
要记住这个命令 adb shell setenforce 1 # 1为打开 #0为关闭
Android 10 的 selinux 规则是放在 system/sepolicy 目录下的:
service.te 和 service_contexts 都要加上 TestService的配置:
./prebuilts/api/27.0/public/service.te # 需要
./prebuilts/api/28.0/public/service.te # 需要
./prebuilts/api/29.0/public/service.te # 需要
./prebuilts/api/26.0/public/service.te # 需要
./public/service.te # 需要
./prebuilts/api/27.0/private/service_contexts # 需要
./prebuilts/api/28.0/private/service_contexts # 需要
./prebuilts/api/29.0/private/service_contexts # 需要
./prebuilts/api/26.0/private/service_contexts # 需要
./private/service_contexts # 需要
在上述文件中
service_contexts文件添加配置 itest u:object_r:itest_service:s0
service.te 中添加type itest_service , system_server_service, service_manager_type
总结出一个规律 :就是service.te 文件在public目录下需要添加,private目录下不需要
service_contexts文件在private目录下的需要添加,public目录不需要
到此系统服务已经添加完成
itest u:object_r:itest_service:s0
---------------------------------------------------------------------------------------------------------------------------------
8、验证:
编译完成后,输入adb shell
#service list
查看服务列表中 是否存在有添加服务 :itest
如果不存在 逐步排查 参照上一步看哪一步错误
如果存在 就在代码中验证
找到编译最新生成的class.jar文件,导入Androidstudio(如何导入自行百度)
编译后的class.jar目录\out\target\common\obj\JAVA_LIBRARIES\framework_intermediates
如果未生成 执行下make javac-check-framework 这个命令 就会生成!!!
觉得我写的好的兄弟 动动你发财的小手 点个赞 !!!
你们的认同将是我继续写下去的动力 !!
|