Android VNC Server New
关于VNC请参见维基百科:
关于执行Android VNC Server,请参见前一篇文章:
一、 VNC 下载
1 )fastdroid-vnc
Android VNC Server开源项目
2 )TightVNC
免费的VNC软件(这个都不需要填邮箱^^)
二、程序执行
这种方式看手机了==。Google HTC上倒是可以,详细的说明看注释了。
1 )简易UI
布局一点没变动…
VNCServerNewActivity.java
- public class VNCServerNewActivity extends Activity {
- private static final String TAG = "VNCServer";
- private static final boolean LOGD = true;
- /** assets目录下的VNCServer文件名 */
- private static final String VNC_SERV_NAME = "fastdroid-vnc";
- /** Shell工具类 */
- private ShellUtil mShellUtil;
- /** dialog基础标识值 */
- private static final int DLG_BASE = 0;
- /** 获root权限失败的dialog id */
- private static final int DLG_ROOT_FAILED = DLG_BASE + 1;
- /** 开启VNC服务失败的dialog id */
- private static final int DLG_EXEC_FAILED = DLG_BASE + 2;
- /** VNC服务端口:5901 */
- private static final String VNC_SERV_PORT = "5901";
- /** 按钮 */
- private Button startBtn, stopBtn;
- /** 标签 */
- private TextView statusView, connectView;
- /** 'ps -l VNC_SERV_NAME'命令 */
- private static final String[] PS_VNC_SERVER = new String[] { "ps", "-l",
- VNC_SERV_NAME };
- /** 执行上述命令获取的pid结果集 */
- private ArrayList<String> pidList;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mShellUtil = ShellUtil.getInstance(); // 获取实例
- initApp(); // 初始化应用
- initViews(); // 初始化组件
- }
- /** 初始化组件 */
- private void initViews() {
- startBtn = (Button) findViewById(R.id.startBtn);
- stopBtn = (Button) findViewById(R.id.stopBtn);
- statusView = (TextView) findViewById(R.id.statusView);
- connectView = (TextView) findViewById(R.id.connectView);
- updateViews(isServerOn()); // 由服务状态更新界面
- }
- /** 初始化应用 */
- private void initApp() {
- boolean result = mShellUtil.root(); // 获取ROOT权限
- if (LOGD)
- Log.d(TAG, "获取Root权限:" + result);
- if (result) {
- copyVNCServer(); // 检查VNCServer文件
- } else {
- showDialog(DLG_ROOT_FAILED); // 提示DLG_ROOT_FAILED对话框
- }
- }
- /** 检查VNCServer文件,不存在时复制进去 */
- private void copyVNCServer() {
- String filePath = "/data/local/" + VNC_SERV_NAME;
- File file = new File(filePath);
- /* 文件不存在时,从assets复制进去 */
- if (!file.exists()) {
- /* /data/local/目录增加所有用户的写权限 */
- boolean result = mShellUtil.rootCommand("chmod a+x /data/local/");
- if (LOGD)
- Log.d(TAG, "/data/local/增加写权限:" + result);
- // 避免某些机子new FileOutputStream(file)时报System.err==
- result = mShellUtil.rootCommand("touch " + filePath);
- if (LOGD)
- Log.d(TAG, "创建一个空文件:" + result);
- /* VNCServer文件设为777权限 */
- result = mShellUtil.rootCommand("chmod 777 " + filePath);
- if (LOGD)
- Log.d(TAG, "/data/local/设为777权限:" + result);
- if (result) {
- try {
- /* 将VNCServer文件复制入/data/local/ */
- InputStream is = getAssets().open(VNC_SERV_NAME);
- FileOutputStream fos = new FileOutputStream(file);
- byte[] buffer = new byte[4096];
- int count = 0;
- while ((count = is.read(buffer)) > 0) {
- fos.write(buffer, 0, count);
- }
- fos.close();
- is.close();
- if (LOGD)
- Log.d(TAG, VNC_SERV_NAME + "文件写入/data/local/!");
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- } else {
- if (LOGD)
- Log.d(TAG, VNC_SERV_NAME + "文件已存在/data/目录下!");
- }
- }
- /** startBtn点击事件 */
- public void startBtn(View v) {
- /* 执行VNCServer文件 */
- String cmd = "/data/local/" + VNC_SERV_NAME + " &";
- boolean result = mShellUtil.rootCommand(cmd);
- if (LOGD)
- Log.d(TAG, cmd + ":" + result);
- if (isServerOn()) { // 否开启了服务
- updateViews(true);
- } else {
- /**
- * 1)有些ROM执行开启不了T^T(ps进程显示Done,再一会就没了)
- * 2)用其他VNC文件发现未开启也好提示用户
- */
- showDialog(DLG_EXEC_FAILED); // 提示DLG_EXEC_FAILED对话框
- }
- }
- /** stopBtn点击事件 */
- public void stopBtn(View v) {
- boolean result;
- /** 直到关闭为止。避免kill进程仅改变了状态(遇到S->Z的情况) */
- while (isServerOn()) {
- for (String pid : pidList) {
- result = mShellUtil.rootCommand("kill " + pid);
- if (LOGD)
- Log.d(TAG, "kill " + pid + ":" + result);
- }
- }
- updateViews(false); // 更新服务关闭时的界面状态
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- boolean result = mShellUtil.rootRelease(); // 释放占用资源
- if (LOGD)
- Log.d(TAG, "释放占用资源:" + result);
- }
- /** 由服务状态更新界面 */
- private void updateViews(boolean isServerOn) {
- /* 更新按钮状态 */
- startBtn.setEnabled(!isServerOn);
- stopBtn.setEnabled(isServerOn);
- /* 更新标签显示 */
- if (isServerOn) {
- statusView.setText(R.string.status_run);
- connectView.setText(getLocalIpAddress() + ":" + VNC_SERV_PORT);
- } else {
- statusView.setText(R.string.status_stop);
- connectView.setText(R.string.address);
- }
- }
- /** 是否开启了服务,并处理得到pid列表 */
- private boolean isServerOn() {
- mShellUtil.setFilter(new PsLineFilter()); // 设置过滤器
- // 获取ps命令的pid列表
- pidList = mShellUtil.execCommand(PS_VNC_SERVER, null, true);
- mShellUtil.resetFilter(); // 重置过滤器
- boolean result = (null != pidList) && (pidList.size() >= 1);
- if (LOGD)
- Log.d(TAG, "VNC服务开启状态:" + result);
- return result;
- }
- /** 获取IP地址 */
- private String getLocalIpAddress() {
- try {
- // 遍历网络接口
- for (Enumeration<NetworkInterface> en = NetworkInterface
- .getNetworkInterfaces(); en.hasMoreElements();) {
- NetworkInterface intf = en.nextElement();
- // 遍历IP地址
- for (Enumeration<InetAddress> enumIpAddr = intf
- .getInetAddresses(); enumIpAddr.hasMoreElements();) {
- InetAddress inetAddress = enumIpAddr.nextElement();
- // 非回传地址时返回
- if (!inetAddress.isLoopbackAddress()) {
- return inetAddress.getHostAddress().toString();
- }
- }
- }
- } catch (SocketException e) {
- e.printStackTrace();
- }
- return null;
- }
- @Override
- protected Dialog onCreateDialog(int id) {
- 省略……
- }
- }
2 )Shell 工具类
ShellUtil.java
- /** Shell工具类 */
- public final class ShellUtil {
- /** 内部类ShellUtilHolder */
- static class ShellUtilHolder {
- static ShellUtil instance = new ShellUtil();
- }
- /** 返回ShellUtil的单例 */
- public static ShellUtil getInstance() {
- return ShellUtilHolder.instance;
- }
- /** \link #root()\endlink后的进程 */
- private Process process;
- /** \link #root()\endlink后的父进程的标准输入 */
- private DataOutputStream dos;
- /** 标准输出的过滤 */
- private IStdoutFilter<String> mIStdoutFilter;
- /** 设置标准输出的过滤器 */
- public void setFilter(IStdoutFilter<String> filter) {
- this.mIStdoutFilter = filter;
- }
- /** 重置过滤器为空 */
- public void resetFilter() {
- this.mIStdoutFilter = null;
- }
- /**
- * @brief 切换至ROOT用户
- * @details 执行su命令,变更为root用户
- * @pre 设备已经破解,否则su不可用
- *
- * @return 是否成功
- */
- public boolean root() {
- try {
- // 执行su变更用户身份为root
- process = Runtime.getRuntime().exec("su");
- // 转成DataOutputStream方便写入字符串
- dos = new DataOutputStream(process.getOutputStream());
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- return true;
- }
- /**
- * @brief ROOT权限下执行命令
- * @pre 执行\link #root()\endlink
- *
- * @param cmd 命令
- */
- public boolean rootCommand(String cmd) {
- if (null != dos) {
- try {
- dos.writeBytes(cmd + "\n");
- dos.flush();
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- return true;
- }
- return false;
- }
- // /**
- // * @brief \link #rootCommand()\endlink后的结果
- // * @pre 执行\link #rootCommand()\endlink
- // *
- // * @warning 不能在stdin流输入命令后再从stdout获输出结果
- // * (之前测试版也放在不同位置试过,都不成,死锁?没找到更多资料)
- // *
- // * @return 输出结果的集合
- // */
- // public ArrayList<String> getStdout() {
- // ArrayList<String> lineArray = new ArrayList<String>();
- // try {
- // handleStdout(lineArray, process);
- // } catch (IOException e) {
- // e.printStackTrace();
- // }
- // return lineArray;
- // }
- /** 释放占用资源 */
- public boolean rootRelease() {
- try {
- dos.writeBytes("exit\n");
- dos.flush();
- process.waitFor(); // 等待执行完成
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- } finally {
- try {
- if (null != process) {
- process.destroy();
- }
- if (null != dos) {
- dos.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return true;
- }
- /**
- * @brief 执行一个shell命令
- *
- * @param cmd 命令&参数组成的数组
- * @param workDir 命令工作目录
- * @param isStdout 是否输出结果
- * @return 输出结果的集合
- */
- public ArrayList<String> execCommand(String[] cmd, String workDir,
- boolean isStdout) {
- ArrayList<String> lineArray = null;
- try {
- // 创建操作系统进程(也可以由Runtime.exec()启动)
- ProcessBuilder builder = new ProcessBuilder(cmd);
- // 设置命令工作目录
- if (workDir != null) {
- builder.directory(new File(workDir));
- }
- // 合并标准错误和标准输出
- builder.redirectErrorStream(true);
- // 启动一个新进程
- Process process = builder.start();
- // 如果输出结果的话
- if (isStdout) {
- lineArray = new ArrayList<String>(); // 创建对象
- handleStdout(lineArray, process);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return lineArray;
- }
- /**
- * 处理标准输出内容
- *
- * @throws IOException
- */
- private void handleStdout(ArrayList<String> lineArray, Process process)
- throws IOException {
- InputStream is = process.getInputStream(); // 获得标准输出流
- if (null != mIStdoutFilter) { // 如果设置了过滤
- // 判断是否是行过滤器
- if (mIStdoutFilter instanceof AbstractLineFilter) {
- // 转成BufferedReader
- BufferedReader br = new BufferedReader(
- new InputStreamReader(is));
- String line;
- while (null != (line = br.readLine())) {
- /* 如果未被过滤,则将处理后内容加入List */
- if (!mIStdoutFilter.filter(line)) {
- lineArray.add(mIStdoutFilter.handle());
- }
- }
- if (br != null) {
- br.close();
- }
- } else {
- // 默认把流直接转成字符串返回
- lineArray.add(inputStream2Str(is));
- }
- } else {
- // 默认把流直接转成字符串返回
- lineArray.add(inputStream2Str(is));
- }
- if (is != null) {
- is.close();
- }
- }
- /**
- * 输入流转成字符串
- *
- * @throws IOException
- */
- public String inputStream2Str(InputStream is) throws IOException {
- StringBuffer out = new StringBuffer();
- byte[] b = new byte[4096];
- for (int n; (n = is.read(b)) != -1;) {
- out.append(new String(b, 0, n));
- }
- return out.toString();
- }
- }
3 )过滤器
IStdoutFilter.java
- /** 标准输出过滤接口 */
- public interface IStdoutFilter<T> {
- /**
- * @brief 过滤操作
- * @param stdout
- * 标准输出的内容
- * @return true:过滤;false:保留
- */
- boolean filter(T stdout);
- /**
- * @brief 处理操作
- * @return 处理后的内容
- */
- T handle();
- }
AbstractLineFilter.java
- /**
- * @brief 抽象的行过滤器
- * @details 以行的方式遍历标准输出,都进行一次过滤判断
- */
- public abstract class AbstractLineFilter implements IStdoutFilter<String> {
- /** 行内容 */
- protected String line;
- /**
- * @brief 行过滤操作
- * @param line
- * 标准输出的某行内容
- * @return true:过滤;false:保留
- */
- protected abstract boolean lineFilter(String line);
- @Override
- public boolean filter(String stdout) {
- this.line = stdout;
- return lineFilter(stdout);
- }
- @Override
- public String handle() {
- return line; // 默认返回原行
- }
- }
PsLineFilter.java(应该加个单例==)
- /**
- * ps命令的行过滤及处理成pid的实现
- */
- public final class PsLineFilter extends AbstractLineFilter {
- @Override
- protected boolean lineFilter(String line) {
- // 过滤空行及标题行
- if (null == line || "".endsWith(line) || line.startsWith("USER")) {
- return true;
- }
- return false;
- }
- @Override
- public String handle() {
- try {
- return line.trim().split("\\s+")[1]; // 获取PID列
- } catch (Exception e) { // null和越界异常
- return line;
- }
- }
- }
三、后记
这个东西貌似还得动手改源码么T^T。小弟告罄了,不想碰那个东西==。
附件工程,随便看看了……
ps:Doxygen简单生成了个chm帮助文件,在docs目录下。
我是不是很无聊了-_-!