Android PackageManager源碼淺析以及靜默安裝實現方式


http://blog.csdn.net/sk719887916/article/details/50314017

Aandroid應用管理

     
       2016了 本篇成了我的開年之博文,距上次做靜默安裝和輔助服務已經半年多了,最近一直在做項目中的插件功能,也一直沒時間整理平時接觸的東西,甚至年終總結,今天就從經常用到的知識來開始2016的道路吧。
  
   Aandroid的應用管理主要由PMS(PackageManagerService)來負責管理;上層上來由PackageManager來進行管理,通過PM我們可以得到設備上的所有安裝包信息,包括未安裝和安裝過的, 未安裝的包信息采用反射和未暴露的API也可以進行深度解析得到我們想要的信息。而應用的的安裝和卸載也有PM負責。
今天我們主要說一下PackageManager,至於PMS來說和上層有Binder進行交互,PMS在實際開發中我們很少直接用到,但是我們上層通過PM來獲取的一些基礎信息,都需要PMS來調用底層,當通過看源碼得知PackageManager沿用了Android面向接口編程的風格,比如viewRoot,WindowManger,ActivityManager都采用了面向接口編程,這些Mgr為我們提供了一些基礎的功能接口,具體都由各自的Service來動態注入Impl,就是我們通常說的熱插拔,至於這么寫的好處嗎 這里稍微說兩句,在接觸過java編程久的朋友都知道面向接口的可擴展性很強,因為安卓源碼也需要升級,谷歌工程師在下一個版本中或許就會新增一些api,那么這樣設計的理念也便於源碼的維護和升級,我們平時開發中也可以借鑒這種優雅的面向協議編程,當然iOS同樣適用。

PackageManager

 

   PackageManager 在android.content.pm包下,它主要來負責應用的解析,和APK的安裝,卸載和更新,那么我們可以清晰得看到此類的以下方法

   1)負責安裝
   private abstract void installPackage(
        Uri packageURI, IPackageInstallObserver observer, int flags,

  2)卸載
     private abstract void deletePackage(
        String packageName, IPackageDeleteObserver observer, int flags);


安卓系統通過以上api和底層的pms交互進行安裝,我們的普通apk無法直接安裝的第三方應用的, 因為源碼沒有開放其方法,只有我們發送一條安裝意圖才可以交友pms來安裝apk,具體由系統級別的apk(包名com.android.packageinstaller)來進行處理。


    Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setAction(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(file),        "application/vnd.android.package-archive");
            context.startActivity(intent);



以上安全限制並非是谷歌的撒手鐧,除了用未開放安裝的api,在調用此方法前系統也會進行權限動態監測,我們可以看看pm的又一重要方法:

  public abstract int checkPermission(String permName, String pkgName);

      這就為何證明了普通應用為何沒有安裝的權限,其內部會對調用此api的進行權限監測,如果是普通應用那么返回int值為1的返回值,在這里我們要和分析下安卓apk的級別內置應用和普通應用

 預裝程序(即相機,日歷和瀏覽器等)保存在/system/app/中。
 用戶安裝程序(APIDemo,Any.do等)保存在/data/app/中。
 當然目前安卓4.4以后內置預裝程序的app/下又會新增了pri-app/ 和/app,用來個用戶提供卸載內置程序的入口,那么在pri下的apk無法卸載的,除非我們root后才能卸載。

**

實現靜默安裝

** 
通過了解了上面pm的簡單工作原理,我們就可以想到靜默安裝的途徑

一 采用偽造系統PM(PackageManger)

   通過偽造自己的Pm實現開放的api,並且采用自己的IPackageInstallObserver,說道這里你估計會不明白此類用來干嘛的。此觀察者是用來檢測apk是否安裝的的回調,那么卸載同樣有自己的觀者這,此通過aidl和pms進行通訊,我們可以從源碼copy一份到自己的項目下面 注意的是包名和路勁必須和源碼保持一致。


     偽造安裝所需要的observer和PM后 在我們的代碼里直接掉用pm.installPackager()即可,但是又會來到權限的問題,那么怎么做到繞過權限呢,我通過改checkPermission()方法,但是沒用,即使我返回0也無法達到繞過權限的問題,那么,今天的靜默安裝也到此無法達到大家期望的普通靜默安裝的效果,但是在root后或者app為系統apk的時候,我們是可以做到靜默安裝的,至於安裝成功后你需要顯示什么view 我們同樣可以在回調中進行處理。

二 運行PM命令

 我們可以直接在拿到系統builder寫入pm命令,加入到系統進程中執行install方法進行安裝
 代碼如下:
 final ResultBuilder builder = Result.newBuilder();

    String[] args = {"pm", "install", "-r", getPackageUri(path).getPath()};
    String result = null;
    ProcessBuilder processBuilder = new ProcessBuilder(args);
    Process process= null;
    InputStream errIs = null;
    InputStream inIs = null;
    try{
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int read=-1;
        process = processBuilder.start();
        errIs=process.getErrorStream();
        while((read = errIs.read()) != -1){
            baos.write(read);
        }
        baos.write('\n');
        inIs=process.getInputStream();
        while((read=inIs.read())!=-1){
            baos.write(read);
        }

        byte[] data=baos.toByteArray();

        result = new String(data);

    } catch (IOException e) {
        e.printStackTrace();
        String log = e.toString() + ":" + "file is write ex!";
        Log.e("", log);
        builder.setCustomMessage(log);
    }

   當然此方法也不是完美的,也需要root權限或者系統界別的apk

三 反射方法

   我們也可以采用反射方法,通過java反射原理,反射出PM然后執行insatallPackage(),PM並非直接可以反射,它是需要ActivityTherad進行提供支持,我們通過對系統總線程的反射出PM,接着反射出installPackage方法即可
  Class<?>pmService;
Class<?> activityTherad;
Method method;


activityTherad = Class.forName("android.app.ActivityThread");
Class<?> paramTypes[] = getParamTypes(activityTherad, "getPackageManager");
method = activityTherad.getMethod("getPackageManager", paramTypes);
Object PackageManagerService = method.invoke(activityTherad);
pmService = PackageManagerService.getClass();
Class<?> paramTypes1[] =getParamTypes(pmService, "installPackage");
method = pmService.getMethod("installPackage", paramTypes1);
method.invoke(PackageManagerService, getPackageUri(Path), null, 0, null);
 
        

  總結

     注意:實現靜默安裝都需要系統級別權限才能執行,具體是否系統給予權限

  <permission Android:name="android.permission.INSTALL_PACKAGES" />

  其實root也是擁有系統級別權限的一種方式,本質是系統權限才有運行install的權限,

     我們可以請求用戶root 代碼:

                     /**
     * check rootPerssion.
     * @return
     */
    private static boolean hasRootPerssion() {
        
   
        PrintWriter PrintWriter = null;
    
        Process process = null;
    
        try {
  
            process = Runtime.getRuntime().exec("su");
    
            PrintWriter = new PrintWriter(process.getOutputStream());
    
            PrintWriter.flush();
   
            PrintWriter.close();
    
            int value = process.waitFor();


            return returnResult(value);
   
        } catch (Exception e) {
   
            e.printStackTrace();


        } finally {
  
            if (process!=null) {
                process.destroy();
            }
        }
    
        return false;
    }

 

    所以實現靜默安裝的前提必須內置或者root,其他情況無法做到靜默安裝。那么是不是我們無法做到除了以上內置的靜默安裝了呢 其實也未必

    目前我們可以采用輔助功能(Accessibility)實現自動安裝,用來代替用戶點擊,監控在com.android.packageinstaller包的界面元素來遍歷出所需要的按鈕文本,來執行安裝操作,微信搶紅包插件也是利用此原理,但是采用輔助依舊會顯示安裝界面的,我們可以在原有的系統界面上添加一個view浮層來偽裝靜默安裝功能。

    除此之外我們也可以采用動態加載來實現一個apk的安裝,其實真正意義上並非靜默安裝,這需要一個apk來做宿主,只是將我們的apk解析出所用的組件信息,保存到本地,再將宿主的上下文直接注入插件apk中,至於插件實現apk免安裝的知識有很多方式, 下期再介紹我的一種實現原理。

 

 

--

輔助安裝請看:http://blog.csdn.NET/sk719887916/article/details/46746991

 

插件免安裝:https://github.com/NeglectedByBoss/PluginLoader


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM