Android實踐 -- App的靜默安裝和解除安裝
App的靜默安裝和解除安裝
Android系統本身提供了安裝解除安裝功能,但是api介面是@hide
的,不是公開的介面,所以在應用級別
是無法實現靜默安裝和解除安裝的,要實現靜默安裝和解除安裝需要是系統應用,要有系統簽名和相應的許可權
簡單思路如下:
1. 通過反射獲得安裝介面installPackage
和 解除安裝介面 deletePackage
2. 在自己的包中引入兩個介面IPackageInstallObserver
和IPackageDeleteObserver
的空實現
3. 呼叫安裝解除安裝的方法,回撥上面的兩個介面
4. 新增許可權 <uses-permission android:name="android.permission.DELETE_PACKAGES"/>
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
5. 進行系統簽名
6. 將應用push到系統中,作為系統應用
在PackageManager
中的提供的介面如下
安裝介面
// @SystemApi public abstract void installPackage( Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName);
解除安裝介面
// @SystemApi public abstract void deletePackage( String packageName, IPackageDeleteObserver observer, int flags);
引入兩個回掉的空實現
在自己應用的工程中新建一個包android.content.pm
,並新增兩個檔案
* IPackageDeleteObserver.java
package android.content.pm;
public interface IPackageDeleteObserver extends android.os.IInterface {
public abstract static class Stub extends android.os.Binder implements android.content.pm.IPackageDeleteObserver {
public Stub() {
throw new RuntimeException("Stub!");
}
public static android.content.pm.IPackageDeleteObserver asInterface(android.os.IBinder obj) {
throw new RuntimeException("Stub!");
}
public android.os.IBinder asBinder() {
throw new RuntimeException("Stub!");
}
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
throw new RuntimeException("Stub!");
}
}
public abstract void packageDeleted(java.lang.String packageName, int returnCode)
throws android.os.RemoteException;
}
IPackageInstallObserver.java
package android.content.pm; public interface IPackageInstallObserver extends android.os.IInterface { public abstract static class Stub extends android.os.Binder implements android.content.pm.IPackageInstallObserver { public Stub() { throw new RuntimeException("Stub!"); } public static android.content.pm.IPackageInstallObserver asInterface(android.os.IBinder obj) { throw new RuntimeException("Stub!"); } public android.os.IBinder asBinder() { throw new RuntimeException("Stub!"); } public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { throw new RuntimeException("Stub!"); } } public abstract void packageInstalled(java.lang.String packageName, int returnCode) throws android.os.RemoteException; }
自己定義個介面回撥
OnPackagedObserver.java
package com.example; public interface OnPackagedObserver { public void packageInstalled(String packageName, int returnCode); public void packageDeleted(String packageName,int returnCode); }
實現方法
反射介面
PackageManager pm = context.getPackageManager(); Class<?>[] types = new Class[] {Uri.class, IPackageInstallObserver.class, int.class, String.class}; Class<?>[] uninstalltypes = new Class[] {String.class, IPackageDeleteObserver.class, int.class}; Method method = pm.getClass().getMethod("installPackage", types); Method uninstallmethod = pm.getClass().getMethod("deletePackage", uninstalltypes);
實現回撥介面
private OnPackagedObserver onInstalledPackaged; class PackageInstallObserver extends IPackageInstallObserver.Stub { public void packageInstalled(String packageName, int returnCode) throws RemoteException { if (onInstalledPackaged != null) { onInstalledPackaged.packageInstalled(packageName, returnCode); } } } class PackageDeleteObserver extends IPackageDeleteObserver.Stub { public void packageDeleted(String packageName, int returnCode) throws RemoteException { if (onInstalledPackaged != null) { onInstalledPackaged.packageDeleted(packageName, returnCode); } } }
實現
解除安裝介面只需要提供要解除安裝的應用的包名packagename
,安裝提供了三個介面public void uninstallPackage(String packagename) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { uninstallmethod.invoke(pm, new Object[] {packagename, observerdelete, 0}); } public void installPackage(String apkFile) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { installPackage(new File(apkFile)); } public void installPackage(File apkFile) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { if (!apkFile.exists()) throw new IllegalArgumentException(); Uri packageURI = Uri.fromFile(apkFile); installPackage(packageURI); } public void installPackage(Uri apkFile) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { method.invoke(pm, new Object[] {apkFile, observer, INSTALL_REPLACE_EXISTING, null}); }
簽名
生成一個apk檔案,需要對這個apk檔案進行系統簽名,由於<uses-permission android:name="android.permission.DELETE_PACKAGES"/>
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
是系統應用需要的許可權,在開發應用時,如果載入AndroidManifest.xml
中會編譯不過,需要先用工具 apktool
工具先把apk檔案解壓出來,用編輯器在AndroidManifest.xml中加入上面的兩個許可權,然後在用工具apktool
重新打包
具體的使用方法參考 [Android實踐 -- Apktool的使用](http://blog.csdn.net/coderminer/article/details/52815890)
解壓
apktool d test.apk
修改之後,重新打包
apktool b test
java -jar signapk.jar platform.x509.pem platform.pk8 test.apk test_signed.apk
將簽名之後的檔案
push
到手機中,需要root
許可權
具體的程式碼實現
附錄,安裝解除安裝錯誤碼速查
回撥中的returnCode
在PackageManager
中的相關的定義如下:
* 安裝錯誤碼
public static final int INSTALL_SUCCEEDED = 1;
public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
public static final int INSTALL_FAILED_INVALID_APK = -2;
public static final int INSTALL_FAILED_INVALID_URI = -3;
public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
public static final int INSTALL_FAILED_DEXOPT = -11;
public static final int INSTALL_FAILED_OLDER_SDK = -12;
public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
public static final int INSTALL_FAILED_NEWER_SDK = -14;
public static final int INSTALL_FAILED_TEST_ONLY = -15;
public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
public static final int INSTALL_FAILED_UID_CHANGED = -24;
public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;
public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;
public static final int INSTALL_FAILED_NO_MATCHING_ABIS = -113;
public static final int NO_NATIVE_LIBRARIES = -114;
public static final int INSTALL_FAILED_ABORTED = -115;
解除安裝錯誤碼
public static final int DELETE_SUCCEEDED = 1; public static final int DELETE_FAILED_INTERNAL_ERROR = -1; public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2; public static final int DELETE_FAILED_USER_RESTRICTED = -3; public static final int DELETE_FAILED_OWNER_BLOCKED = -4; public static final int DELETE_FAILED_ABORTED = -5;