前置文章

《创建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/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

添加 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 #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_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 #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 代码

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。

更多相关文章

  1. Android有用代码片断(六)
  2. 更改Android(安卓)AVD模拟器创建路径位置的方法
  3. [置顶] 搬家、备份后启动Android(安卓)PANIC :Could not open D:
  4. 深入解读Linux与Android的相互关系
  5. Android中JNI的使用
  6. Android进程 Handler Message Looper
  7. 爱Android更要懂Android
  8. Android多进程之Binder的意外死亡及权限校验
  9. Linux与Android

随机推荐

  1. android:layout_weight属性详解
  2. 【视频课程】Android底层开发关键技术—A
  3. Android学习备忘006——Manifest.xml
  4. 如何修改Android自带浏览器的User Agent
  5. Android发送邮件附带文件
  6. Android(安卓)使用Lottie的三个小技巧
  7. android 设置应用退出后不在运行列表中显
  8. Ubuntu下android源码下载与编译
  9. Android的init过程(二):初始化语言(init.rc)解
  10. Android应用实例之----天气预报程序