首先分析如何才能让我们的应用程序才能后台安装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;
}