前置文章

《创建Android守护进程(底层服务) 》

前言

在文章 《创建Android守护进程(底层服务) 》 中,学习了如何创建一个 Android 守护进程,但是这个进程还没有做任何有价值的事情。因此,在此篇文章中,来学习如何利用 Android 守护进程做一些事情。

在本文中,将讲述一个上层的 Android APP 如何和 Android 守护进程建立通信,传输数据,完成某项功能。本文将以 APP 读取 Android 设备 CPU 频率为例,APP 与守护进程通过建立 socket 通信,守护进程通过 socket 通道,把 CPU 频率上报到 APP 显示。

注:本文承接 《创建Android守护进程(底层服务) 》 一文中的内容。采用 MTK Android 8.0 的基带平台代码

本文涉及的主要代码,已经上传到 Github:

  1. nativeservice - 守护进程
  2. SocketConnectNative - APP

添加Socket配置

在开机启动配置文件中添加创建 socket,如下代码的 socket…部分,其它代码和文章《创建Android守护进程(底层服务) 》是相同的。

service nativeservice /system/bin/nativeserviceclass main #main类,属于main的服务会开机被运行,且死掉会重启group system #属于 system 组#user system #以system用户启动,不设置以root用户启动seclabel u:r:nativeservice:s0 #SeAndroid SContext,domain是nativeservicerestorecon nativeservicesocket nativeservice stream 0666 root root #创建nativeservice socket,用户为 rootwrite /proc/bootprof "start nativeservice"

代码路径:system/core/rootdir/init.rc

添加 SeAndroid 权限

承接文章《创建Android守护进程(底层服务) 》中的内容,修改或者添加部分内容。

声明 socket 通道文件的角色

type nativeservice_socket, file_type;

在文件 device/mediatek/sepolicy/basic/non_plat/file.te 中添加。

声明 socket 通道文件的安全上下文

/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 文件权限

守护进程需要打开和读取 /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 文件。

native_main.cpp 代码

//
// Created familyyuan.
//#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <cutils/sockets.h>
#include <cutils/log.h>
#include "NativeServiceListener.h"#include <fcntl.h>
#include <android-base/logging.h>#include <sys/types.h>
#include <sys/stat.h>
#include <sys/epoll.h>
#include <cutils/uevent.h>#include <sys/ioctl.h>using namespace android;#define MAX_EPOLL_EVENTS 40#define BUFFER_SIZE PIPE_BUFint 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

NativeServiceListener.h 文件

声明继承 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。

NativeServiceListener.cpp 文件

这个文件就是 SocketListener 的实现类,在 onDataAvailable() 函数中做事情。

/* Copyright (C) 2016 Tcl Corporation Limited */
#define LOG_TAG "NativeServiceListener"#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/sysinfo.h>#include <cutils/log.h>
#include <sysutils/SocketClient.h>
#include <cutils/properties.h>#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 代码

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() {}@Overridepublic 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() {@Overridepublic 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 socketmSocket = new LocalSocket();// LocalSocketAddress.Namespace.RESERVED keep namespaceLocalSocketAddress 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();}@Overridepublic 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();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {mThreadHandler.sendEmptyMessage(CONN_SOCKET);return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();stopForeground(true);}/*** socket 连接是耗时操作,需要在子线程完成*/private class ThreadHandler extends Handler {@Overridepublic 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 {@Overridepublic 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。

守护进程通信之Socket相关推荐

  1. 【进程通信】Socket

    网络通信 Socket   实现网络应用时要先从网络提供的接口开始,几乎所有计算机系统都将网络协议的软件实现作为操作系统的一部分,因此网络应用程序编程接口(API)一般都是操作系统提供的.套接字接口S ...

  2. Linux下进程通信知识点学习笔记(一)

    4种主要事件导致进程创建: 系统的初始化: 执行了正在运行的进程所调用的进程创建系统调用: 用户请求创建一个进程: 一个批处理作业的初始化: 进程的终止: 正常退出: 出错退: 严重错误: 被其他进程 ...

  3. python守护进程进程池_Python—守护进程管理工具(Supervisor)

    一.前言简介 1.Supervisor 是一个 Python 开发的 client/server 系统,可以管理和监控类 UNIX 操作系统上面的进程.可以很方便的用来启动.重启.关闭进程(不仅仅是 ...

  4. Supervisor守护进程

    Supervisor(http://supervisord.org/ )是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系 ...

  5. Docker 4 之 Docker 客户端和守护进程

    学习内容整理笔记来自 极客学院的 docker 入门教程,更多信息查看 Docker 文档 四.Docker 的 C/S 模式 1.Docker 的守护进程 Docker 是以客户端和守护进程的方式来 ...

  6. linux进程管道通信缺点,Linux进程通信(IPC)的方式详解

    前言:Linux进程通信的方式 什么是进程通信?进程通信是指进程之间交换信息 进程通信方式共有6种: 管道(pipe),包括流管道(s_pipe)和有名管道(named pipe) 信号(signal ...

  7. windows守护进程_在Linux的Windows子系统上(WSL)使用Docker(Ubuntu)

    平时开发大部人都是在提供了高效GUI的window下工作,但是真正部署环境普遍都是在Linux中,所以为了让开发环境和部署环境统一,我们需要在windows模拟LInux环境,以前我们可能通过虚拟机的 ...

  8. Linux守护进程设计规范及python实现

    http://blog.csdn.net/mr_jj_lian/article/details/7252222 守护进程 守护进程是生存期长的一种进程.它们独立于控制终端并且周期性的执行某种任务或等待 ...

  9. Linux开发(十三):守护进程

    目录 一.概述 二.守护进程的创建 1.顺序创建 2.库函数daemon() 三.守护进程出错处理 一.概述 Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任 ...

最新文章

  1. 学业水平考试b能上985吗_河南单招哪些学院好考?哪些专业能录取?
  2. win7拒绝访问_win7系统提示无法访问application data如何解决
  3. 追寻终极数据库 - 事务/分析混合处理系统的交付挑战 (1)
  4. PyTorch模型的保存加载以及数据的可视化
  5. Machine Learning week 5 quiz: programming assignment-Multi-Neural Network Learning
  6. POJ 2492 A Bug's Life (带权并查集 向量偏移)
  7. 排序算法Java实现(快速排序)
  8. vmware快速搭建OpenStack云计算平台
  9. 最小方法ZOJ 1579 Bridge
  10. 分布式存储系统设计的若干原则
  11. html 显示 16进制 颜色,16进制颜色(html颜色值)
  12. Django发送电子邮件
  13. 我能取得成就的原因和不足之处
  14. CSS学习之绘制几何图形
  15. python中使用opencv的HSV颜色空间提取物体
  16. 您的基于云的应用程序可能是有利可图的产品
  17. 【自动控制原理_B站网课笔记】系统时间响应的性能指标稳定性分析
  18. 8. Android MultiMedia框架完全解析 - prepareAsync的过程分析
  19. CSS实用技巧第一讲:文字处理
  20. 文件传服务器上全是乱码,解决txt文件上传oss服务器乱码的问题

热门文章

  1. 张兴个人简历计算机,张兴-合肥工业大学电气与自动化工程学院
  2. 酷贝网:站在淘宝肩上收钱
  3. Ubuntu下的常用命令之——cp
  4. 【Android Training - Multimedia】捕获照片 [Lesson 1 - 简单的拍照动作]
  5. 国家政策创业扶持资金有哪些申报技巧?
  6. Linux 排除指定文件夹打包压缩
  7. 行、支付宝这么干!赶紧查查你卡里两位数的存款还在不在?
  8. LintCode - 524.左填充
  9. 虚拟机下点阵汉字的字模读取与显示
  10. Android 仿今日头条的开源项目