手机画面同步:部署 minicap 程序到手机>执行 minicap 程序>绑定 Socket 端口到本地 PC>监听并解析协议内容
调用 adb 模拟点击:获取鼠标按下 x 坐标>获取鼠标松开 x 坐标>计算两个 x 坐标的差值>换算成 adb 长按的时长
初始化 minicap:
package com.phery.minicap;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
public class InitDevices
{
public static int SDK_VERSION;
public static String ABI;
public static int PRE;
public static String REL;
public static String DIR;
public static String BIN;
public static void init()
{
SDK_VERSION = Integer.valueOf(excuteCommand("adb shell getprop ro.build.version.sdk")).intValue();
ABI = excuteCommand("adb shell getprop ro.product.cpu.abi");
String preSDK = excuteCommand("adb shell getprop ro.build.version.preview_sdk");
if (!"".equals(preSDK))
PRE = Integer.valueOf(preSDK).intValue();
REL = excuteCommand("adb shell getprop ro.build.version.release");
DIR = "/data/local/tmp/minicap-devel";
if (PRE > 0)
SDK_VERSION++;
if (SDK_VERSION >= 16)
BIN = "minicap";
else
BIN = "minicap-nopie";
String result;
result = excuteCommand("adb shell \"mkdir " + DIR + " 2>/dev/null || true\"");
System.out.println(result);
result = excuteCommand("adb push resource/libs/" + ABI + "/" + BIN + " " + DIR);
System.out.println(result);
result = excuteCommand("adb push resource/aosp/libs/android-" + SDK_VERSION + "/" + ABI + "/minicap.so " + DIR);
System.out.println(result);
result = excuteCommand("adb shell \"chmod 777 " + DIR + "/*\"");
System.out.println(result);
result = excuteCommand("adb forward tcp:1717 localabstract:minicap");
System.out.println(result);
String wmsize = excuteCommand("adb shell wm size");
String[] value = wmsize.split(" ");
wmsize = value[2];
try
{
Runtime.getRuntime().exec(
"adb shell LD_LIBRARY_PATH=" + DIR + " " + DIR + "/" + BIN + " -Q 100 -P " + wmsize + "@480x800/0");
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// System.out.println(result);
}
// public static void main(String[] args)
// {
// init();
// }
public static String excuteCommand(String cmd)
{
/* 获取cmd命令 */
try
{
System.out.println("正在执行命令:" + cmd);
Process pro = Runtime.getRuntime().exec(cmd); // 添加要进行的命令,"cmd
// /c
// calc"中calc代表要执行打开计算器,如何设置关机请自己查找cmd命令
BufferedReader br = new BufferedReader(new InputStreamReader(pro.getInputStream())); // 虽然cmd命令可以直接输出,但是通过IO流技术可以保证对数据进行一个缓冲。
String msg = null;
String result = null;
System.out.println("执行完成!!!");
while ((msg = br.readLine()) != null)
{
System.out.println(msg);
if (result == null)
result = msg;
}
System.out.println(result);
return result;
} catch (IOException exception)
{
exception.printStackTrace();
return "";
}
/*
* cmd /c dir 是执行完dir命令后关闭命令窗口 cmd /k dir 是执行完dir命令后不关闭命令窗口 cmd /c start
* dir 会打开一个新窗口后执行dir命令,原窗口会关闭 cmd /k start dir 会打开一个新窗口后执行dir命令,原窗口不会关闭
* cmd /? 查看帮助信息
*/
}
}
解析 minicap 协议:
package com.phery.minicap;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.Buffer;
public class MiniCap
{
private RefreshListener listener;
public MiniCap(RefreshListener listener)
{
this.listener = listener;
}
int readBannerBytes = 0;
int bannerLength = 24;
int readFrameBytes = 0;
int frameBodyLength = 0;
byte[] frameBody = new byte[0];
String[] banner = new String[24];
public void tryRead()
{
Socket socket;
InputStream stream = null;
try
{
socket = new Socket("127.0.0.1", 1717);
stream = socket.getInputStream();
} catch (UnknownHostException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
while (true)
{
try
{
DataInputStream input = new DataInputStream(stream);
int len = input.available();// 获取读到的字节长度
byte[] chunk = new byte[len];
input.read(chunk);// 将读取的字节保存到chunk
// System.out.println("拉取到长度:" + len);
for (int cursor = 0; cursor < len;)
{
if (readBannerBytes < bannerLength)
{
switch (readBannerBytes)
{
case 0:
// version
banner[0] = chunk[cursor] + "";
System.out.println("Banner Version:" + banner[0]);
break;
case 1:
// length
bannerLength = chunk[cursor];
banner[1] = String.valueOf(bannerLength);
System.out.println("Banner bannerLength:" + banner[1]);
break;
// case 2:
// case 3:
// case 4:
case 5:
// byte[] temp = new byte[4];
// System.arraycopy(chunk, 2, temp, 0, 4);
banner[5] = bytesToLong(chunk, 2) + "";
// pid
// banner[5] += (chunk[cursor] << ((readBannerBytes
// - 2) * 8)) >>> 0;
System.out.println("Banner pid:" + banner[5]);
break;
// case 6:
// case 7:
// case 8:
case 9:
// real width
banner[9] = bytesToLong(chunk, 6) + "";
// banner[9] += (chunk[cursor] << ((readBannerBytes
// - 6) * 8)) >>> 0;
System.out.println("Banner real width:" + banner[9]);
break;
// case 10:
// case 11:
// case 12:
case 13:
// real height
banner[13] = bytesToLong(chunk, 10) + "";
// banner[13] += (chunk[cursor] << ((readBannerBytes
// - 10) * 8)) >>> 0;
System.out.println("Banner real height:" + banner[13]);
break;
// case 14:
// case 15:
// case 16:
case 17:
// virtual width
banner[17] = bytesToLong(chunk, 14) + "";
// banner[17] += (chunk[cursor] << ((readBannerBytes
// - 14) * 8)) >>> 0;
System.out.println("Banner virtual width:" + banner[17]);
break;
// case 18:
// case 19:
// case 20:
case 21:
// virtual height
banner[21] = bytesToLong(chunk, 18) + "";
// banner[21] += (chunk[cursor] << ((readBannerBytes
// - 18) * 8)) >>> 0;
System.out.println("Banner virtual height:" + banner[21]);
break;
case 22:
// orientation
banner[22] += chunk[cursor] * 90;
System.out.println("Banner orientation:" + banner[22]);
break;
case 23:
// quirks
banner[23] = chunk[cursor] + "";
System.out.println("Banner quirks:" + banner[23]);
break;
}
cursor += 1;
readBannerBytes += 1;
if (readBannerBytes == bannerLength)
{
System.out.println("banner读取完毕!");
// console.log('banner', banner)
}
} else if (readFrameBytes < 4)
{
frameBodyLength = (int) bytesToLong(chunk, 0);
// System.out.println("获取字节长度:" + frameBodyLength);
// frameBodyLength += (chunk[cursor] << (readFrameBytes
// * 8)) >>> 0;
if (frameBodyLength < 0 || frameBodyLength > 999999)
{
cursor += frameBodyLength;
frameBodyLength = readFrameBytes = 0;
frameBody = new byte[0];
continue;
}
cursor += 4;
readFrameBytes += 4;
} else
{
if (len - cursor >= frameBodyLength)
{
// console.info('bodyfin(len=%d,cursor=%d)',
// frameBodyLength, cursor)
byte[] temp = new byte[cursor + frameBodyLength];
System.arraycopy(chunk, cursor, temp, 0, cursor + frameBodyLength);
frameBody = addBytes(frameBody, temp);
// Sanity check for JPG header, only here for
// debugging purposes.
// System.out.println("frameBody[0]:" + frameBody[0]
// + " frameBody[1]" + frameBody[1]
// + " 0xFF:" + 0xFF + " 0xD8:" + 0xD8);
// if (frameBody[0] != -1 || frameBody[1] != -40)
// {
// cursor += frameBodyLength;
// frameBodyLength = readFrameBytes = 0;
// frameBody = new byte[0];
// console.error(
// 'Frame body does not start with JPG header',
// frameBody)
// continue;
// }
// System.out.println("读取完毕:" + frameBody.length);
listener.refresh(frameBody);
cursor += frameBodyLength;
frameBodyLength = readFrameBytes = 0;
frameBody = new byte[0];
} else
{
// console.info('body(len=%d)', len - cursor)
byte[] temp = new byte[len - cursor];
System.arraycopy(chunk, cursor, temp, 0, len - cursor);
frameBody = addBytes(frameBody, temp);
// System.out.println("body:" + (len - cursor));
// System.out.println("frameBodyLength:" +
// frameBodyLength);
frameBodyLength -= len - cursor;
readFrameBytes += len - cursor;
cursor = len;
}
}
}
} catch (Exception e)
{
e.printStackTrace();
System.out.println("报错了");
}
}
}
/**
* byte数组中取int数值,本方法适用于(低位在前,高位在后)的顺序,和和intToBytes()配套使用
*
* @param src
* byte数组
* @param offset
* 从数组的第offset位开始
* @return int数值
*/
public static long bytesToLong(byte[] src, int offset)
{
long value;
value = (long) ((src[offset] & 0xFF) | ((src[offset + 1] & 0xFF) << 8) | ((src[offset + 2] & 0xFF) << 16)
| ((src[offset + 3] & 0xFF) << 24));
return value;
}
/**
*
* @param data1
* @param data2
* @return data1 与 data2拼接的结果
*/
public static byte[] addBytes(byte[] data1, byte[] data2)
{
byte[] data3 = new byte[data1.length + data2.length];
System.arraycopy(data1, 0, data3, 0, data1.length);
System.arraycopy(data2, 0, data3, data1.length, data2.length);
return data3;
}
}