实现真正意义上的静默安装,两种方式:
1 2 3 4 5 6 pm install -r <test.apk> # 或 通过反射调用 PackageManager.installPackage(...)
使用pm 命令需要满足三个条件,使用反射机制要满足前两个条件
在Manifest.xml根节点中加入 **android:sharedUserId=”android.uid.system” **
为Apk加上系统平台数字签名
apk要预装在/system/app下面
这些需要在有源码的基础上才可以做到,这些条件可如下实现。
Manifest.xml
1 2 3 4 5 6 7 8 9 <manifest xmlns:android ="http://schemas.android.com/apk/res/android" xmlns:tools ="http://schemas.android.com/tools" package ="site.your.package" android:sharedUserId ="android.uid.system" > ...</manifest >
数字签名
在安卓源码中拿到以下工具
在android源码下 build/target/product/security 找到两个密钥文件 platform.x509.pem platform.pk8
out/host/Linux-x86/framework/signapk.jar找到系统封装工具signapk.jar
使用命令给 build 好的apk进行签名
1 2 java -jar signapk.jar /path /to/platform.x509.pem /path /to/platform.pk8 /path /to/test.apk /path /to/test-sign.apk
test-sign.apk
系统预装App
在如下路径创建目录 Test 存放对应的test-sign.apk (视平台而定)
1 2 android/device/softwinner/common/prebuild/apk/Test/test-sign.apk
在该目录下创建Android.mk, 编辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 LOCAL_PATH :=$(call my-dir ) include $(CLEAR_VARS) LOCAL_MODULE := Test LOCAL_MODULE_TAGS := optional LOCAL_CERTIFICATE := platform LOCAL_DEX_PREOPT := false LOCAL_MODULE_CLASS := APPS LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) LOCAL_CERTIFICATE :=PRESIGNED LOCAL_SRC_FILES := test-sign.apk LOCAL_PREBUILD_JNI_LIBS :=@lib/$(TARGET_ARCH) /libjni.soinclude $(BUILD_PREBUILT)
在方案mk文件(device/vendor-name/device-name/product-name.mk)中的 PRODUCT_PACKAGES项中加入:
1 2 PRODUCT_PACKAGES += Test (模块的唯一名字)
至此, app被提升为系统预装app, 系统启动后进入 adb shell, 可在 /system/app/ 下面看到Test.apk。
反射调用 installPackage()
需要在项目根目录建立相关的package 和 class:
IPackageDeleteObserver:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public interface IPackageDeleteObserver { abstract 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!" ); } } void packageDeleted (java.lang.String packageName, int returnCode) throws android.os.RemoteException; }
IPackageInstallObserver:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public interface IPackageInstallObserver { abstract 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!" ); } } void packageInstalled (java.lang.String packageName, int returnCode) throws android.os.RemoteException; }
PackageDeleteObserver:
1 2 3 4 5 6 7 8 9 public class PackageDeleteObserver extends IPackageDeleteObserver .Stub { @Override public void packageDeleted (String packageName, int returnCode) throws RemoteException { } }
PackageInstallObserver:
1 2 3 4 5 6 7 8 public class PackageInstallObserver extends IPackageInstallObserver .Stub { @Override public void packageInstalled (String packageName, int returnCode) throws RemoteException { } }
ContextPackageManager(类名自定义):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class ContextPackageManager { private static final int INSTALL_REPLACE_EXISTING = 0x00000002 ; private static final int INSTALL_ALL_USERS = 0x00000040 ; public static void installPackage (Context context, File apk) { PackageManager pm = context.getPackageManager(); Class<?>[] types = new Class []{Uri.class, IPackageInstallObserver.class, int .class, String.class}; try { Method installPackage = pm.getClass().getMethod("installPackage" , types); installPackage.invoke(pm, Uri.fromFile(apk), new PackageInstallObserver (), INSTALL_ALL_USERS, null ); } catch (Exception e) { e.printStackTrace(); } } public static void updatePackage (Context context, File apk) { PackageManager pm = context.getPackageManager(); Class<?>[] types = new Class []{Uri.class, IPackageInstallObserver.class, int .class, String.class}; try { Method installPackage = pm.getClass().getMethod("installPackage" , types); installPackage.invoke(pm, Uri.fromFile(apk), new PackageInstallObserver (), INSTALL_REPLACE_EXISTING, null ); } catch (Exception e) { e.printStackTrace(); } } public static void deletePackage (Context context) { PackageManager pm = context.getPackageManager(); Class<?>[] types = new Class []{String.class, IPackageDeleteObserver.class, int .class}; try { Method deletePackage = pm.getClass().getMethod("deletePackage" , types); deletePackage.invoke(pm, DeviceConfig.PACKAGE_NAME, new PackageDeleteObserver (), 0 ); } catch (Exception e) { e.printStackTrace(); } } }
调用处一行代码即可:
1 2 ContextPackageManager.updatePackage(context, new File (appPath));
pm insatll 命令安装
调用pm 命令,如下:
1 2 ShellUtil.executeCommand(new String []{"pm install -r /sdcard/.../test.apk" });
ShellUtil.executeCommand():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 void executeCommand (String[] commands) { Process process = null ; BufferedReader successResult = null ; BufferedReader errorResult = null ; StringBuilder successMsg = null ; StringBuilder errorMsg = null ; DataOutputStream os = null ; try { process = Runtime.getRuntime().exec("sh" ); os = new DataOutputStream (process.getOutputStream()); for (String command : commands) { if (command == null ) { continue ; } os.write(command.getBytes()); os.writeBytes("exit\n" ); os.flush(); } os.writeBytes("\n" ); os.flush(); int result = process.waitFor(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (os != null ) { os.close(); } if (successResult != null ) { successResult.close(); } if (errorResult != null ) { errorResult.close(); } } catch (IOException e) { e.printStackTrace(); } if (process != null ) { process.destroy(); } } }
pm静默安装参考Trinea