1. 程式人生 > >第十期 基於模擬器的Helloworld Framework介面 《手機就是開發板》

第十期 基於模擬器的Helloworld Framework介面 《手機就是開發板》

https://blog.csdn.net/aggresss/article/details/53576022

        這一期我們在Android系統的Application Frameworks層提供Java介面的硬體服務,結合上一期新增的JNI方法來呼叫底層硬體。
        下面提到的程式碼儲存在https://github.com/aggresss/PHDemo.git 的Code目錄的hello_Framework檔案中,也可以直接訪問:
https://github.com/aggresss/PHDemo/tree/master/Code/hello_Framework
        在Android系統中,硬體服務一般是執行在一個獨立的程序中為各種應用程式提供服務。因此,呼叫這些硬體服務的應用程式與這些硬體服務之間的通訊需要通過代理來進行。為此,我們要先定義好通訊介面。進入到frameworks/base/core/java/android/os目錄,新增IHelloService.aidl介面定義檔案:

package android.os;  
   
interface IHelloService {  
    void setVal(int val);  
    int getVal();  
}  


然後進入 frameworks/base目錄,開啟Android.mk檔案,修改LOCAL_SRC_FILES變數的值,增加IHelloService.aidl原始檔:

LOCAL_SRC_FILES += /
....................................................................
core/java/android/os/IVibratorService.aidl /
core/java/android/os/IHelloService.aidl /
core/java/android/service/urlrenderer/IUrlRendererService.aidl /
.....................................................................


執行 mmm frameworks/base 這樣,就會根據IHelloService.aidl生成相應的IHelloService.Stub介面。
進入到frameworks/base/services/java/com/android/server目錄,新增HelloService.java檔案:

package com.android.server;  
import android.content.Context;  
import android.os.IHelloService;  
import android.util.Slog;  
 
public class HelloService extends IHelloService.Stub {  
    private static final String TAG = "HelloService";  
    HelloService() {  
        init_native();  
    }  
    public void setVal(int val) {  
        setVal_native(val);  
    }     
    public int getVal() {  
        return getVal_native();  
    }  
      
    private static native boolean init_native();  
        private static native void setVal_native(int val);  
    private static native int getVal_native();  
};  


修改同目錄的SystemServer.java檔案,在ServerThread::run函式中增加載入HelloService的程式碼:

@Override
public void run() {
....................................................................................
try {
Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
Slog.e(TAG, "Failure starting DiskStats Service", e);
}
try {
Slog.i(TAG, "Hello Service");
ServiceManager.addService("hello", new HelloService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Hello Service", e);
}
......................................................................................
}

執行 mmm frameworks/base/services/java
然後 make snod 重新生成system.img
重新打包後的system.img系統映象檔案就在Application Frameworks層中包含了我們自定義的硬體服務HelloService了,並且會在系統啟動的時候,自動載入HelloService。這時,應用程式就可以通過Java介面來訪問Hello硬體服務了。
=======================分割線==========================
        但是從android5.0以後的系統引入了SELinux,SELinux定義了系統中每個使用者,程序,應用和檔案的訪問和轉變的許可權,然後它使用一個安全策略來控制這些實體(使用者、程序、應用和檔案)之間的互動,安全策略指定如何嚴格或寬鬆地進行檢查。
Android 5.0以後,因為採取了SEAndroid/SElinux的安全機制,即使擁有root許可權,或者對某核心節點設定為777的許可權,仍然無法在JNI層訪問。要想讓JNI可以成功的訪問/dev/hello硬體就必須修改SELinux的策略,否則Android系統再啟動是就是出現 
add_service ('hello',4e) uid=1000 - PERMISSION DENIED 的錯誤資訊。
這裡有兩篇參考的部落格,裡面詳細的講解了怎麼修改SELinux策略:
http://blog.csdn.net/eliot_shao/article/details/51770558
http://blog.csdn.net/wh_19910525/article/details/45170755
AOSP中,SELinux相關的策略配置檔案儲存在 /external/sepolicy/中,為了完成我們這次實驗,需要修改5個 .te 檔案,可通過訪問https://github.com/aggresss/PHDemo/tree/master/Code/hello_Framework/sepolicy 獲得

具體的檔案改動,都在 #add by aggresss 標籤下。
為了確保修改後的 .te 檔案被成功的編譯進system.img 建議執行一次 make update-api ,然後重新執行 make 進行編譯。
=======================分割線==========================
        上面的步驟完成後,我們來驗證一下 HelloService 是否啟動成功,因為只有驗證成功後我們才可以進行下一步,如果每完成一步都不驗證,到最後實驗出現問題時,一層一層的分析起來太複雜了。
我們驗證的方式是檢視android啟動時的列印資訊,因為我們的HelloService的原始碼內都設定有除錯資訊,啟動成功和失敗都會有資訊輸出,在Android中,當系統核心啟動後,所有的列印資訊都通過logcat機制進行輸出,所以只要獲取到logcat資訊就可以對已啟動的Android系統進行除錯分析,這裡我們使用android studio 內整合的功能,在開啟模擬器前先啟動android studio ,選擇 Android Monitor 。然後啟動模擬器,當核心載入成功進入到Andorid系統後,logcat便會收到系統資訊。因為資訊非常多,所以我們進行一下篩選,在filter內輸出 "hello" ,當andorid系統完全啟動後,就能搜尋到和hello 相關的資訊,如下圖所示:

        當出現 Hello JNI: hello device is open. 的資訊後就說明我們新增的HelloService服務已經成功執行。