1. 程式人生 > >Android完全禁止第三方軟體安裝的方法

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,依然失敗!!

至此,大功告成~