1. 程式人生 > >自動搜尋與徹底刪除APP--(三)getPackageSizeInfo

自動搜尋與徹底刪除APP--(三)getPackageSizeInfo

需要呼叫getPackageSizeInfo來計算快取大小,但PackageManager中這個方法是隱藏的,可能開發人員覺得不安全不暴露介面?用到反射的概念
Method method=MainActivity.this.getPackageManager().getClass().getMethod(..)
然後呼叫getPackageSizeInfo 方法
method.invoke(PackageManager(方法所在的類),String.class,IPackageStatsObserver.class(方法引數,本來以為第二個引數是自定義的,只要是實現了AIDL 介面並且繼承了Binder的類就可以,然後看了getPackageSizeInfo的原始碼發現IPackageStatsObserver類是確定的並且有回撥方法onGetStatsCompleted()但是這個方法是自定義的,所以這個IPackageStatsObserver類是什麼?為啥還需要AIDL,直接繼承Interface不久可以了嗎?而且要將IPackageStatsObserver.aidl檔案放在android.content.pm包下,aidl檔案C/s端的aidl檔案要有相同的包名(檔案不一致會出現問題),難道是遠端呼叫?那c/s端分別是什麼?))
下面是從網上找的getPackageSizeInfo原始碼:

public void getPackageSizeInfo(final String packageName,

        final IPackageStatsObserverobserver) {

   mContext.enforceCallingOrSelfPermission(

            android.Manifest.permission.GET_PACKAGE_SIZE, null);

   // Queue up an async operation since the package deletion may take alittle while.

   mHandler.post(new Runnable() {

        public void run() {

            mHandler.removeCallbacks(this);

//獲取應用包資訊

            PackageStats lStats = new PackageStats(packageName);

            final boolean succeded;

            synchronized (mInstallLock) {

                succeded = getPackageSizeInfoLI(packageName,lStats);

            }

            if(observer != null) {

                try {

//回撥IPackageStatsObserver的方法

                    observer.onGetStatsCompleted(lStats, succeded);

                } catch (RemoteException e){

                    Log.i(TAG,"Observer no longer exists.");

                }

            } //end if observer

        } //end run

   });

}

1)。需要注意,permission.GET_PACKAGE_SIZE許可權。

2)。PackageStats lStats將會存放著得到的資料,

3)。getPackageSizeInfoLI是主要的函式。

4) 。onGetStatsCompleted, 我們需要實現的回撥函式,這在上一篇文章中已經有體現。

那麼,在getPackageSizeInfoLI中,有:

int res = mInstaller.getSizeInfo(packageName,p.mPath,

                publicSrcDir, pStats);

而mInstaller是Installer型別的一個例項,事實上,它是與android中的守護程序installd進行通訊的,通過socket.

  1. Installer的定義如下:

class Installer {

private static final String TAG ="Installer";

InputStream mIn;

OutputStream mOut;

LocalSocket mSocket;

byte buf[] = new byte[1024];

int buflen = 0;

private boolean connect() {

    if (mSocket != null) {

        return true;

    }

    Log.i(TAG, "connecting...");

    try {

        mSocket = new LocalSocket();



        LocalSocketAddress address = newLocalSocketAddress(

            "installd", LocalSocketAddress.Namespace.RESERVED);



        mSocket.connect(address);



        mIn = mSocket.getInputStream();

        mOut = mSocket.getOutputStream();

    } catch (IOException ex) {

        disconnect();

        return false;

    }

    return true;

}

private void disconnect(){

   Log.i(TAG,"disconnecting...");

     try {

            if (mSocket != null) mSocket.close();

     } catch (IOException ex) { }

     try {

            if (mIn != null) mIn.close();

     } catch (IOException ex) { }

     try {

            if (mOut != null) mOut.close();

     } catch (IOException ex) { }

     mSocket = null;

     mIn = null;

     mOut = null;

}

private boolean readBytes(byte buffer[], int len)

private boolean writeCommand(String _cmd)

private synchronizedString transaction(String cmd)

private int execute(String cmd) {

     String res = transaction(cmd);

     try {

            return Integer.parseInt(res);

     } catch (NumberFormatException ex) {

            return -1;

     }

}

public int install(String name, int uid, int gid) {

    StringBuilder builder = newStringBuilder("install");

    builder.append(' ');

    builder.append(name);

    builder.append(' ');

    builder.append(uid);

    builder.append(' ');

    builder.append(gid);

    return execute(builder.toString());

}

public int freeCache(long freeStorageSize) {

    StringBuilder builder = newStringBuilder("freecache");

    builder.append(' ');

   builder.append(String.valueOf(freeStorageSize));

    return execute(builder.toString());

}

public int getSizeInfo(String pkgName, String apkPath,

        String fwdLockApkPath, PackageStatspStats) {

    StringBuilder builder = newStringBuilder("getsize");

    builder.append(' ');

    builder.append(pkgName);

    builder.append(' ');

    builder.append(apkPath);

    builder.append(' ');

    builder.append(fwdLockApkPath != null ?fwdLockApkPath : "!");



    String s =transaction(builder.toString());

    String res[] = s.split(" ");



    if((res == null) || (res.length != 4)){

        return -1;

    }

    try {

        pStats.codeSize =Long.parseLong(res[1]);

        pStats.dataSize =Long.parseLong(res[2]);

        pStats.cacheSize =Long.parseLong(res[3]);

        return Integer.parseInt(res[0]);

    } catch (NumberFormatException e) {

        return -1;

    }

}   

分析:

1) 在connect函式中,與它進行socket連線的是installd。Installd是android中的守護程序,用ps可以看到它,它的父程序是init,關於Android啟動流程的更加詳細的說明,請看下面這篇文章:

2) 有一些read,write函式,當然是用於對socket讀取寫資料;

3) install, freeCache, getSizeInfo只是將固定字串的命令傳入到execute函式中;

4) execute的實現,只是將命令字串通過transaction傳下去,而在中會呼叫writeCommand,去真正地將命令寫入到與之連線好的守護程序Installd。

這下,好象理清一些了:

PackageManager——aidl——- > PackageManagerService的Installer —-socket——–> Installd(c程式)。

  1. Installd:這是一個用c寫成的帶有main函式的守護程序,在開機之後由init.rc呼叫,程式碼位於:framworks/base/cmds/installd目錄下。其中,最後一個字母d,程式碼的就是“dameon”,事實上,在android中,定義了好多這樣的守護程序,正是由於它們的存在,android系統才能夠正常運轉起來,也正是由於整個系統中,像這種實現方式是android中到處可以看見的。關於Installd,給出一些可以想像得到的有趣的程式碼片段,有興趣的讀者,可以自己去看原始碼。

Installd.c:

struct cmdinfo cmds[] ={

{ "ping",                 0, do_ping },

{ "install",             3, do_install },

{ "dexopt",               3, do_dexopt },

{ "movedex",              2, do_move_dex },

{ "rmdex",                1, do_rm_dex },

{ "remove",               1, do_remove },

{ "freecache",           1, do_free_cache },

{ "rmcache",              1, do_rm_cache },

{ "protect",              2, do_protect },

{ "getsize",             3, do_get_size },

{ "rmuserdata",           1, do_rm_user_data },

};

就是從PackageManagerService的Installer 傳遞過來的命令字串。