首先分析如何才能讓我們的應用程序才能後臺安裝APK——獲取Root權限使用命令安裝,這個問題解決了只能說實現了一般,那麼怎樣才能實現自己安裝自己,最開始的想法是在在下載升級程序包後自己調用命令安裝,但是發現一整忙碌後,這種方式根本不行,原因也很簡單,就是在執行安裝命令到一半的時候自己的APK就已經退出了,那該怎麼辦呢?
觀察發現別人的應用管理程序都是安裝其他的應用包,受到這個啓發,我就自己寫一個簡單的APK安裝程序,放在自己的資源目錄下,當自己的應用被安裝後就將這個文件釋放出來,並進行後臺安裝這個APK安裝程序,代碼如下
/**
* 釋放Server程序
*/
private void installServer() {
Intent intent = new Intent();
try {
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setComponent(new ComponentName(ConsValue.serverApkPackage, ConsValue.serverApkPackage + ".MainActivity"));
intent.putExtra("tag", 1);
startActivity(intent);
Log.e(TAG, "釋放Server程序: 啓動成功");
} catch (Exception e) {
e.printStackTrace();
//安裝Server程序
new Thread() {
@Override
public void run() {
super.run();
File file = new File(ConsValue.apkFilePath + File.separator + "server.apk");
if (!file.exists()) {
Log.e(TAG, "釋放Server程序: 安裝的Server.apk文件不存在!");
return;
}
String cmd_install = "pm install -r "; //靜默安裝命令
cmd_install = cmd_install + file.getAbsolutePath();
Log.e(TAG, "root權限,本地版本低可升級: 升級命令:" + cmd_install);
int suCMD = Utils.excuteSuCMD(cmd_install);
Log.e(TAG, "釋放Server程序: 安裝結果:suCMD=" + suCMD);
if (suCMD != -1) {
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setComponent(new ComponentName(ConsValue.serverApkPackage, ConsValue.serverApkPackage + ".MainActivity"));
intent.putExtra("tag", 1);
startActivity(intent);
mSpreferences.edit().putBoolean(ConsValue.spIsOnce, false).commit();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "釋放Server程序: 那個Server程序確實使用不了");
}
}
});
}
}
}.start();
}
}
可以執行Root命令的方法我也給出來
/**
* 執行shell命令
*/
public static int excuteSuCMD(String cmd) {
try {
Process process = Runtime.getRuntime().exec("su");
DataOutputStream dos = new DataOutputStream(
(OutputStream) process.getOutputStream());
// 部分手機Root之後Library path 丟失,導入library path可解決該問題
dos.writeBytes((String) "export LD_LIBRARY_PATH=/vendor/lib:/system/lib\n");
cmd = String.valueOf(cmd);
dos.writeBytes((String) (cmd + "\n"));
dos.flush();
dos.writeBytes("exit\n");
dos.flush();
process.waitFor();
int result = process.exitValue();
return (Integer) result;
} catch (Exception localException) {
localException.printStackTrace();
return -1;
}
}
安裝好以後,爲了不給用戶發現我們其實安裝了兩個APK程序,我們需要將那個APK管理程序的桌面圖標進行隱藏,網上給出了三種
第一種:在代碼中實現,可以隱藏,但是隱藏後的程序,就無法被啓動起來了,故此處不行!
PackageManager p = getPackageManager();
p.setComponentEnabledSetting(getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
第二種:更改AndroidManifest文件中的啓動Activity的intent-filter,要麼將註釋掉,要麼替換爲,但是都是無法允許我們的程序的
第三種:增加AndroidManifest文件中的啓動Activity的intent-filter,
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<data
android:host="AuthActivity"
android:scheme="com.android.example" />
</intent-filter>
</activity>
這個MainActivity可以響應Uri爲com.android.example://AuthActivity的特定 Intent,但是爲什麼加入這個之後app就不顯示圖標了呢?因爲我們把app的入口Activity申明爲由接收隱士的Intent來啓動,這樣自然也就不會顯示圖標了。要啓動就需要在外部來啓動了,恰好符合我們的需求,
APK安裝程序的完整的代碼如下:
public class MainActivity extends AppCompatActivity {
public static final int start = 1;
public static final int installDef = 2; //安裝默認路徑下的apk文件
public static final int installPath = 3; //安裝指定路徑下的apk文件
public static final String DefFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/APK/Client.apk";
String cmd_install = "pm install -r "; //靜默安裝命令
final String serverApkPackage = "com.org.ccbygqd";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
boolean result = false;
if (intent != null) {
int tag = intent.getIntExtra("tag", -1);
switch (tag) {
case start:
break;
case installDef:
result = excuteSuCMD(cmd_install + DefFile);
if (result) {
Toast.makeText(this, "程序安裝成功", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "程序安裝失敗", Toast.LENGTH_LONG).show();
}
break;
case installPath:
String path = intent.getStringExtra("path");
result = excuteSuCMD(cmd_install + path);
if (result) {
Toast.makeText(this, "程序安裝成功", Toast.LENGTH_LONG).show();
Intent intentOpen = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intentOpen.setComponent(new ComponentName(serverApkPackage, serverApkPackage + ".MainActivity"));
startActivity(intentOpen);
} else {
Toast.makeText(this, "程序安裝失敗", Toast.LENGTH_LONG).show();
}
break;
default:
Toast.makeText(this, "安裝服務程序打開成功!", Toast.LENGTH_LONG).show();
break;
}
}
finish();
}
}
我們自己的程序當下載後的安裝代碼如下:
/**
* 需要root權限的版本升級,會比較包名的信息
*
* @param data
*/
private void rootUpgrade(String data) {
datas = Utils.splitCMD(cmdStr);
String cmd = datas[1];
byte[] cmdBytes = cmd.getBytes();
if (cmdBytes[0] == 0X02) { // 同步文件開始
Log.e(TAG, "硬件版本升級(APK版本升級): 開始");
Message msg = handler.obtainMessage();
msg.what = 888;
msg.arg2 = HIDFlag; //表示是HID的通信
msg.sendToTarget();
} else if (cmdBytes[0] == 0X03) { // 同步文件結束
Log.e(TAG, "硬件版本升級(APK版本升級): 結束");
Message msg = handler.obtainMessage();
msg.what = 777;
msg.arg2 = HIDFlag; //表示是HID的通信
msg.sendToTarget();
String serverName = datas[2];
//獲取到本地的版本信息
PackageManager pm = context.getPackageManager();
PackageInfo pi = null;
try {
pi = pm.getPackageInfo(context.getPackageName(), 0);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
String localName = pi.versionName;
String[] localSplit = localName.split("\\.");
String serverCode = serverName.substring(0, serverName.length() - 4);//將 字符串“.apk”去掉
//獲取到服務器的版本信息
String[] serverSplit = serverCode.split("_");
if (serverSplit == null || serverSplit.length < 2) {
UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.UPDHHQ0002));
isLooping = true;
return;
}
File file = new File(ConsValue.apkFilePath + File.separator + serverName);
if (file.exists() && file.isFile()) {
int type = 0; //參數表示:0:版本相同無法升級,1:本地版本低可升級,-1:本地版本高無法升級
try {
//比較版本信息
int len = (localSplit.length < serverSplit.length) ? localSplit.length : serverSplit.length;
for (int i = 0; i < len; i++) {
int local = Integer.parseInt(localSplit[i]);
int server = Integer.parseInt(serverSplit[i]);
if (local > server) {
type = -1;
break;
} else if (local == server) {
type = 0;
} else {
type = 1;
break;
}
Log.e(TAG, "需要root權限的版本升級: 第" + i + "次比較,local=" + local + " ,server=" + server + " ,type=" + type);
}
if (type == 0 && (localSplit.length < serverSplit.length)) {
type = 1;
} else if (type == 0 && (localSplit.length > serverSplit.length)) {
type = -1;
}
//判斷是否可以升級
switch (type) {
case -1: //本地版本高無法升級
UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.UPDHHQ0003));
isLooping = true;
return;
case 0:// 版本相同無法升級
UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.UPDHHQ0004));
isLooping = true;
return;
case 1: // 本地版本低可升級
UsbHid.WriteCmd(Utils.resToChars(true, data, true, ErrorDictionary.UPDHHQ0000));
HIDThreadFlag = false;
isLooping = true;
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setComponent(new ComponentName(ConsValue.serverApkPackage, ConsValue.serverApkPackage + ".MainActivity"));
intent.putExtra("tag", 3);
intent.putExtra("path", file.getAbsolutePath());
context.startActivity(intent);
Log.e(TAG, "需要root權限的版本升級: 開啓Install程序安裝升級!");
break;
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "版本升級: 升級錯誤" + e.toString());
UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.UPDHHQ0001));
}
} else {
UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.TLCHHQ000E));
}
}
isLooping = true;
}