Android靜默安裝及開機自啟的簡單實現


寫在前面


【apk靜默安裝】是android應用的一個重要功能,一般用在app自動更新等方面。靜默安裝在android里面是指不需要界面或用戶參與的app安裝動作,且需要系統已獲root權限。安裝完成后一般通過接收廣播的方式啟動App服務。
【app自啟動】是常用功能,一般通過接收系統啟動廣播實現。

正文


1、被執行安裝 app:##

a. 自定義權限

<permission
        android:name="app.permission.install"
        android:protectionLevel="normal">
</permission>

其中android:name是自定義權限名稱,android:protectionLevel是表示權限等級。
自定義權限android:protectionLevel有四個設置選項分別是:normal、dangerous、signature、signatureOrSystem,依次指明默認的低風險權限等級、涉及用戶私有數據或系統級別組件的高風險權限等級、統一簽名權限等級、系統級簽名權限等級。此處用默認權限。
b. service標簽加入權限限制(執行端必須擁有該權限才能訪問)

<service
	        android:name=".×××Service"
	        android:enabled="true"
	        android:exported="true"
	        android:permission="app.permission.install">
    <intent-filter>
        	<action android:name="×××.×××"/>
    </intent-filter>
</service>

2、執行靜默安裝 app:##

a. 加入被安裝app的自定義權限(獲得訪問權限)###

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

b. 創建自動啟動的廣播接收器###

<receiver android:name=".Broadcast.SilenceInstallReceiver"
	        android:enabled="true"
	        android:exported="true">
    <intent-filter>
	        <action android:name="android.intent.action.PACKAGE_ADDED"/>
	        <data android:scheme="package"/>
    </intent-filter>
</receiver>

廣播接收器:

public class SilenceInstallReceiver2 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 接收安裝廣播
        if (intent.getAction().equals("android.intent.action.PACKAGE_ADDED")) {
            startApp(context);
        }
    }

    /**
     * 啟動app
     */
    public void startApp(Context context) {
        /**
         * 啟動service
         */
        Intent intent1 = new Intent();
        intent1.setPackage("×××.×××"); //設置被執行啟動操作app包名
        intent1.setAction("×××.×××"); //設置被執行啟動操作app中service的自定義intent
        context.startService(intent1);

        /**
         * 啟動activity
         */
        Intent intent2= new Intent();
        ComponentName componentName = new ComponentName(
                "×××.×××",  //被執行啟動操作app的包名
                "×××.×××");   //被執行啟動操作app的activity包路徑
        intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//
        intent2.setComponent(componentName);
        context.startActivity(intent2);
    }
}

c.拷貝需安裝的apk文件到本地

項目中apk存儲位置:

具體代碼:

/**
 * 將assets下的需要安裝的apk文件復制到本地可讀寫目錄
 */
public List<String> extractApkToLocal() {
    List<String> apkListLocal = new ArrayList<>();
    InputStream is = null;
    FileOutputStream fos = null;
    String[] files;
    String assetsDir = "apks";
    String localPath;
    try {
        File file = new File(apkPath);
        FileUtils.createOrExistsDir(file);
        files = mContext.getAssets().list(assetsDir);
        for (String f : files) {
            is = getAssets().open(assetsDir + "/" + f);
            localPath = apkPath + "/" + f;
            apkListLocal.add(localPath);
            fos = new FileOutputStream(new File(localPath));
            byte[] temp = new byte[1024];
            int i = 0;
            while ((i = is.read(temp)) > 0) {
                fos.write(temp, 0, i);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return apkListLocal;
}

d.安裝apk

具體代碼:

public boolean slientInstall() {
    List<String> apkList; 
    apkList = extractApkToLocal(); //獲取apk本地路徑
    boolean result = false;
    for (String path : apkList) {
        File file = new File(path);
        Process process = null;
        OutputStream out = null;
        if (file.exists()) {
            try {
                process = Runtime.getRuntime().exec("su");
                out = process.getOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(out);

                // 更改本地apk文件權限,方便執行安裝操作
                dataOutputStream.writeBytes("chmod 777 " + file.getPath()
                        + "\n");
                     
                // 進行安裝
                dataOutputStream.writeBytes("LD_LIBRARY_PATH=/vendor/lib:/system/lib pm install -r "
                        + file.getPath());
                dataOutputStream.flush();
                dataOutputStream.close();
                out.close();
                int value = process.waitFor();

                // 成功
                if (value == 0) {
                    result = true;
                    // 失敗
                } else if (value == 1) {
                    result = false;
                    // 未知情況
                } else {
                    result = false;
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!result) {
                result = true;
            }
        }
    return result;
}

3、app自啟動:##

a. 添加接收【系統啟動完成】廣播的權限###

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

b. 創建接收器###

<receiver
		android:name=".Broadcast.BootReceiver"
		android:enabled="true">
    <intent-filter>
        <!-- 這是開機啟動發送的廣播意圖-->
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

廣播接收器:

public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("BootReceiver:", "收到開機廣播");
        //開啟服務
        Intent startVoiceService = new Intent(context, ×××.class);
        context.startService(startVoiceService);
    }
}

小結


靜默安裝有它的局限性,需要通過系統的超級用戶(root)進行操作任務,所以對系統安全性不高的情況有較強的實用性。此文是在查詢各方面資料基礎上實現的,旨在拋磚引玉,希望集思廣益后有更好的實現方案!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM