您的位置:首页 > 移动开发 > Android开发

android 开发实现静默安装

2015-04-06 15:52 609 查看
参考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就可以实现静默安装。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: