(原創,轉載請注明出處! http://www.cnblogs.com/Martinium/articles/mirage_install.html )
有一天,客戶提出了這樣一個需求。提供一定數目的 APK 包,要求預先安裝到系統里,並且可以卸載。很顯然,這些 APK 只能 install 到 /data/app/ 目錄下,push 到 /system/app/ 是不能卸載的。
大家心里肯定在想,如果能把這些 APKs 直接燒錄到鏡像文件里去就好了,最好是 data.img,燒錄完成后一啟動,那些APK就安靜地躺在 /data/app/ 那里了。可惜這樣行不通。源碼編譯出來的APK都是放在 /system/app/ 目錄下的,這樣用戶那邊又卸載不了。
如果手頭有 adb 工具,一切也好辦,一條命令解決。
find <dir> -name "*\.apk" -exec adb install {} \;
<dir> 替換成自己的那些 APK 所在的目錄。adb 需要環境變量 PATH 能搜索到,或者用 adb 的全路徑。
可是現在工廠批量生產,沒有提供 adb 工具,或者某些客戶先前禁止了 adb 打開的功能選項。於是選擇從 SD 卡安裝。待用戶手動點擊,一個一個地點擊安裝,確認,安裝完成,這效率多低下啊!如果另外自己寫一個 APK,頭一次開機綁定一個 service,在 AndroidManifest.xml 里添加 permission,
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
在第一次啟動的時候安裝,也就完事了。
可客戶又說了,不希望看到多余的 APK 喔,於是打算修改下 category ,將
<category android:name="android.intent.category.LAUNCHER" />
改成
<category android:name="android.intent.category.DEFAULT" />
客戶說不行,在 Settings->Apps->ALL 下面仍然看得到。而且卸載不了。我知道自己寫的這個APK必須有system權限才能悄悄的安裝的,於是卸載不了。我想把這個 service 插入到某個系統 APK(如packages/app/PackageInstaller)來堵住他的口,心里更想着怎么揍他一頓。
好吧,冷靜,冷靜,讓我再想想其他辦法。
在 PC 上,是用adb (Android Debug Bridge,源碼路徑 system/core/adb/)命令安裝;
在 Android 設備上,可以用 pm (PackageManger ,源碼路徑 frameworks/base/cmds/pm/)命令呀!
最終,我寫了一個簡單的腳本,取名 mirage_install.sh
#!/system/bin/sh MARK=/data/local/FACTORY_RESET PKGS=/mnt/external_sd/mirage_package if [ ! -e $MARK ]; then echo "booting the first time, so pre-install some APKs." busybox find $PKGS -name "*\.apk" -exec sh /system/bin/pm install {} \; # NO NEED to delete these APKs since we keep a mark under data partition. # And the mark will be wiped out after doing factory reset, so you can install # these APKs again if files are still there. # busybox rm -rf $PKGS touch $MARK echo "OK, installation complete." fi
/mnt/external_sd/mirage_package 對應 SD 卡那些 APK 的文件夾,Android 分存儲設備為 Internal Storage 和 External Storage,后者對應 SD。其中設置文件 /data/local/FACTORY_RESET 為標志位“是否是首次啟動”。首次啟動會檢測路徑是否存在並靜默安裝,以后啟動的話無影響。除非通過恢復出廠設置(factory reset)來擦除這個臟位,清零后沒有第三方應用了,於是又可以再次安裝了,所以不用刪除 SD 卡的那些包。
這里用到了 busybox 工具,可以自己下載 busybox 源碼編譯生成,或者修改 frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java 文件,大概思路是:判斷 路徑 /mnt/external_sd/mirage_package 存在與否,然后調用文件里面的 install 函數安裝。不過最好不要改動源碼。
接下來,我嘗試在 init.rc 文件里添加這個 service 成功。
service mirage_install /system/bin/sh /system/bin/mirage_install.sh user root group root disabled oneshot on property:init.svc.bootanim=stopped start mirage_install
這個服務的名字叫 mirage_install,執行 sh /system/bin/mirage_install.sh 這個腳本。
有關 Android Init Language 的講解在源碼 system/core/init/readme.txt 中,可以自己查看意思。
這里 oneshot 表示 Do not restart the service when it exits.
而且在ANDROID開機動畫停止后(init.svc.bootanim=stopped)啟動該項服務。嘗試過其他條件,比如 dev.bootcomplete=1,也就是在 kernel 啟動完成后,這樣是不行的,有關的系統服務還沒有起來,關於其他的 property,可以在控制台下用 getprop 查看, setprop 修改.
注意給 mirage_install.sh 文件加執行權限 chmod a+x mirage_install.sh
然后在 device/<company>/<product>/device.mk 文件里添加下面一行
PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/system/bin/mirage_install.sh:system/bin/mirage_install.sh
冒號左邊對應 mirage_install.sh 文件的相對路徑,根據自己放置的路徑而定,這里的 LOCAL_PATH 就是 device.mk 所在的文件夾,右邊固定為 system/bin/mirage_install.sh,表示將復制到 Android 設備上的 /system/bin/mirage_install.sh 路徑下。
重新編譯 Android 工程,將 APK 包放到 SD 卡的 mirage_package 路徑下,然后燒錄生成的 *.img文件,看到了 APK 的靜默安裝,OK,大工告成!