http://blog.csdn.net/sk719887916/article/details/50314017
Aandroid應用管理
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