Android VNC Server New
2012-03-23 20:12
176 查看
Android VNC Server New
关于VNC请参见维基百科:http://zh.wikipedia.org/wiki/VNC
关于执行Android VNC Server,请参见前一篇文章:点击链接
一、VNC下载
1)fastdroid-vnc
Android VNC Server开源项目
http://code.google.com/p/fastdroid-vnc/
2)TightVNC
免费的VNC软件(这个都不需要填邮箱^^)
http://www.tightvnc.com/download.php
二、程序执行
这种方式看手机了==。Google HTC上倒是可以,详细的说明看注释了。
1)简易UI
布局一点没变动…
VNCServerNewActivity.java
2)Shell工具类
ShellUtil.java
3)过滤器
IStdoutFilter.java
AbstractLineFilter.java
PsLineFilter.java(应该加个单例==)
三、后记
这个东西貌似还得动手改源码么T^T。小弟告罄了,不想碰那个东西==。
附件工程,随便看看了……
ps:Doxygen简单生成了个chm帮助文件,在docs目录下。
我是不是很无聊了-_-!
附件:http://down.51cto.com/data/2360120
关于VNC请参见维基百科:http://zh.wikipedia.org/wiki/VNC
关于执行Android VNC Server,请参见前一篇文章:点击链接
一、VNC下载
1)fastdroid-vnc
Android VNC Server开源项目
http://code.google.com/p/fastdroid-vnc/
2)TightVNC
免费的VNC软件(这个都不需要填邮箱^^)
http://www.tightvnc.com/download.php
二、程序执行
这种方式看手机了==。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目录下。
我是不是很无聊了-_-!
附件:http://down.51cto.com/data/2360120
相关文章推荐
- 《Appium学习中遇到的问题》之“运行脚本时driver = new AndroidDriver(new URL(serverURL), capabilities)报错”
- Android VNC Server
- Run a VNC server on Android device
- android虚拟机androidvncserver+电脑VNCView 远程监控
- Android VNC Server
- Android下通过root实现对system_server中binder的ioctl调用拦截
- 基于Linux搭建一个类似Qik手机录像直播平台(服务器端:feng streaming server + web server,客户端:Android手机应用)
- Android SystemServer总结
- android gdbserver
- Android Intent.FLAG_NEW_TASK详解,包括其他的标记的一些解释
- eclipse的new server里tomcat7.0根本选不上解决方法
- Android ADB server didn't ACK * failed to start daemon * 简单有效的解决方案
- android system server 启动流程
- android-BluetoothServerSocket
- Unable to find config file. Creating new servlet engine config file: /WEB-INF/server-config.wsdd的解决方法
- Android实战简易教程-第六十六枪(server端搭建和server端Json数据交互)
- Android Intent.FLAG_NEW_TASK详解,包括其他的标记的一些解释
- 【Android Studio】android Internal HTTP server disabled 解决
- Ubuntu 12.04 server 搭建Android开发环境记录
- No compatible targets were found Do you wish to a add new Android Virtual Device ?