1. 程式人生 > >新增有原始碼apk到系統目錄下編譯(使應用擁有系統應用許可權)

新增有原始碼apk到系統目錄下編譯(使應用擁有系統應用許可權)

需求

當我們的應用想要使用一些系統應用才能使用的功能時該怎麼辦呢?如何讓我們的應用“變成”系統應用? 現在我寫了一個測試demo,用於開啟GSP,但是2.3(還是2.1?)以後不允許三方應用直接開啟location開關。那我們該怎麼做呢?
先不管應用系統(system app)的限制,我們照常寫app。許可權該新增的新增,即使出現下面的提示:
然後安裝執行,應用可能強退或者操作無響應。 大概log會提示
  1. Caused by: java.lang.SecurityException:Permission denial: writing to settings requires:
    android.permission.WRITE_SECURE_SETTINGS
沒關係,下面我們有三種方法讓這個應用可用。 1. 給應用系統簽名
2. 不帶原始碼直接放apk,mm編譯 3. 帶原始碼mm編譯 生成的apk放到system/app 或者priv-app目錄下,讓系統認為這是一個系統應用。
第1個方法最快,第2,3可以應用到預置apk的需求中。 下面我們分別說一下具體的步驟

1. 給應用系統簽名

說起來簡單也不簡單。說簡單因為就一條命令:
  1. java -jar signapk.jar platform.x509.pem platform.pk8 app-debug.
    apk ControlCenter_signed.apk
檔案目錄: signapk.jar: android/out/host/linux-x86/framework/signapk.jar 另外兩個檔案在 android/build/target/product/security/
除了platform簽名以後還有 testkey media shared releasekey。 系統級別的簽名使用的是platform來簽名(此時使用android:sharedUserId="android.uid.system"才有用)。 但是使用的時候要注意,一定要使用和目標系統匹配的檔案來簽名 比如我的原始碼目中有兩組platform簽名的檔案,我第一次簽名搞錯了(我沒發現有另一組在devices目錄下的檔案),所有才有了後面兩種方法的嘗試。
之後我重新嘗試另一組簽名檔案,簽名成功後是可以符合期望工作的。

2.  不帶原始碼直接放apk,mm編譯

其實這一步也可以叫做“預置不帶原始碼的apk”。 1) 使用這種方法我們要為apk新建一個目錄(一般自己的應用預置在packages/apps目錄下,三方的可能在Vendor/third-party下), 2) 把apk放到目錄下 3) 編寫一個對應的Android.mk檔案。 這裡我們以googel translate.apk的預置為例,並以這個為參考編寫我們自己的Android.mk。 下面是Translate目錄下的Android.mk部分內容:
  1. # Translate
  2. LOCAL_PATH := $(call my-dir)
  3. include $(CLEAR_VARS)
  4. # Module name should match apk name to be installed
  5. LOCAL_MODULE :=Translate
  6. LOCAL_MODULE_TAGS := optional
  7. LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
  8. LOCAL_MODULE_CLASS := APPS
  9. LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
  10. LOCAL_CERTIFICATE := PRESIGNED
  11. LOCAL_BUILT_MODULE_STEM := package.apk
  12. include $(BUILD_PREBUILT)
這裡LOCAL_CERTIFICATE := PRESIGNED,表示保留原簽名,如果我們想讓apk擁有和系統一樣的簽名則需要改成
  1. LOCAL_CERTIFICATE := platform
  2. LOCAL_PRIVILEGED_MODULE :=true
4)之後在目錄下mm編譯。把生成的檔案push到手機上就可以用了。 5)目前為止我們只是拿到系統應用的許可權,如果需要在整包編譯的時候把這個apk也編譯進去的話,需要修改對應的編譯系統的.mk檔案,新增這個模組
  1. PRODUCT_PACKAGES += \
  2. Books \
  3. Bugle \
  4. GoogleCamera \

3. 帶原始碼mm編譯

加入現在已經有一個使用Android Studio編寫的app了,我們想要把它的原始碼放到Android系統目錄下編譯,該如何做呢? 1) 跟2差不多,還是先建目錄已packages/app/目錄下面為例,新建一個ControlCenter的目錄, 2) 然後把原始碼全部放進去,然後把AndroidMainfest.xml複製到根目錄。 如果不做清理的話,目錄就是這個樣子的
這裡提醒一下,如果要保持Android Studio Project的目錄格式的話,在用mm編譯前要把build目錄,test和androidTest目錄刪掉才能成功編譯。 如果有強迫症的話也可以刪除其他檔案,改成下面的樣子。
是不是簡潔了很多? 但是上面那種有個好處,既可以mm編譯,又可以單獨用Android Studio編譯。看自己需求。 3) 新建Android.mk檔案。貼一下Android.mk檔案內容:
  1. LOCAL_PATH:= $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE_TAGS := optional
  4. LOCAL_SRC_FILES := $(call all-subdir-java-files)
  5. #將res移動到這個應用的根目錄
  6. #LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
  7. #如果你是Android Studio的res目錄
  8. LOCAL_RESOURCE_DIR+= $(LOCAL_PATH)/app/src/main/res
  9. #apk名字
  10. LOCAL_PACKAGE_NAME :=ControlCenter
  11. #系統簽名
  12. LOCAL_CERTIFICATE := platform
  13. #如果有使用到依賴
  14. LOCAL_STATIC_JAVA_LIBRARIES:= \
  15. android-support-v4
  16. LOCAL_PRIVILEGED_MODULE :=true
  17. include $(BUILD_PACKAGE)
mm/mma編譯 4)同樣如果編整包的時候把這個也編譯進去,要修改對應的.mk檔案。 因為我只是寫了個簡單的demo,jni和lib等不在本次討論之列。 注意:這裡和上面有個區別,編譯apk的時候我們用LOCAL_PACKAGE_NAME定義應用名,預置apk的時候使用LOCAL_MODULE定義模組名

期間遇到的問題

mm編譯不過 錯誤提示:
  1. packages/apps/ControlCenter/res/layout/activity_main.xml:14: error:Error:This attribute must be localized.(at 'text' with value 'wifi').
對應的程式碼:
  1. <Switch
  2. android:id="@+id/switch_wifi"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content"
  5. android:layout_weight="1"
  6. android:text="wifi"
  7. android:onClick="onSwitchClicked"/>
居然不允許直接使用字元,修改成@string後編譯正常了。

總結

到此為止我們的需求已經通過三種方法實現了。 在有簽名檔案的情況下第一種方法最快捷。 第二種方法適合沒有原始碼的情況,或者app已經穩定不需要修改的情況下。而且可以預置到系統中。 第三種方法適合有原始碼,而且這個app可能隨時調整的情況。

擴充套件

一些Android.mk引數說明: LOCAL_PATH:= $(call my-dir) 一個Android.mk file首先必須定義好LOCAL_PATH變數。它用於在開發樹中查詢原始檔。
巨集函式’my-dir’,由編譯系統提供,用於返回當前路徑(即包含Android.mk file檔案的目錄)。
#begin 在加入這個巨集之前apk在/system/app下 #LOCAL_PRIVILEGED_MODULE := true  #end 加入後apk在/system/priv-app下 #這個巨集控制的是apk可解除安裝,恢復出廠設定後無法恢復 #LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) #這個巨集控制系統給apk簽名 LOCAL_CERTIFICATE := PRESIGNED #這個巨集控制的是apk可解除安裝,恢復出廠設定後可以恢復 LOCAL_MODULE_PATH := $(TARGET_OUT)/vendor/operator/app Android.mk中可以定義多個編譯模組,每個編譯模組都是以include $(CLEAR_VARS)開始,以include $(BUILD_XXX)結束(詳解參考文末第二個連結)。
LOCAL_MODULE_TAGS := optional 解析: LOCAL_MODULE_TAGS :=user eng tests optional user:  指該模組只在user版本下才編譯
eng:  指該模組只在eng版本下才編譯
tests: 指該模組只在tests版本下才編譯
optional:指該模組在所有版本下都編譯
取值範圍debug eng tests optional samples shell_ash shell_mksh。注意不能取值user,如果要預裝,則應定義core.mk。

參考:

4. 帶有原始碼的apk預置到系統 http://blog.csdn.net/u013377887/article/details/53870803

推薦閱讀