参考Android中实现静态的默认安装和卸载应用

使用系统自带的安装程序来实现程序的安装:
android自带了一个安装程序---/system/app/PackageInstaller.apk.大多数情况下,我们手机上安装应用都是通过这个apk来安装
代码使用:

  /* 安装apk */      public static void installApk(Context context, String fileName) {          Intent intent = new Intent();          intent.setAction(Intent.ACTION_VIEW);          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);          intent.setDataAndType(Uri.parse("file://" + fileName),"application/vnd.android.package-archive");          context.startActivity(intent);      }            /* 卸载apk */      public static void uninstallApk(Context context, String packageName) {          Uri uri = Uri.parse("package:" + packageName);          Intent intent = new Intent(Intent.ACTION_DELETE, uri);          context.startActivity(intent);      }  
同时,我们还可以监听apk文件的安装:
在我们的系统安装和卸载程序的时候,系统会发送以下广播:
在安装和卸载完后,android系统会发一个广播
android.intent.action.PACKAGE_ADDED(安装)
android.intent.action.PACKAGE_REMOVED(卸载)

我们可以自定义一个广播接收者:

public void onReceive(Context context, Intent intent){          //接收安装广播           if (intent.getAction().equals("android.intent.action.PACKAGE_ADDED")) {                 //TODO              }             //接收卸载广播            if (intent.getAction().equals("android.intent.action.PACKAGE_REMOVED")) {                 //TODO          }   }  
可是如果我们需要实现静默安装,这样显然是无法满足的,下面实现两种静默安装的方式:

通过运行命令来进行安装:
在adb shell中通过pm install -f apk文件路径
我们新建一个方法,用来在adb shell环境下执行pm install命令
final String path = "/storage/sdcard0/183/test/pertest.apk";

实现一个用来执行命令的方法:

 public static String execCommand(String ...command)  {  Process process=null;  InputStream errIs=null;  InputStream inIs=null;  String result="";  try {  process=new ProcessBuilder().command(command).start();  ByteArrayOutputStream baos = new ByteArrayOutputStream();  int read = -1;  errIs=process.getErrorStream();           while((read=errIs.read())!=-1){  baos.write(read);  }  inIs=process.getInputStream();  while((read=inIs.read())!=-1){  baos.write(read);  }  result=new String(baos.toByteArray());  if(inIs!=null)  inIs.close();  if(errIs!=null)  errIs.close();  process.destroy();  } catch (IOException e) {  result = e.getMessage();  }  return result;  }  
其实,真正运行安装代码的只有这么一句话:process=new ProcessBuilder().command(command).start(); 剩下的只是为了返回给一个安装结果给用户

在mainactivity的onCreate方法中执行如下代码即可:

String result = execCommand("pm","install","-f",path);//安装apkString result = execCommand("pm","uninstall", "com.qihoo360.mobilesafe"); //卸载apk
此时这个result就是系统返回给我们的一个安装或是卸载结果
注意需要在Manifest.xml中添加如下权限:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
注意android系统出于安全考虑,我们是不能进行静默安装的,那么我们需要给该Manifest.xml中添加如下一句:
android:sharedUserId="android.uid.system"
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.cmddemo"    android:versionCode="1"    android:versionName="1.0"     android:sharedUserId="android.uid.system">
表示该apk和系统程序运行在统一个进程中,即具有系统应用程序的权限:

此时运行我们的apk文件,会出现如下错误:

Installation error: INSTALL_FAILED_SHARED_USER_INCOMPATIBLE
Please check logcat output for more details.
Launch canceled!

说明我们需要对该apk进行系统签名,我们刚才已经手动运行了一边该工程,虽然不能直接运行,可是还是有用的,因为它会在该工程的bin目录下生成一个apk文件,接下来我们进行系统签名的时候就需要用到这个apk
对apk进行系统签名,我们需要以下三个文件:

对apk进行系统签名,我们需要以下三个文件:
platform.pk8,platform.x509.pem,signapk.jar这三个文件进行签名,注意这三个签名文件是在系统的源码中才有的:
platform.x509.pem和platform.pk8这两个文件是在/build/target/product/security/路径下
signapk.jar : /out/host/linux-x86/framework/signapk.jar

将三个文件拷贝到统一个目录中,然后将我们之前生成的apk同样拷贝到该目录下,然后进入该目录中,执行如下命令进行签名:
java -jar signapk.jar platform.x509.pem platform.pk8 CmdDemo.apk afterMo.apk
其中CmdDemo.apk是我们之前生命的apk,afterMo.apk是我们生成之后的签名的apk,此时安装这个afterMo.apk,就可以实现静默安装了。
注意这里有一个限制,就是我们只能在已知的平台上来进行静默安装,也就是说我们必须需要获得该平台的以上三个签名文件才可以的,因为现在的手机厂商都会进行自己的签名来确保安全性。

通过aidl实现静默安装

进入android源码目录
在android源码目录下有一个/frameworks/base/core/java/android/content/pm包
在这个包里边有以下几个文件:
IPackageInstallObserver.aidl、IPackageDeleteObserver.aidl 和IPackageMoveObserver.aidl以及PackageManager.java文件
如果需要实现静默安装,我们需要在自己的工程里边新建一个包android.content.pm(这里需要注意,由于我们使用到了aidl,所以这里的包名必须和源码中的一样即"android.content.pm"),然后将我们需要用到的以上几个文件拷贝到该目录下:
因为用到了aidl,所以我们需要在bin目录下,新建一个aidl目录,在aidl目录下,继续新建以下目录:android/content/pm,在pm目录下,将我们需要用到的IPackageInstallObserver.aidl、IPackageDeleteObserver.aidl 和IPackageMoveObserver.aidl这三个接口文件拷贝到该目录下

安装apk程序的代码:

String fileName = "/storage/sdcard0/183/test/plug.apk";Uri uri = Uri.fromFile(new File(fileName));int installFlags = 0;PackageManager pm = getPackageManager();try {PackageInfo pi = pm.getPackageInfo("com.example.plug",PackageManager.GET_UNINSTALLED_PACKAGES);if(pi != null) {installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;}} catch (NameNotFoundException e) {}MyPakcageInstallObserver observer = new MyPakcageInstallObserver();pm.installPackage(uri, observer, installFlags, "com.example.plug"); /*静默安装回调*/      class MyPakcageInstallObserver extends IPackageInstallObserver.Stub{                        @Override          public void packageInstalled(String packageName, int returnCode) {              if (returnCode == 1) {                  Log.e("DEMO","安装成功");                  new ToastThread(InstallActivity.this,"安装成功").start();              }else{                  Log.e("DEMO","安装失败,返回码是:"+returnCode);                  new ToastThread(InstallActivity.this,"安装失败,返回码是:"+returnCode).start();              }          }      }  
卸载程序的代码:

PackageManager pm = InstallActivity.this.getPackageManager();IPackageDeleteObserver observer = new MyPackageDeleteObserver();pm.deletePackage("com.example.plug", observer, 0); /* 静默卸载回调 */      class MyPackageDeleteObserver extends IPackageDeleteObserver.Stub {                @Override          public void packageDeleted(String packageName, int returnCode) {              if (returnCode == 1) {                  Log.e("DEMO","卸载成功...");                  new ToastThread(InstallActivity.this,"卸载成功...").start();              }else{                  Log.e("DEMO","卸载失败...返回码:"+returnCode);                  new ToastThread(InstallActivity.this,"卸载失败...返回码:"+returnCode).start();              }          }      }  
另外需要在Manifest.xml文件中添加如下权限:
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>    <uses-permission android:name="android.permission.INTERNET" />    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />    <uses-permission android:name="android.permission.DELETE_PACKAGES" />    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> 
同样需要添加android:sharedUserId="android.uid.system",来告诉系统我们的工程是系统进程
最后运行该工程,同样会出现如下错误:
[2015-04-06 11:05:51 - androidauto] Installation error: INSTALL_FAILED_SHARED_USER_INCOMPATIBLE
[2015-04-06 11:05:51 - androidauto] Please check logcat output for more details.
[2015-04-06 11:05:51 - androidauto] Launch canceled!



此时我们需要对之前bin目录生成的apk进行系统签名:
java -jar signapk.jar platform.x509.pem platform.pk8 需要签名的apk 签名之后的apk
此时签名之后的apk是拥有系统权限的,此时这个apk就可以实现静默安装。


更多相关文章

  1. Android(安卓)ViewPager的简单使用
  2. Android(安卓)应用安装位置
  3. android ndk 安装
  4. Android强制联网
  5. ios应用比android大,为什么iOS版APP安装文件比Android大几倍?
  6. Android(安卓)M 新的运行时权限开发者需要知道的一切
  7. Android(安卓)M 新的运行时权限开发者需要知道的一切
  8. 强烈推荐Android新手的入门学习策略之一,Android(安卓)Studio4使
  9. React Native接入现有Android原生工程并实现简单的RN与Android通

随机推荐

  1. Android 监控网络状态
  2. Android全局异常统一处理
  3. android SharedFerence使用
  4. android 获取屏幕高度和宽度
  5. Android--WindowManager.LayoutParams(上
  6. android 模拟滑动事件
  7. Android:BT测试代码
  8. Android 判断程序是否在前台运行
  9. android如何禁止安装第三方应用
  10. Android Studio3.0 Error:Execution fail