新聞中心
這篇文章主要講解了“Android怎么添加系統(tǒng)服務(wù)”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Android怎么添加系統(tǒng)服務(wù)”吧!
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供高臺網(wǎng)站建設(shè)、高臺做網(wǎng)站、高臺網(wǎng)站設(shè)計(jì)、高臺網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、高臺企業(yè)網(wǎng)站模板建站服務(wù),十余年高臺做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
一、前言
系統(tǒng)服務(wù)是Android中非常重要的一部分, 像ActivityManagerService, PackageManagerService, WindowManagerService, 這些系統(tǒng)服務(wù)都是Framework層的關(guān)鍵服務(wù), 本篇文章主要講一下如何基于Android源碼添加一個系統(tǒng)服務(wù)的完整流程, 除了添加基本系統(tǒng)服務(wù), 其中還包含添加JNI部分代碼和App通過AIDL調(diào)用的演示Demo, 調(diào)用包含App調(diào)用服務(wù)端, 也包含服務(wù)端回調(diào)App, 也就是完成一個簡單的雙向通信.
注: 測試代碼基于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)功能, 所以必須寫一個回調(diào)接口 IEventCallback, 另外AIDL文件中 oneway 關(guān)鍵字表明調(diào)用此函數(shù)不會阻塞當(dāng)前線程, 調(diào)用端調(diào)用此函數(shù)會立即返回, 接收端收到函數(shù)調(diào)用是在Binder線程池中的某個線程中. 可以根據(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)鍵字用哪個和是服務(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編寫沒有錯誤, 編譯后會生成對應(yīng)java文件, 服務(wù)端要實(shí)現(xiàn)對應(yīng)接口.
三、編寫Manager類
我們可以看到, Android API 中有很多Manager類, 這些類一般都是某個系統(tǒng)服務(wù)的客戶端代理類, 其實(shí)我們不寫Manager類, 只通過AIDL文件自動生成的類, 也可以完成功能, 但封裝一下AIDL接口使用起來更方便, 我們測試用的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ù)注冊時使用的名字, 確保和已有的服務(wù)名字不沖突 public static final String SERVICE = "test_systemevent"; private final Context mContext; private final ISystemEvent mService; public SystemEventManager(Context context, ISystemEvent service) { mContext = context; mService = service; Log.d(TAG, "SystemEventManager init"); } public void register(IEventCallback callback) { try { mService.registerCallback(callback); } catch (RemoteException e) { Log.w(TAG, "remote exception happen"); e.printStackTrace(); } } public void unregister(IEventCallback callback) { try { mService.unregisterCallback(callback); } catch (RemoteException e) { Log.w(TAG, "remote exception happen"); e.printStackTrace(); } } /** * Send event to SystemEventService. */ public void sendEvent(int type, String value) { try { mService.sendEvent(type, value); } catch (RemoteException e) { Log.w(TAG, "remote exception happen"); e.printStackTrace(); } }}
代碼很簡單, 就封裝了下AIDL接口, 定義了系統(tǒng)服務(wù)注冊時用的名字.
public SystemEventManager(Context context, ISystemEvent service)
構(gòu)造函數(shù)中的 ISystemEvent 參數(shù)在后面注冊Manager時候會通過Binder相關(guān)接口獲取.
編譯代碼, 確保沒有錯誤, 下面編寫系統(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
服務(wù)端繼承自 ISystemEvent.Stub, 實(shí)現(xiàn)對應(yīng)的三個方法即可, 需要注意的是, 由于有回調(diào)功能, 所以要把注冊的 IEventCallback 加到鏈表里面, 這里使用了 RemoteCallbackList, 之所以不能使用普通的 List 或者 Map, 原因是, 跨進(jìn)程調(diào)用, App調(diào)用 registerCallback 和 unregisterCallback 時, 即便每次傳遞的都是同一個 IEventCallback 對象, 但到服務(wù)端, 經(jīng)過跨進(jìn)程處理后, 就會生成不同的對象, 所以不能通過直接比較是否是同一個對象來判斷是不是同一個客戶端對象, Android中專門用來處理跨進(jìn)程調(diào)用回調(diào)的類就是 RemoteCallbackList, RemoteCallbackList 還能自動處理App端異常死亡情況, 這種情況會自動移除已經(jīng)注冊的回調(diào).
RemoteCallbackList 使用非常簡單, 注冊和移除分別調(diào)用 register() 和 unregister() 即可, 遍歷所有Callback 稍微麻煩一點(diǎn), 代碼參考上面的 sendEventToRemote() 方法.
可以看到, 我們測試用的的系統(tǒng)服務(wù)邏輯很簡單, 注冊和移除 Callback 調(diào)用 RemoteCallbackList 對應(yīng)方法即可, sendEvent() 方法在App端調(diào)用的基礎(chǔ)上, 在字符串后面加上 " remote" 后回調(diào)給App, 每個方法也加了log方便理解流程, 服務(wù)端代碼就完成了.
五、 注冊系統(tǒng)服務(wù)
代碼寫好后, 要注冊到SystemServer中, 所有系統(tǒng)服務(wù)都運(yùn)行在名為 system_server 的進(jìn)程中, 我們要把編寫好的服務(wù)加進(jìn)去, SystemServer中有很多服務(wù), 我們把我們的系統(tǒng)服務(wù)加到最后面, 對應(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("starting SystemEventService", e); } // 部分代碼省略...}
通過 ServiceManager 將服務(wù)加到SystemServer中, 名字使用 SystemEventManager.SERVICE, 后面獲取服務(wù)會通過名字來獲取. 此時, 如果直接編譯運(yùn)行, 開機(jī)后會出現(xiàn)如下錯誤:
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
這個是沒有Selinux權(quán)限, 我們需要加上添加服務(wù)的權(quán)限, 代碼如下:
首先定義類型, test_systemevent 要和添加服務(wù)用的名字保持一致
system/sepolicy/service_contexts
wifiscanner u:object_r:wifiscanner_service:s0wifi u:object_r:wifi_service:s0window 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)行了.
六、注冊Manager
系統(tǒng)服務(wù)運(yùn)行好了, 接下來就是App怎么獲取的問題了, App獲取系統(tǒng)服務(wù), 我們也用通用接口:
context.getSystemService()
在調(diào)用 getSystemService() 之前, 需要先注冊, 代碼如下:
frameworks/base/core/java/android/app/SystemServiceRegistry.java
import com.example.utils.ISystemEvent;import com.example.utils.SystemEventManager;static { // 部分代碼省略, 參考其他代碼, 注冊Manger registerService(SystemEventManager.SERVICE, SystemEventManager.class, new CachedServiceFetcher
注冊后, 如果你在App里面通過 getSystemService(SystemEventManager.SERVICE); 獲取Manager并調(diào)用接口, 會發(fā)現(xiàn)又會出錯, 又是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_serviceallow untrusted_app test_systemevent_service:service_manager find;
這個 Selinux 的知識有興趣自己去學(xué)一下, 報(bào)了什么權(quán)限, 就按照錯誤信息去對應(yīng)文件添加權(quán)限.
至此, 系統(tǒng)代碼修改完成了, 編譯系統(tǒng)刷機(jī), 下面通過App調(diào)用.
七、App調(diào)用
文件拷貝和準(zhǔn)備:我們需要復(fù)制三個文件到App中, 兩個AIDL文件, 一個Manager文件:
IEventCallback.aidlISystemEvent.aidlSystemEventManager.java
所有AIDL文件和java文件, 在App工程中的包名和路徑都需要和系統(tǒng)保持一致, 這三個文件App不能做任何修改, 除非系統(tǒng)源碼中也做對應(yīng)修改, 總的來說, 這三個文件App和系統(tǒng)中要完全保持一致, 類名包名和包路徑都需一致, 復(fù)制這三個文件到工程中后, 編譯后, 調(diào)用方式如下.
獲取服務(wù):
SystemEventManager eventManager = (SystemEventManager) context.getSystemService(SystemEventManager.SERVICE);
這里Android Studio可能會報(bào) getSystemService() 參數(shù)不是Context里面的某個服務(wù)的錯誤, 可以直接忽略, 不影響編譯.
注冊/取消注冊:
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("SystemEvent", "type:" + type + " value:" + value); }};
調(diào)用:
eventManager.sendEvent(1, "test string");
測試Log如下:
D SystemEventManager: SystemEventManager initD SystemEventService: register pid:3944 uid:10035 result:trueD SystemEventService: remote callback count:1D SystemEvent: type:1 value:test string remoteD SystemEventService: unregister pid:3944 uid:10035 result:true
可以看到調(diào)用了服務(wù)端并成功收到服務(wù)端拼接的字符串.
八、添加JNI部分代碼
我們一般添加系統(tǒng)服務(wù), 可能是為了調(diào)用驅(qū)動里面的代碼, 所有一般要用JNI部分代碼, 這里不是講怎么編寫JNI代碼, 而是說下系統(tǒng)服務(wù)中已有的JNI代碼, 我們可以直接在這基礎(chǔ)上增加我們的功能.
JNI部分代碼位置為:
frameworks/base/services/core/jni/
編譯對應(yīng)mk為:
frameworks/base/services/Android.mkframeworks/base/services/core/jni/Android.mk
此部分代碼直接編譯為 libandroid_servers 動態(tài)庫, 在SystemServer進(jìn)行加載:frameworks/base/services/java/com/android/server/SystemServer.java
// Initialize native services.System.loadLibrary("android_servers");
如果需要添加JNI部分代碼, 直接在frameworks/base/services/core/jni/目錄下增加對應(yīng)文件,在frameworks/base/services/core/jni/Android.mk中加入新增文件進(jìn)行編譯即可.同時按照已有文件中JNI函數(shù)注冊方式, 寫好對應(yīng)注冊方法, 統(tǒng)一在frameworks/base/services/core/jni/onload.cpp中動態(tài)注冊函數(shù).關(guān)于JNI動態(tài)注冊知識, 可參考之前寫的一篇文章:兩種JNI注冊方式
感謝各位的閱讀,以上就是“Android怎么添加系統(tǒng)服務(wù)”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Android怎么添加系統(tǒng)服務(wù)這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
分享文章:Android怎么添加系統(tǒng)服務(wù)
文章分享:http://fisionsoft.com.cn/article/gchcoo.html