寫在前面
【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)進行操作任務,所以對系統安全性不高的情況有較強的實用性。此文是在查詢各方面資料基礎上實現的,旨在拋磚引玉,希望集思廣益后有更好的實現方案!