秋霞步兵区国产精品,国产精品视频二区第二页,亚洲aⅴ欧美综合一区二区三区,亚洲日韩欧美一区二区不卡

      1. <small id="x8tpb"></small>
        <address id="x8tpb"></address>

        新疆信息港歡迎您!

        新疆信息港
        新疆信息港 > 汽車 >Android 添加系統(tǒng)服務(wù)的方法

        Android 添加系統(tǒng)服務(wù)的方法

        2020-03-23 08:31:57
        來源:互聯(lián)網(wǎng)
        閱讀:-

        和你一起終身學(xué)習(xí),這里是程序員 Android本篇文章主要介紹 Android 開發(fā)中的部分知識(shí)點(diǎn),通過閱讀本篇文章,您將收獲以下內(nèi)容:一、前言二、編寫AIDL文件三、編寫Manager類四、 編寫系統(tǒng)服務(wù)五、 注冊(cè)系統(tǒng)服務(wù)六、注冊(cè)Manager七、App調(diào)用八、添加JNI部分代...

        和你一起終身學(xué)習(xí),這里是程序員 Android

        本篇文章主要介紹 Android 開發(fā)中的部分知識(shí)點(diǎn),通過閱讀本篇文章,您將收獲以下內(nèi)容:

        一、前言
        二、編寫AIDL文件
        三、編寫Manager類
        四、 編寫系統(tǒng)服務(wù)
        五、 注冊(cè)系統(tǒng)服務(wù)
        六、注冊(cè)Manager
        七、App調(diào)用
        八、添加JNI部分代碼
        九、總結(jié)

        一、前言

        系統(tǒng)服務(wù)是Android中非常重要的一部分, 像ActivityManagerService, PackageManagerService, WindowManagerService, 這些系統(tǒng)服務(wù)都是Framework層的關(guān)鍵服務(wù), 本篇文章主要講一下如何基于Android源碼添加一個(gè)系統(tǒng)服務(wù)的完整流程, 除了添加基本系統(tǒng)服務(wù), 其中還包含添加JNI部分代碼和App通過AIDL調(diào)用的演示Demo, 調(diào)用包含App調(diào)用服務(wù)端, 也包含服務(wù)端回調(diào)App, 也就是完成一個(gè)簡(jiǎn)單的雙向通信.

        注: 測(cè)試代碼基于Android 7.1.1, 其他Android版本都是大同小異.

        二、編寫AIDL文件

        添加服務(wù)首先是編寫AIDL文件, AIDL文件路徑如下:

        frameworks/base/core/java/com/example/utils/

        1.ISystemEvent.aidl 內(nèi)容如下:

        package com.example.utils;

        import com.example.utils.IEventCallback;

        interface ISystemEvent {
        void registerCallback(IEventCallback callback);

        void unregisterCallback(IEventCallback callback);

        void sendEvent(int type, String value);
        }

        2.IEventCallback.aidl 內(nèi)容如下

        package com.example.utils;

        interface IEventCallback
        {
        oneway void onSystemEvent(int type, String value);
        }

        AIDL文件編寫, 教程很多, 我這里就不詳細(xì)說明了, 需要注意的是, 由于我們要實(shí)現(xiàn)回調(diào)功能, 所以必須寫一個(gè)回調(diào)接口 IEventCallback, 另外AIDL文件中 oneway 關(guān)鍵字表明調(diào)用此函數(shù)不會(huì)阻塞當(dāng)前線程, 調(diào)用端調(diào)用此函數(shù)會(huì)立即返回, 接收端收到函數(shù)調(diào)用是在Binder線程池中的某個(gè)線程中. 可以根據(jù)實(shí)際項(xiàng)目需求選擇是否需要加 oneway 關(guān)鍵字.

        AIDL只支持傳輸基本java類型數(shù)據(jù), 要想傳遞自定義類, 類需要實(shí)現(xiàn) Parcelable 接口, 另外, 如果傳遞基本類型數(shù)組, 需要指定 in out 關(guān)鍵字, 比如 void test(in byte[] input, out byte[] output) , 用 in 還是 out, 只需要記住: 數(shù)組如果作為參數(shù), 通過調(diào)用端傳給被調(diào)端, 則使用 in, 如果數(shù)組只是用來接受數(shù)據(jù), 實(shí)際數(shù)據(jù)是由被調(diào)用端來填充的, 則使用 out, 這里之所以沒有說服務(wù)端和客戶端, 是因?yàn)?in out 關(guān)鍵字用哪個(gè)和是服務(wù)端還是客戶端沒有聯(lián)系, 遠(yuǎn)程調(diào)用和被調(diào)用更適合描述.

        文件寫完后, 添加到編譯的 Android.mk 中 LOCAL_SRC_FILES 后面:

        3.frameworks/base/Android.mk

        LOCAL_SRC_FILES += \
        core/java/android/view/IWindow.aidl \
        core/java/android/view/IWindowFocusObserver.aidl \
        core/java/android/view/IWindowId.aidl \
        部分代碼省略 ...
        core/java/com/example/utils/ISystemEvent.aidl \
        core/java/com/example/utils/IEventCallback.aidl \
        部分代碼省略 ...

        編譯代碼, 編譯前需執(zhí)行 make update-api, 更新接口, 然后編譯代碼,確保AIDL編寫沒有錯(cuò)誤, 編譯后會(huì)生成對(duì)應(yīng)java文件, 服務(wù)端要實(shí)現(xiàn)對(duì)應(yīng)接口.

        三、編寫Manager類

        我們可以看到, Android API 中有很多Manager類, 這些類一般都是某個(gè)系統(tǒng)服務(wù)的客戶端代理類, 其實(shí)我們不寫Manager類, 只通過AIDL文件自動(dòng)生成的類, 也可以完成功能, 但封裝一下AIDL接口使用起來更方便, 我們測(cè)試用的Manager類為 SystemEventManager, 代碼如下:
        frameworks/base/core/java/com/example/utils/SystemEventManager.java

        package com.example.utils;

        import android.content.Context;
        import android.os.RemoteException;
        import android.util.Log;

        import com.example.example.ISystemEvent;
        import com.example.IEventCallback;

        public class SystemEventManager {

        private static final String TAG = SystemEventManager.class.getSimpleName();
        // 系統(tǒng)服務(wù)注冊(cè)時(shí)使用的名字, 確保和已有的服務(wù)名字不沖突
        public static final String SERVICE = &#34;test_systemevent&#34;;

        private final Context mContext;
        private final ISystemEvent mService;

        public SystemEventManager(Context context, ISystemEvent service) {
        mContext = context;
        mService = service;
        Log.d(TAG, &#34;SystemEventManager init&#34;);
        }

        public void register(IEventCallback callback) {
        try {
        mService.registerCallback(callback);
        } catch (RemoteException e) {
        Log.w(TAG, &#34;remote exception happen&#34;);
        e.printStackTrace();
        }
        }

        public void unregister(IEventCallback callback) {
        try {
        mService.unregisterCallback(callback);
        } catch (RemoteException e) {
        Log.w(TAG, &#34;remote exception happen&#34;);
        e.printStackTrace();
        }
        }

        /**
        * Send event to SystemEventService.
        */
        public void sendEvent(int type, String value) {
        try {
        mService.sendEvent(type, value);
        } catch (RemoteException e) {
        Log.w(TAG, &#34;remote exception happen&#34;);
        e.printStackTrace();
        }
        }
        }

        代碼很簡(jiǎn)單, 就封裝了下AIDL接口, 定義了系統(tǒng)服務(wù)注冊(cè)時(shí)用的名字.

        public SystemEventManager(Context context, ISystemEvent service)

        構(gòu)造函數(shù)中的 ISystemEvent 參數(shù)在后面注冊(cè)Manager時(shí)候會(huì)通過Binder相關(guān)接口獲取.

        編譯代碼, 確保沒有錯(cuò)誤, 下面編寫系統(tǒng)服務(wù).

        四、 編寫系統(tǒng)服務(wù)

        路徑以及代碼如下:
        frameworks/base/services/core/java/com/android/server/example/SystemEventService.java

        package com.android.server.example;

        import android.content.Context;
        import android.os.Binder;
        import android.os.RemoteCallbackList;
        import android.os.RemoteException;
        import android.os.ServiceManager;
        import android.util.Log;

        import com.example.utils.ISystemEvent;
        import com.example.utils.IEventCallback;

        public class SystemEventService extends ISystemEvent.Stub {

        private static final String TAG = SystemEventService.class.getSimpleName();
        private RemoteCallbackList&lt;IEventCallback&gt; mCallbackList = new RemoteCallbackList&lt;&gt;();

        private Context mContext;

        public SystemEventService(Context context) {
        mContext = context;
        Log.d(TAG, &#34;SystemEventService init&#34;);
        }

        @Override
        public void registerCallback(IEventCallback callback) {
        boolean result = mCallbackList.register(callback);
        Log.d(TAG, &#34;register pid:&#34; + Binder.getCallingPid()
        + &#34; uid:&#34; + Binder.getCallingUid() + &#34; result:&#34; + result);

        }

        @Override
        public void unregisterCallback(IEventCallback callback) {
        boolean result = mCallbackList.unregister(callback);
        Log.d(TAG, &#34;unregister pid:&#34; + Binder.getCallingPid()
        + &#34; uid:&#34; + Binder.getCallingUid() + &#34; result:&#34; + result);

        }

        @Override
        public void sendEvent(int type, String value) {
        sendEventToRemote(type, value + &#34; remote&#34;);
        }

        public void sendEventToRemote(int type, String value) {
        int count = mCallbackList.getRegisteredCallbackCount();
        Log.d(TAG, &#34;remote callback count:&#34; + count);
        if (count &gt; 0) {
        final int size = mCallbackList.beginBroadcast();
        for (int i = 0; i &lt; size; i++) {
        IEventCallback cb = mCallbackList.getBroadcastItem(i);
        try {
        if (cb != null) {
        cb.onSystemEvent(type, value);
        }
        } catch (RemoteException e) {
        e.printStackTrace();
        Log.d(TAG, &#34;remote exception:&#34; + e.getMessage());
        }
        }
        mCallbackList.finishBroadcast();
        }
        }
        }

        服務(wù)端繼承自 ISystemEvent.Stub, 實(shí)現(xiàn)對(duì)應(yīng)的三個(gè)方法即可, 需要注意的是, 由于有回調(diào)功能, 所以要把注冊(cè)的 IEventCallback 加到鏈表里面, 這里使用了 RemoteCallbackList, 之所以不能使用普通的 List 或者 Map, 原因是, 跨進(jìn)程調(diào)用, App調(diào)用 registerCallback 和 unregisterCallback 時(shí), 即便每次傳遞的都是同一個(gè) IEventCallback 對(duì)象, 但到服務(wù)端, 經(jīng)過跨進(jìn)程處理后, 就會(huì)生成不同的對(duì)象, 所以不能通過直接比較是否是同一個(gè)對(duì)象來判斷是不是同一個(gè)客戶端對(duì)象, Android中專門用來處理跨進(jìn)程調(diào)用回調(diào)的類就是 RemoteCallbackList, RemoteCallbackList 還能自動(dòng)處理App端異常死亡情況, 這種情況會(huì)自動(dòng)移除已經(jīng)注冊(cè)的回調(diào).

        RemoteCallbackList 使用非常簡(jiǎn)單, 注冊(cè)和移除分別調(diào)用 register() 和 unregister() 即可, 遍歷所有Callback 稍微麻煩一點(diǎn), 代碼參考上面的 sendEventToRemote() 方法.

        可以看到, 我們測(cè)試用的的系統(tǒng)服務(wù)邏輯很簡(jiǎn)單, 注冊(cè)和移除 Callback 調(diào)用 RemoteCallbackList 對(duì)應(yīng)方法即可, sendEvent() 方法在App端調(diào)用的基礎(chǔ)上, 在字符串后面加上 &#34; remote&#34; 后回調(diào)給App, 每個(gè)方法也加了log方便理解流程, 服務(wù)端代碼就完成了.

        五、 注冊(cè)系統(tǒng)服務(wù)

        代碼寫好后, 要注冊(cè)到SystemServer中, 所有系統(tǒng)服務(wù)都運(yùn)行在名為 system_server 的進(jìn)程中, 我們要把編寫好的服務(wù)加進(jìn)去, SystemServer中有很多服務(wù), 我們把我們的系統(tǒng)服務(wù)加到最后面, 對(duì)應(yīng)路徑和代碼如下:
        frameworks/base/services/java/com/android/server/SystemServer.java

        import com.android.server.example.SystemEventService;
        import com.example.utils.SystemEventManager;

        /**
        * Starts a miscellaneous grab bag of stuff that has yet to be refactored
        * and organized.
        */
        private void startOtherServices() {
        // 部分代碼省略...
        // start SystemEventService
        try {
        ServiceManager.addService(SystemEventManager.SERVICE,
        new SystemEventService(mSystemContext));
        } catch (Throwable e) {
        reportWtf(&#34;starting SystemEventService&#34;, e);
        }
        // 部分代碼省略...
        }

        通過 ServiceManager 將服務(wù)加到SystemServer中, 名字使用 SystemEventManager.SERVICE, 后面獲取服務(wù)會(huì)通過名字來獲取. 此時(shí), 如果直接編譯運(yùn)行, 開機(jī)后會(huì)出現(xiàn)如下錯(cuò)誤:

        E SystemServer: java.lang.SecurityException

        E SELinux : avc: denied { add } for service=test_systemevent pid=1940 uid=1000 scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=0

        這個(gè)是沒有Selinux權(quán)限, 我們需要加上添加服務(wù)的權(quán)限, 代碼如下:

        首先定義類型, test_systemevent 要和添加服務(wù)用的名字保持一致
        system/sepolicy/service_contexts

        wifiscanner u:object_r:wifiscanner_service:s0
        wifi u:object_r:wifi_service:s0
        window u:object_r:window_service:s0
        # 部分代碼省略...
        test_systemevent u:object_r:test_systemevent_service:s0
        * u:object_r:default_android_service:s0

        system/sepolicy/service.te

        # 加入剛剛定義好的 test_systemevent_service 類型, 表明它是系統(tǒng)服務(wù)
        type test_systemevent_service, system_api_service, system_server_service, service_manager_type;

        加入上面代碼后, 編譯刷機(jī)開機(jī)后, 服務(wù)就能正常運(yùn)行了.

        六、注冊(cè)Manager

        系統(tǒng)服務(wù)運(yùn)行好了, 接下來就是App怎么獲取的問題了, App獲取系統(tǒng)服務(wù), 我們也用通用接口:
        context.getSystemService()
        在調(diào)用 getSystemService() 之前, 需要先注冊(cè), 代碼如下:

        frameworks/base/core/java/android/app/SystemServiceRegistry.java

        import com.example.utils.ISystemEvent;
        import com.example.utils.SystemEventManager;

        static {
        // 部分代碼省略, 參考其他代碼, 注冊(cè)Manger
        registerService(SystemEventManager.SERVICE, SystemEventManager.class,
        new CachedServiceFetcher&lt;SystemEventManager&gt;() {
        @Override
        public SystemEventManager createService(ContextImpl ctx) {
        // 獲取服務(wù)
        IBinder b = ServiceManager.getService(SystemEventManager.SERVICE);
        // 轉(zhuǎn)為 ISystemEvent
        ISystemEvent service = ISystemEvent.Stub.asInterface(b);
        return new SystemEventManager(ctx.getOuterContext(), service);
        }});
        }

        注冊(cè)后, 如果你在App里面通過 getSystemService(SystemEventManager.SERVICE); 獲取Manager并調(diào)用接口, 會(huì)發(fā)現(xiàn)又會(huì)出錯(cuò), 又是Selinux權(quán)限問題:

        E SELinux : avc: denied { find } for service=test_systemevent pid=4123 uid=10035 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:test_systemevent_service:s0 tclass=service_manager permissive=0

        說是沒有 find 權(quán)限, 因此又要加權(quán)限, 修改代碼如下:
        system/sepolicy/untrusted_app.te

        # 允許 untrusted_app 查找 test_systemevent_service
        allow untrusted_app test_systemevent_service:service_manager find;

        這個(gè) Selinux 的知識(shí)有興趣自己去學(xué)一下, 報(bào)了什么權(quán)限, 就按照錯(cuò)誤信息去對(duì)應(yīng)文件添加權(quán)限.

        至此, 系統(tǒng)代碼修改完成了, 編譯系統(tǒng)刷機(jī), 下面通過App調(diào)用.

        七、App調(diào)用

        文件拷貝和準(zhǔn)備:
        我們需要復(fù)制三個(gè)文件到App中, 兩個(gè)AIDL文件, 一個(gè)Manager文件:

        IEventCallback.aidl
        ISystemEvent.aidl
        SystemEventManager.java

        所有AIDL文件和java文件, 在App工程中的包名和路徑都需要和系統(tǒng)保持一致, 這三個(gè)文件App不能做任何修改, 除非系統(tǒng)源碼中也做對(duì)應(yīng)修改, 總的來說, 這三個(gè)文件App和系統(tǒng)中要完全保持一致, 類名包名和包路徑都需一致, 復(fù)制這三個(gè)文件到工程中后, 編譯后, 調(diào)用方式如下.

        獲取服務(wù):

        SystemEventManager eventManager = (SystemEventManager) 
        context.getSystemService(SystemEventManager.SERVICE);

        這里Android Studio可能會(huì)報(bào) getSystemService() 參數(shù)不是Context里面的某個(gè)服務(wù)的錯(cuò)誤, 可以直接忽略, 不影響編譯.

        注冊(cè)/取消注冊(cè):

        eventManager.register(eventCallback);

        eventManager.unregister(eventCallback);

        private IEventCallback.Stub eventCallback = new IEventCallback.Stub() {
        @Override
        public void onSystemEvent(int type, String value) throws RemoteException {
        Log.d(&#34;SystemEvent&#34;, &#34;type:&#34; + type + &#34; value:&#34; + value);
        }
        };

        調(diào)用:

        eventManager.sendEvent(1, &#34;test string&#34;);

        測(cè)試Log如下:

        D SystemEventManager: SystemEventManager init
        D SystemEventService: register pid:3944 uid:10035 result:true
        D SystemEventService: remote callback count:1
        D SystemEvent: type:1 value:test string remote
        D SystemEventService: unregister pid:3944 uid:10035 result:true

        可以看到調(diào)用了服務(wù)端并成功收到服務(wù)端拼接的字符串.

        八、添加JNI部分代碼

        我們一般添加系統(tǒng)服務(wù), 可能是為了調(diào)用驅(qū)動(dòng)里面的代碼, 所有一般要用JNI部分代碼, 這里不是講怎么編寫JNI代碼, 而是說下系統(tǒng)服務(wù)中已有的JNI代碼, 我們可以直接在這基礎(chǔ)上增加我們的功能.

        JNI部分代碼位置為:

        frameworks/base/services/core/jni/

        編譯對(duì)應(yīng)mk為:

        frameworks/base/services/Android.mk
        frameworks/base/services/core/jni/Android.mk

        此部分代碼直接編譯為 libandroid_servers 動(dòng)態(tài)庫(kù), 在SystemServer進(jìn)行加載:
        frameworks/base/services/java/com/android/server/SystemServer.java

        // Initialize native services.
        System.loadLibrary(&#34;android_servers&#34;);

        如果需要添加JNI部分代碼, 直接在 frameworks/base/services/core/jni/目錄下增加對(duì)應(yīng)文件,
        在frameworks/base/services/core/jni/Android.mk中加入新增文件進(jìn)行編譯即可.
        同時(shí)按照已有文件中JNI函數(shù)注冊(cè)方式, 寫好對(duì)應(yīng)注冊(cè)方法, 統(tǒng)一在
        frameworks/base/services/core/jni/onload.cpp中動(dòng)態(tài)注冊(cè)函數(shù).
        關(guān)于JNI動(dòng)態(tài)注冊(cè)知識(shí), 可參考之前寫的一篇文章: 兩種JNI注冊(cè)方式

        九、總結(jié)

        從上面一個(gè)完整的流程下來, 基本就理解了我們平常調(diào)用 getSystemService() 具體是怎么工作的, 總體來說也不麻煩, 真正有技術(shù)含量的跨進(jìn)程調(diào)用被隱藏起來了, 我們只管按照規(guī)則調(diào)用接口即可,以上就是Android系統(tǒng)中添加一個(gè)系統(tǒng)服務(wù)和App調(diào)用的完整流程, 如有疑問, 歡迎討論!
        文章轉(zhuǎn)載網(wǎng)絡(luò),原文鏈接如下:
        原文鏈接

        推薦閱讀:蘋果7p有什么顏色

        免責(zé)聲明:本文僅代表企業(yè)觀點(diǎn),與新疆信息港無關(guān)。其原創(chuàng)性以及文中陳述文字和內(nèi)容未經(jīng)本站證實(shí),對(duì)本文以及其中全部或者部分內(nèi)容、文字的真實(shí)性、完整性、及時(shí)性本站不作任何保證或承諾,請(qǐng)讀者僅作參考,并請(qǐng)自行核實(shí)相關(guān)內(nèi)容。
        熱門圖片
        熱門搜索