手机一定要root ,没有root 实现不了,安装其他apk是参考网上的资料的,安装自身apk原理上差不多。不管哪样,安装完后要重启手机才能看到。这也是一个没解决的问题。
安装其他apk:tom.apk
要安装的tom.apk要事先存储在手机存储的根目录下。通过在代码中执行linux命令来将tom.apk安装到system/app目录下。其实实际上是把这个tom.apk文件拷贝到了system/app目录下。所以安装完成后要重启手机才能看到tom.apk的图标。
这一部分参考:http://bbs.youkuaiyun.com/topics/390171063
具体的解释都在代码里了,直接上代码:
安装apk自身:
将apk自身安装到系统system/app目录下。手机需要root
原理:和将其他apk安装到系统目录下是一样的,首先本项目是一个扮演Installer身份的apk.
需要事先创建真正要安装的apk(比如installmyself2systemappentity.apk),将这个apk放在本项目的res/raw目录下。
经测试以上均成功,但是出现新问题:就是这个installer也会存在桌面上,
所以最后还得调用系统程序,将这个installer卸载。
这是基于我的水平能想到的方法了,可能够笨的,但是要求的功能还是实现了,大家有什么好的想法可以交流一下。
RootCmd.java
/**
* 执行安装到system/app功能的主要类
*/
package com.example.installmyself2systemapp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public final class RootCmd {
// 执行linux命令并且输出结果
protected static String execRootCmd(String paramString) {
String result = "result : ";
try {
Process localProcess = Runtime.getRuntime().exec("su ");// 经过Root处理的android系统即有su命令
OutputStream localOutputStream = localProcess.getOutputStream();
DataOutputStream localDataOutputStream = new DataOutputStream(
localOutputStream);
InputStream localInputStream = localProcess.getInputStream();
DataInputStream localDataInputStream = new DataInputStream(
localInputStream);
String str1 = String.valueOf(paramString);
String str2 = str1 + "\n";
localDataOutputStream.writeBytes(str2);
localDataOutputStream.flush();
String str3 = null;
// while ((str3 = localDataInputStream.readLine()) != null) {
// Log.d("result", str3);
// }
localDataOutputStream.writeBytes("exit\n");
localDataOutputStream.flush();
localProcess.waitFor();
return result;
} catch (Exception localException) {
localException.printStackTrace();
return result;
}
}
// 执行linux命令但不关注结果输出
protected static int execRootCmdSilent(String paramString) {
try {
Process localProcess = Runtime.getRuntime().exec("su");
Object localObject = localProcess.getOutputStream();
DataOutputStream localDataOutputStream = new DataOutputStream(
(OutputStream) localObject);
String str = String.valueOf(paramString);
localObject = str + "\n";
localDataOutputStream.writeBytes((String) localObject);
localDataOutputStream.flush();
localDataOutputStream.writeBytes("exit\n");
localDataOutputStream.flush();
localProcess.waitFor();
int result = localProcess.exitValue();
return (Integer) result;
} catch (Exception localException) {
localException.printStackTrace();
return -1;
}
}
// 判断机器Android是否已经root,即是否获取root权限
protected static boolean haveRoot() {
int i = execRootCmdSilent("echo test"); // 通过执行测试命令来检测
if (i != -1) {
return true;
}
return false;
}
}
以上这个类的代码是参考网上资料的。
MainActivity.java
mainactivity.java的流程是安装apk自身的流程,但是其中包含了安装其他apk的方法,只要提取出来beginInstall()这个方法单独用就行了
/**
* 本项目实现了将apk自身安装到系统system/app目录下。手机需要root
* 原理:和将其他apk安装到系统目录下是一样的,首先本项目是一个扮演Installer身份的apk.
* 需要事先创建真正要安装的apk,将这个apk放在本项目的res/raw目录下。
* 然后启动本apk,首先将真正的apk复制到手机存储上,然后就和安装其他apk步骤一样了。
*
* 经测试以上均成功,但是出现新问题:就是这个installer也会存在桌面上,
* 所以最后还得调用系统程序,将这个installer卸载。
*
*/
package com.example.installmyself2systemapp;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.view.Menu;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 如果复制成功,则开始安装
boolean copyOK = exportAPK2Phone();
if (copyOK) {
beginInstall();
}
uninstall("com.example.installmyself2systemapp",this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
// 将res/raw目录下的文件拷贝出来
private boolean exportAPK2Phone() {
String filePath = Environment.getExternalStorageDirectory()
+ "/installmyself2systemappentity.apk";// 文件路径
File file = null;
try {
// 目录存在,则将apk中raw中的需要的文档复制到该目录下
file = new File(filePath);
if (!file.exists()) {// 文件不存在
System.out.println("要打开的文件不存在");
InputStream ins = getResources().openRawResource(
R.raw.installmyself2systemappentity);// 通过raw得到数据资源
System.out.println("开始读入");
FileOutputStream fos = new FileOutputStream(file);
System.out.println("开始写出");
byte[] buffer = new byte[8192];
int count = 0;// 循环写出
while ((count = ins.read(buffer)) > 0) {
fos.write(buffer, 0, count);
}
System.out.println("已经创建该文件");
fos.close();// 关闭流
ins.close();
}
} catch (Exception e) {
e.printStackTrace();
}
if (file != null && file.exists()) {
return true;
}
return false;
}
// 开始安装手机存储上的apk
public void beginInstall() {
String apkName = "installmyself2systemappentity.apk";
// 组装要执行的linux命令
String paramString = "adb push " + apkName + " /system/app"
+ "\n" // 上传要安装的文件,为安装做准备
+ "adb shell" + "\n" + "su"
+ "\n" // 切换到root用户,手机必须已经root
+ "mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system"
+ "\n" // 让分区可写
+ "cat /sdcard/" + apkName + " > /system/app/" + apkName + "\n" // 相当于赋值的过程
+ "chmod 644 /system/app/" + apkName + "\n" // 更改复制后的文件的权限
// 刚开始是rw-------,更改为rw-r--r--
+ "mount -o remount,ro -t yaffs2 /dev/block/mtdblock3 /system" // 还原属性,只读
+ "\n" + "exit" + "\n" + "exit";
if (RootCmd.haveRoot()) { // 如果已经root
// 执行安装并返回结果
if (RootCmd.execRootCmdSilent(paramString) == -1) {
Toast.makeText(this, "安装不成功", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "安装成功", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(this, "没有root权限", Toast.LENGTH_LONG).show();
}
//以上就是单纯安装其他apk的方法
clearEntityFile();
}
// 安装完成后为了掩人耳目,将复制出来的apk删除。
public void clearEntityFile() {
String filePath = Environment.getExternalStorageDirectory()
+ "/installmyself2systemappentity.apk";// 文件路径
File f = new File(filePath);
if (f.exists()) {
f.delete();
}
}
//卸载Installer自身:是否是静默卸载?
public static boolean uninstall(String packageName,Context context){
if(RootCmd.haveRoot()){
// 有root权限,利用静默卸载实现
return clientUninstall(packageName);
}else{
Uri packageURI = Uri.parse("package:" + packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE,packageURI);
uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(uninstallIntent);
return true;
}
}
/**
* 静默卸载
*/
private static boolean clientUninstall(String packageName){
PrintWriter PrintWriter = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("su");
PrintWriter = new PrintWriter(process.getOutputStream());
PrintWriter.println("LD_LIBRARY_PATH=/vendor/lib:/system/lib ");
PrintWriter.println("pm uninstall "+packageName);
PrintWriter.flush();
PrintWriter.close();
int value = process.waitFor();
// 代表成功
if (value == 0) {
return true;
} else if (value == 1) { // 失败
return false;
} else { // 未知情况
return false;
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(process!=null){
process.destroy();
}
}
return false;
}
}
AndroidManifest.xml 记得加权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.installmyself2systemapp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.installmyself2systemapp.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
目录结构:再次注意,当安装apk自身时,res/raw目录下的installmyself2systemappentity.apk才是真正要安装的apk.