android 开发实现静默安装

         参考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);//安装apk
String 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就可以实现静默安装。


你可能感兴趣的:(android,静默安装)