您的位置:首页 > 移动开发 > Android开发

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
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  shell root vnc 休闲 android