《创建Android守护进程(底层服务) 》
在文章 《创建Android守护进程(底层服务) 》 中,学习了如何创建一个 Android 守护进程,但是这个进程还没有做任何有价值的事情。因此,在此篇文章中,来学习如何利用 Android 守护进程做一些事情。
在本文中,将讲述一个上层的 Android APP 如何和 Android 守护进程建立通信,传输数据,完成某项功能。本文将以 APP 读取 Android 设备 CPU 频率为例,APP 与守护进程通过建立 socket 通信,守护进程通过 socket 通道,把 CPU 频率上报到 APP 显示。
注:本文承接 《创建Android守护进程(底层服务) 》 一文中的内容。采用 MTK Android 8.0 的基带平台代码
本文涉及的主要代码,已经上传到 Github:
在开机启动配置文件中添加创建 socket,如下代码的 socket…部分,其它代码和文章《创建Android守护进程(底层服务) 》是相同的。
service nativeservice /system/bin/nativeservice
class main #main类,属于main的服务会开机被运行,且死掉会重启
group system #属于 system 组
#user system #以system用户启动,不设置以root用户启动
seclabel u:r:nativeservice:s0 #SeAndroid SContext,domain是nativeservice
restorecon nativeservice
socket nativeservice stream 0666 root root #创建nativeservice socket,用户为 root
write /proc/bootprof "start nativeservice"
代码路径:system/core/rootdir/init.rc
承接文章《创建Android守护进程(底层服务) 》中的内容,修改或者添加部分内容。
type nativeservice_socket, file_type;
在文件 device/mediatek/sepolicy/basic/non_plat/file.te 中添加。
/dev/socket/nativeservice u:object_r:nativeservice_socket:s0
在文件 device/mediatek/sepolicy/basic/non_plat/file_contexts 中添加。
因为用的 socket 命名空间是 RESERVED,因此,socket 通道会在 /dev/socket/ 地下,详情可以查阅文件frameworks/base/core/java/android/net/LocalSocketAddress.java。
以上两个声明文件的角色和安全上下文,后面就是给某个进程赋予该角色被操作的权限。本文中,采用 System app 来和守护进程建立 socket 连接,因此,添加如下代码
allow system_app nativeservice_socket:sock_file { write append };
allow system_app nativeservice:unix_stream_socket { connectto };
在文件 device/mediatek/sepolicy/basic/non_plat/system_app.te 中添加。
守护进程需要打开和读取 /proc/ 底下的 CPU 频率文件,因此给守护进程 nativeservice 授予如下权限
allow nativeservice proc:file { open read };
在文件 device/mediatek/sepolicy/basic/non_plat/nativeservice.te 中添加。
先看一下现在守护进程代码的目录架构,比文章 《创建Android守护进程(底层服务) 》 中多了 NativeServiceListener.cpp 和 NativeServiceListener.h 两个文件,修改了 native_main.cpp 文件。
//
// Created familyyuan.
//
#include
#include
#include
#include
#include
#include "NativeServiceListener.h"
#include
#include
#include
#include
#include
#include
#include
using namespace android;
#define MAX_EPOLL_EVENTS 40
#define BUFFER_SIZE PIPE_BUF
int main(int argc, char *argv[]) {
SLOGD("native_service start");
//
fcntl(android_get_control_socket("nativeservice"), F_SETFD, FD_CLOEXEC);
//核心代码,就这几行,实例化 socket 监听器,监听 socket 连接
NativeServiceListener *cl;
cl = new NativeServiceListener("nativeservice", true);
if (cl->startListener()) {
SLOGE("native_service Unable to start NativeServiceListener (%s)", strerror(errno));
exit(1);
}
while(1){
sleep(1000);
}
SLOGD("native_service die");
return 0;
}
代码文件 system/core/nativeservice/native_main.cpp
声明继承 SocketListener.h 的头文件,当 socket 建立连接收到数据,会回调 onDataAvailable() 函数。
/* Copyright (C) 2016 Tcl Corporation Limited */
#ifndef _NATIVESERVICESOCKETLISTENER_H
#define _NATIVESERVICESOCKETLISTENER_H
#include "SocketListener.h"
class SocketClient;
class NativeServiceListener : public SocketListener {
public:
static const int CMD_ARGS_MAX = 26;
static const int CMD_BUF_SIZE = 1024;
/* 1 out of errorRate will be dropped */
int errorRate;
protected:
bool onDataAvailable(SocketClient *c);
public:
NativeServiceListener(const char *socketName);
NativeServiceListener(const char *socketName, bool withSeq);
NativeServiceListener(int sock);
virtual ~NativeServiceListener() {}
private:
void init(const char *socketName, bool withSeq);
};
#endif
代码文件 system/core/nativeservice/NativeServiceListener.h。
这个文件就是 SocketListener 的实现类,在 onDataAvailable() 函数中做事情。
/* Copyright (C) 2016 Tcl Corporation Limited */
#define LOG_TAG "NativeServiceListener"
#include
#include
#include
#include
#include
#include
#include
#include
#include "NativeServiceListener.h"
static int file_fd;
#define DUMP_ARGS 1
#define UNUSED __attribute__((unused))
NativeServiceListener::NativeServiceListener(const char *socketName, bool withSeq) :
SocketListener(socketName, true, withSeq) {
init(socketName, withSeq);
}
NativeServiceListener::NativeServiceListener(const char *socketName) :
SocketListener(socketName, true, false) {
init(socketName, false);
}
NativeServiceListener::NativeServiceListener(int sock) :
SocketListener(sock, true) {
init(NULL, false);
}
void NativeServiceListener::init(const char *socketName UNUSED, bool withSeq) {
}
bool NativeServiceListener::onDataAvailable(SocketClient *c) {
char buffer[CMD_BUF_SIZE] = {0};
int len;
//读取 socket 客户端传来的数据
len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
if (len < 0) {
SLOGE("native_service read() failed (%s)", strerror(errno));
return false;
} else if (!len) {
SLOGD("native_service socket data %s", buffer);
return false;
}
SLOGD("native_service runnig");
char buffer_data[20];
int res = -1;
//读取 cpu 当前频率,基于 MTK Android 8.0 基带,笔者设备是 8 核 MTK 芯片,
//L 和 LL 核,本文读取 LL 核的频率
file_fd = TEMP_FAILURE_RETRY(open("/proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_freq",O_RDONLY));
if (file_fd < 0) {
SLOGD("native_service open failed");
} else {
SLOGD("native_service open success");
}
if (file_fd != -1){
res = read(file_fd, buffer_data, sizeof(buffer_data));
SLOGD("native_service result=%s", buffer_data);
} else {
SLOGD("native_service open failed");
}
if(res > 0){
char *buf;
int ret = 0;
strtok(buffer_data, "\n");
//把返回给客户端的数据组装成 json 格式
ret = asprintf(&buf, "%s%s%s%s", "{\"code\":200,", "\"cpu\":\"", buffer_data, "\"}");
SLOGD("native_service for java result=%s", buf);
//往 socket 客户端写返回数据
if (ret != -1) {
c->sendMsg(buf);
} else {
c->sendMsg(buffer_data);
}
free(buf);
} else {
SLOGD("native_service open failed %d", 500);
c->sendCode(500);
}
close(file_fd);
free(buffer);
free(buffer_data);
return true;
}
代码文件 system/core/nativeservice/NativeServiceListener.cpp。
至此,底层的代码就编写完毕了,以上代码编译开机,在 /dev/socket/ 下创建了 nativeservice socket 文件,如下图
守护进程接收到 socket 连接后打印的 log
APP 实现的功能是与 Nativeservice 建立 socket 连接,读取 socket 返回的 cpu 频率数据,显示在屏幕上。如下图所示,有一个 Activity 界面,启动一个 Service,Service 和 nativeservcie 建立 socket 连接,每隔 1 秒拿一次 cpu 频率数据,显示在一个浮动 view 上。
如下是 Service 的代码,更多代码请看 Github - SocketConnectNative。
package yuan.family.com.socketconnectnative;
public class SocketConnNativeService extends Service {
final String TAG = SocketConnNativeService.class.getSimpleName();
private WindowManager mWindowManager;
private WindowManager.LayoutParams wmParams;
private LocalSocket mSocket;
private InputStream mIn;
private OutputStream mOut;
private Handler mHandler;
private Handler mThreadHandler;
private HandlerThread mHandlerThread;
private final int CONN_SOCKET = 101;
private final int SOCKET_RESULT = 101;
private TextView mCpuFreqTV;
public SocketConnNativeService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* 浮动窗口的 window 和 view 配置
*/
private void initWindowParams() {
mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
wmParams = new WindowManager.LayoutParams();
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
wmParams.format = PixelFormat.TRANSLUCENT;
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
wmParams.width = WindowManager.LayoutParams.MATCH_PARENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
}
/**
* 引入 view,实例化组件,把 view 显示出来
*/
private void initView() {
initWindowParams();
View dialogView = LayoutInflater.from(this).inflate(R.layout.cpu_freq_view, null);
mCpuFreqTV = dialogView.findViewById(R.id.cpu_freq);
dialogView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWindowManager.removeViewImmediate(v);
stopSelf();
}
});
mWindowManager.addView(dialogView, wmParams);
}
/**
* 由于使用的是 Android 8.0 需要把 service 推到前台,不然被系统杀掉
*/
private Notification getNotification(){
NotificationManager mNotiManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel mChannel = new NotificationChannel("socket_native_conn", "socket_native_conn", NotificationManager.IMPORTANCE_DEFAULT);
mNotiManager.createNotificationChannel(mChannel);
Notification.Builder mBuilder = new Notification.Builder(this);
mBuilder.setShowWhen(false);
mBuilder.setAutoCancel(false);
mBuilder.setSmallIcon(R.mipmap.ic_launcher);
mBuilder.setContentText("SocketConnNative keep");
mBuilder.setContentTitle("SocketConnNative keep");
mBuilder.setChannelId("socket_native_conn");
return mBuilder.build();
}
/**
* 与 socket 建立连接,获取输入输出流
*/
private boolean connect() {
if (mSocket != null && mSocket.isConnected()) {
return true;
}
try {
// a non-server socket
mSocket = new LocalSocket();
// LocalSocketAddress.Namespace.RESERVED keep namespace
LocalSocketAddress address = new LocalSocketAddress("nativeservice", LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
mIn = mSocket.getInputStream();
mOut = mSocket.getOutputStream();
} catch (Exception ex) {
ex.printStackTrace();
//disconnect();
return false;
}
return true;
}
public void disconnect() {
try {
if (mSocket != null) {
mSocket.shutdownInput();
mSocket.shutdownOutput();
mSocket.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
try {
if (mIn != null) {
mIn.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
try {
if (mOut != null) {
mOut.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
mSocket = null;
mIn = null;
mOut = null;
}
protected boolean sendCommand(byte[] cmd) {
try {
String prefixCmd = "0 traceability ";
byte fullCmd[] = new byte[prefixCmd.length() + cmd.length];
System.arraycopy(prefixCmd.getBytes(), 0, fullCmd, 0, prefixCmd.length());
System.arraycopy(cmd, 0, fullCmd, prefixCmd.length(), cmd.length);
if (mOut != null) {
mOut.write(fullCmd, 0, fullCmd.length);
}
} catch (Exception ex) {
Log.e(TAG, "write error");
return false;
}
return true;
}
public String connSocketNative(byte[] cmd) {
byte[] result = new byte[128];
StringBuilder stringBuilder = new StringBuilder();
if (!connect()) {
Log.d(TAG, "Connecting nativeservice proxy fail!");
mThreadHandler.sendEmptyMessage(CONN_SOCKET);
} else if (!sendCommand(cmd)) {
Log.d(TAG, "Send command to nativeservice proxy fail!");
mThreadHandler.sendEmptyMessage(CONN_SOCKET);
} else {
BufferedReader br = null;
try {
//读取 nativeservice 返回的 cpu 频率数据
br = new BufferedReader(new InputStreamReader(mIn, "UTF-8"));
String resultStr = br.readLine();
while (!TextUtils.isEmpty(resultStr)) {
stringBuilder.append(resultStr);
resultStr = br.readLine();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
disconnect();
return stringBuilder.toString().replace("[?]", "").trim();
}
@Override
public void onCreate() {
super.onCreate();
startForeground(1, getNotification());
mHandler = new MainHandler(Looper.getMainLooper());
mHandlerThread = new HandlerThread("thread", Thread.MAX_PRIORITY);
mHandlerThread.start();
mThreadHandler = new ThreadHandler(mHandlerThread.getLooper());
initView();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mThreadHandler.sendEmptyMessage(CONN_SOCKET);
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
}
/**
* socket 连接是耗时操作,需要在子线程完成
*/
private class ThreadHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CONN_SOCKET:
String result = connSocketNative(new byte[]{'A'});
Gson gson = new Gson();
CpuInfo cpuInfo = gson.fromJson(result, CpuInfo.class);
//获取成功,将 cpu 频率数据发给 UI 线程
if (cpuInfo != null && cpuInfo.getCode() == 200) {
mHandler.sendMessage(Message.obtain(mHandler, SOCKET_RESULT, cpuInfo.getCpu()));
}
break;
default:
break;
}
}
public ThreadHandler(Looper looper) {
super(looper);
}
}
/**
* UI 线程获取到数据后,显示在 TextView 上
* 每隔 1 秒钟获取一次
*/
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SOCKET_RESULT:
mCpuFreqTV.setText(msg.obj.toString());
mThreadHandler.sendEmptyMessageDelayed(CONN_SOCKET, 1000);
break;
default:
break;
}
}
public MainHandler(Looper looper) {
super(looper);
}
}
}
守护进程在文章 《创建Android守护进程(底层服务) 》 的基础上,没有太多的修改和添加,主要三点,一是配置 SeAndroid 权限,二是增加加载 CPU 频率的代码,三是添加 Socket 监听器。上层 APP 是纯新的代码,但是代码都比较简单,核心代码就在 SocketConnNativeService.java 文件中。守护进程通过 socket 上报给 APP 的数据基于 Json 数据格式,便于 APP 解析,本文中使用 Google 的 Json 数据处理框架 Gson。