静默安装、自启动实践

最近在做门禁机内嵌app,门禁机基于api22原生系统,涉及到的技术点包括静默安装、自启动。翻阅大量源码、blog以后得到解决方案,因此记录下来。

静默安装

    静默安装,即不弹窗系统安装界面的情况下,完成安装动作,在用户不知情的情况下完成安装动作显然是非常危险的行为
    ,因此静默安装不会开发给开发者,不过在Google Play商店已经自定义Rom上都有开发此权限,充分说明拥有权限的重要
    性,自家的系统想咋样就咋样。

权限

    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />

安装代码

有用push到system/app去调用@hide实现的,有用doc命令实现的,对项目需求而言二者皆可,考虑到项目包含过多.so文件,而.so文件需要push到/system/lib赋予权限,故直接采用了第二种策略使用doc命令搞定,即(在子线程安装,避免ANR,需要root权限):

        pm install -r

实现代码

/**
* install slient
* 
* @param context
* @param filePath
* @return 0 means normal, 1 means file not exist, 2 means other exception error
*/
public static int installSlient(Context context, String filePath) {
    File file = new File(filePath);
    if (filePath == null || filePath.length() == 0 || (file = new File(filePath)) == null || file.length() <= 0
    || !file.exists() || !file.isFile()) {
        return 1;
    }

    String[] args = { "pm", "install", "-r", filePath };
    ProcessBuilder processBuilder = new ProcessBuilder(args);

    Process process = null;
    BufferedReader successResult = null;
    BufferedReader errorResult = null;
    StringBuilder successMsg = new StringBuilder();
    StringBuilder errorMsg = new StringBuilder();
    int result;
    try {
        process = processBuilder.start();
        successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
        errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        String s;

        while ((s = successResult.readLine()) != null) {
        successMsg.append(s);
        }

        while ((s = errorResult.readLine()) != null) {
        errorMsg.append(s);
        }
    } catch (IOException e) {
        e.printStackTrace();
        result = 2;
    } catch (Exception e) {
        e.printStackTrace();
        result = 2;
    } finally {
        try {
        if (successResult != null) {
            successResult.close();
            }
            if (errorResult != null) {
            errorResult.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (process != null) {
        process.destroy();
        }
    }

    // TODO should add memory is not enough here
    if (successMsg.toString().contains("Success") || successMsg.toString().contains("success")) {
        result = 0;
    } else {
        result = 2;
    }
    Log.d("installSlient", "successMsg:" + successMsg + ", ErrorMsg:" + errorMsg);
    return result;
}

自启动

无非就是静态广播拉起laucherActivity,不过这里需要注意的问题是,但静默安装成功后,app进程会被杀死,这个过程中自身无法接收到广播,这时候就需要辅助app了,这个app用来处理接收android.intent.action.PACKAGE_ADDED广播,并发送广播给宿主app从而实现自启动,这个过程代码是很简单的。
如果想要优化的话可以去做定时轮询相互检查进程是否存活,并相互拉起。如何定时轮询,如何检查,整个策略,会在后续中编写…

参考:
Trinea-Android常用代码之普通及系统权限静默安装APK
pm.java

Task :app:compileDebugJavaWithJavac Failed处理

今天遇到了Task :app:compileDebugJavaWithJavac FAILED问题,网上答案很杂,解决以后总结一下。

该问题基本由于gradle编译导致,可以用

    $ gradlew compileDebug --stacktrace
    

来跟踪gradle编译时的stacktrace信息,如果出现-bash: ./gradlew: Permission denied错误,可以输入

    $ chmod +x gradlew

来解决权限问题,不过gradlew comileDebug 并不能看到有价值的信息,根据提示,输入

    $ gradlew compileDebugSources --stacktrace -info

解决问题,原来是某个.java文件没push导致。