Android完全禁止第三方軟體安裝的方法
這段時間在給公司的產品做CTA認證,公司的產品宣告不允許第三方軟體安裝,所以需要禁止掉APK的安裝功能。一開始我把Packageinstaller.apk從系統裡面刪了,試了一下,放一個APK到SD卡,點選安裝,確實安裝不了,我以為就成功了,心想還挺簡單麼!!
不過,CTA認證實驗室說,不行,還是可以安裝第三方軟體!仔細問了一下,他們是通過PC端類似於“手機助手”的軟體安裝上去的。自己趕緊試了一下,確實可以安裝,又試了一下adb install命令,也可以安裝!!
得,還是問問軟體開發的同事吧,給個patch也行呀,誰知道人家太忙,沒時間搞,但是這事情分給我幹了,還是硬著頭皮搞吧。不過,對於一個搞驅動的,碰到這種應用的問題,半天都沒思路呀!!不過,功夫不負有心人,百度了一天,終於搞定了。
囉嗦完了,說正事!!
通過網上大神們對於Android系統APK安裝過程的分析,系統安裝APK主要的原始碼在檔案“frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java”裡,順便說一下,我用的是Android 5.0系統。
Android系統安裝APK有4種方式:
1. 開機時系統應用的安裝;
2. 手機APP下載應用安裝;
3. ADB命令安裝;
4. 點選APK檔案安裝;(我之前刪除的Packageinstaller.apk就是負責這種安裝)
第1種安裝方式函式呼叫結構大致如下:
PackageManagerService
->scanDirLI
–>scanPackageLI(File scanFile, …)
—>(省略)
—->mInstaller.install
剩下3種安裝方式,雖然開始是從不同介面進入,但是最後統一的呼叫如下:
installPackage
->scanPackageLI(PackageParser.Package pkg, …)
–>scanPackageDirtyLI
—>createDataDirsLI
—->mInstaller.install
由於不是很懂JAVA語法規則,所以,一開始我以為,第1個“scanPackageLI”函式應該不會像下面的“scanPackageLI”函式一樣呼叫“createDataDirsLI”函式,所以我做如下修改“createDataDirsLI”函式:
private int createDataDirsLI(String packageName, int uid, String seinfo) {
int[] users = sUserManager.getUserIds();
//直接返回失敗,禁止安裝APK
return PackageManager.INSTALL_FAILED_INVALID_APK;
/*
int res = mInstaller.install(packageName, uid, uid, seinfo);
if (res < 0) {
return res;
}
for (int user : users) {
if (user != 0) {
res = mInstaller.createUserData(packageName,
UserHandle.getUid(user, uid), user, seinfo);
if (res < 0) {
return res;
}
}
}
return res;
*/
}
編譯執行之後發現,系統卡在開機動畫那裡不動了!!
所以,我估計第1種安裝方式的函式“scanPackageLI”一樣呼叫了“createDataDirsLI”函式,這個時候就麻煩了,系統APK的安裝和其他方式APK安裝呼叫一樣的函式!!
然後又去百度,看了大神的關於Android系統啟動時APK安裝過程的分析後,想到一個辦法,定義一個標誌位,用來標記系統啟動時APK是否安裝完成,當系統APK安裝沒有完成時,“createDataDirsLI”函式正常工作,當系統啟動完成,讓“createDataDirsLI”函式失效。
系統啟動時安裝APK呼叫的函式為上面第1種安裝方式的“PackageManagerService”,然後呼叫“scanDirLI”函式完成安裝APK,安裝完成後,“呼叫updatePermissionsLPw來申請為了特定的資源訪問許可權的apk分配相應的linux使用者組ID”,這是文章原話。
所以先定義一個標誌位:private static boolean APK_install_finish = false;
初始化為“false”代表系統APK安裝還沒有完成。
找到“PackageManagerService”函式內“updatePermissionsLPw”函式的呼叫處,發現只有一處。
在“updatePermissionsLPw”函式呼叫之前,“scanDirLI”函式呼叫之後,改變“APK_install_finish ”變數的值,表示系統APK安裝完成,程式碼如下:
...
Slog.i(TAG, "Time to scan packages: "
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
//系統APK安裝完成
APK_install_finish = true;
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
final boolean regrantPermissions = mSettings.mInternalSdkPlatform
!= mSdkVersion;
if (regrantPermissions) Slog.i(TAG, "Platform changed from "
+ mSettings.mInternalSdkPlatform + " to " + mSdkVersion
+ "; regranting permissions for internal storage");
mSettings.mInternalSdkPlatform = mSdkVersion;
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
| (regrantPermissions
? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
: 0));
...
然後重新修改“createDataDirsLI”函式:
private int createDataDirsLI(String packageName, int uid, String seinfo) {
int[] users = sUserManager.getUserIds();
//如果系統APK安裝完成,則禁止安裝任何APK
if(APK_install_finish)
{
return PackageManager.INSTALL_FAILED_INVALID_APK;
}
int res = mInstaller.install(packageName, uid, uid, seinfo);
if (res < 0) {
return res;
}
for (int user : users) {
if (user != 0) {
res = mInstaller.createUserData(packageName,
UserHandle.getUid(user, uid), user, seinfo);
if (res < 0) {
return res;
}
}
}
return res;
}
只有當系統APK安裝完成之後,才讓“createDataDirsLI”函式失效。
編譯下載執行,可以正常開機,開機之後,先使用ADB安裝APK,失敗;然後點選APK裝包,安裝失敗;再使用PC端“手機助手”安裝APK,依然失敗!!
至此,大功告成~