JKS制作及静默安装

  • 前言
  • 系统APP
  • APK的安装流程及管理:
  • 系统签名JKS制作
  • 1. 签名资料准备
  • 2. JKS文件生成
  • 3. JKS生成中遇到的问题
  • APK静默安装
  • ROOT安装法
  • 总结




前言

最近公司软件有需求要求将Android apk经系统签名以达到更新静默安装的效果。文章主要讲解如何进行系统签名的jks文件生成和静默安装的代码执行,内容及代码都是我亲自实验可行的,如遇疑问请联系我进行沟通讨论及解决。


系统APP

1. android:sharedUserId
想要将app做成系统App,必定需要在在AndroidManifest.xml中配置属性android:sharedUserId。

此声明将使APK拥有系统权限,接下来需要为它进行系统签名。否则安装时会出现 INSTALL_FAILED_SHARED_USER_INCOMPATIBLE报错

// 根据个人不同的需求进行选择,只能四选一
android:sharedUserId="android.uid.system"
android:sharedUserId="android.uid.shared"
android:sharedUserId="android.media"
android:sharedUserId="android.uid.system"

2. 系统app的签名
apk想要发布是需要签名的,系统apk的签名有两种方式。

方式1:生成系统签名jks,然后常规方式在Android Studio中Build apk安装包。
配置后调试和打包都非常方便快捷,强烈支持这种方式。

方式2:先生成普通签名apk ,然后通过如下方式签名。

// 注意将signapk.jar, platform.x509.pem ,platform.pk8,unsign.apk,sign.apk放在同一文件夹,且cmd进入该文件夹目录下。
java -jar signapk.jar platform.x509.pem platform.pk8 apk路径(签名前) apk路径(签名后)

两种方式都需要 platform.x509.pem和 platform.pk8文件,在后面的系统签名JKS制作中会详细说明两文件。

3. Android安装方式

  1. 系统应用安装,apk放在系统文件夹中,无安装界面
  2. 手机应用市场安装,每个手机自带的厂商应用市场可能是经系统文件签名,所以安装其他软件无界面
  3. ADB工具安装,没有安装界面
  4. 第三方应用安装,比如我们自己软件下载更新然后安装,有安装界面

APK的安装流程及管理:

1. apk安装流程

  1. system/app : 系统自带的应用程序,无法删除。该路径下放system.apk
  2. data/app:用户程序安装的目录,有删除权限。该路径下放customer.apk
  3. data/data: 存放应用程序的数据
  4. Data/dalvik-cache: 将apk中的dex文件(Dalvik字节码)安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)

APK的安装过程:将apk复制到data/app或system/app中,解压并扫描安装包,把dex文件保存到dalvik-cache目录,并在data/data目录下创建对应的应用数据目录。

2. apk开机管理

PackageManagerService处理各种应用的安装,卸载,管理等工作,开机时由systemServer启动此服务。PackageManagerService的启动流程如下:

1. 首先扫描安装“system\framework”目录下的jar包
scanDirLI(mFrameworkDir,PackageParser.PARSE_IS_SYSTEM,scanMode | SCAN_NO_DEX);

2. 第二步扫描安装“system\app”目录下的各个系统应用,即安装系统应用

scanDirLI(mSystemAppDir,PackageParser.PARSE_IS_SYSTEM, scanMode);

3. 第三步扫描“data\app”目录,即用户安装的第三方应用
scanDirLI(mAppInstallDir, 0, scanMode);

4. 第四步扫描" data\app-private"目录,即安装DRM保护的APK文件(此类应用极少)。
scanDirLI(mDrmAppPrivateInstallDir,0, scanMode | SCAN_FORWARD_LOCKED);

系统签名JKS制作

我们都知道apk安装到设备上需要签名制作成release 版apk.一般软件使用Android studio制作的jks签名秘钥签名就行。对于需要系统签名的APK的JKS文件则有了一些特殊额外的操作。

系统签名JKS需要Linux环境,Linux环境配置教程

1. 签名资料准备

1. 首先需要准备签名文件

它们在Android系统源码build\target\product\security 。签名文件有四种类型,它们必定是成对出现的(.pem,.pk8代表私钥,.x509.pem代表公钥 )

window Android11签名文件 签名安装包_系统签名

可以看见Android的签名有四种类型media,platform,shared,testkey四种。经过四种签名的apk具有不同的权限。

2. 准备签名工具signapk.jar

它在系统源码的out/host/linux-x86/framework/signapk.jar 中

window Android11签名文件 签名安装包_sharedUserId_02

3.签名工具 keytool-importkeypair

下载地址:

https://github.com/getfatday/keytool-importkeypair


2. JKS文件生成

签名文件及操作方法资源我

window Android11签名文件 签名安装包_系统签名_03


系统签名JKS文件需要在Linux环境下生成,在Windows环境下无法生成。linux下执行命令如下:

./keytool-importkeypair -k djKey.jks -p 123456 -pk8 platform.pk8 -cert platform.x509.pem -alias djKey

注意:
1. 文件夹的名字一定要是keytool-importkeypair2. platform.pk8和platform.x509.pem文件每个Android系统都可能不一样,请自己替换


3. JKS生成中遇到的问题

1 ./keytool-importkeypair: Permission denied
在Linux系统中生成签名文件时,可能会碰到该问题。此为文件权限不够,在keytool-importkeypair文件夹的上一层执行如下命令即可。

chmod 7777 keytool-importkeypair

2. bash: keytools: command not found
Linux系统下没有安装JDK环境。需要安装JDK并部署环境。

Linux系统下载安装JDK请参考
在过程中如果碰

JDK环境配置

// JAVA_HOME目录写对,绝对路径
export JAVA_HOME=/java/jdk1.7.0_79  
export  CLASSPATH=.:%JAVA_HOME%/lib/dt.jar:%JAVA_HOME%/lib/tools.jar  
export PATH=$PATH:$JAVA_HOME/bin

APK静默安装

我们在软件更新部分经常会自己检查服务器是否有更新的APK版本。有最新版本下载后就进行安装。

静默安装实质就是安装APK,此时不再弹出询问使用者是否安装等弹框,可直接无感Android后台运行。

所以静默安装是一种不安全的行为,使用请谨慎选择。请注意静默安装的APK可以是普通APK也可以是经系统签名文件签名的 系统APK。

/**
     * m命令可以通过adb在shell中执行,同样,我们可以通过代码来执行
     * execCommand("pm","install","-f",filePath);//安装apk,filePath为apk文件路径,如/mnt/sdcard/ApiDemos.apk
     * ("pm", "install", "-r", apkFile)
     * execCommand("pm","uninstall", packageName);//卸载apk,packageName为包名,如com.example.android.apis
     */
    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;
            errIs = process.getErrorStream();
            while ((read = errIs.read()) != -1) {
                baos.write(read);
            }
            baos.write('\n');

            inIs = process.getInputStream();
            while ((read = inIs.read()) != -1) {
                baos.write(read);
            }
            result = new String(baos.toByteArray());

        } catch (IOException e) {
            result = e.getMessage();
        } finally {
            try {
                if (inIs != null) {
                    inIs.close();
                }
                if (errIs != null) {
                    errIs.close();
                }
                if (process != null) {
                    process.destroy();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

ROOT安装法

该方法安装的的APK不一定是系统签名APK,但是要求手机必须Root过。

该方法缺点明显:

  1. 手机需要root过,可能引起其他安全隐患
  2. 如果你是本安装了APK,现在是版本更细下载后想实现静默安装,第一次安装会弹出一个该软件申请授权root权限的弹框,静默安装完全无感的效果打折扣。
    在自己的软件产品中第一次安装都需要给软件授权root权限,一但弹框中拒绝授予root权限,该弹框后面不在出现,只能通过root精灵类的软件手动设置授权。

root安装法的java调用代码如下:

/**
     * 执行具体的静默安装逻辑,需要手机ROOT,且调用该代码的app也需要授权root权限。
     * @param apkPath 要安装的apk文件的路径
     * @return 安装成功返回true,安装失败返回false。
     */
    public boolean installRoot(String apkPath) {
        boolean result = false;
        DataOutputStream dataOutputStream = null;
        BufferedReader errorStream = null;
        try {
            // 申请su权限,第一次软件调用会弹出 请求授予软件root权限的弹出框
            Process process = Runtime.getRuntime().exec("su");
            dataOutputStream = new DataOutputStream(process.getOutputStream());
            // 执行pm install命令
            String command = "pm install -r " + apkPath + "\n";
            dataOutputStream.write(command.getBytes(Charset.forName("utf-8")));
            dataOutputStream.flush();
            dataOutputStream.writeBytes("exit\n");
            dataOutputStream.flush();
            process.waitFor();
            errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            StringBuilder msg = new StringBuilder();
            String line;
            // 读取命令的执行结果
            while ((line = errorStream.readLine()) != null) {
                msg.append(line);
            }
            Log.d("TAG", "install msg is " + msg);
            // 如果执行结果中包含Failure字样就认为是安装失败,否则就认为安装成功
            if (!msg.toString().contains("Failure")) {
                result = true;
            }
        } catch (Exception e) {
            Log.e("TAG", e.getMessage(), e);
        } finally {
            try {
                if (dataOutputStream != null) {
                    dataOutputStream.close();
                }
                if (errorStream != null) {
                    errorStream.close();
                }
            } catch (IOException e) {
                Log.e("TAG", e.getMessage(), e);
            }
        }
        return result;
    }

    /**
     * 判断手机是否拥有Root权限。
     * @return 有root权限返回true,否则返回false。
     */
    public boolean isRoot() {
        boolean bool = false;
        try {
            bool = new File("/system/bin/su").exists() || new File("/system/xbin/su").exists();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bool;
    }

总结

文章主要讲解了系统app 的JKS文件生成和静默安装流程,一般此类的软件针对定制化App软件。