跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Android手机监控应用(二)

// http service 服务类

package com.example.chinaso.appcrawlermaster;

import android.app.Service; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.Image; import android.media.ImageReader; import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; import android.os.IBinder; import android.os.Binder; import android.util.Base64; import android.view.Surface; import android.widget.Toast; import android.util.DisplayMetrics;

import java.io.*; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.Enumeration; import java.util.Vector;

public class HttpService extends Service { // Http服务器 private HttpServer http_server;

// 本机ip private String local_ip;

// 当前服务是否在运行 private boolean is_running = false;

// 命令接收队列 private Vector cmd_queue = new Vector<>();

// 截屏有关 private static final int REQUEST_MEDIA_PROJECTION = 1; private int mScreenDensity; private int screen_width; private int screen_height; private int mResultCode; private Intent screen_capture_intent = null; private Surface mSurface; private MediaProjectionManager mMediaProjectionManager = null; private MediaProjection mMediaProjection; private VirtualDisplay mVirtualDisplay; private ImageReader image_reader; private static final int MAX_IMAGE_NUM = 15; private boolean has_inited = false;

public HttpService() { super(); }

// 命令调度线程 public class CommandScheduler extends Thread { private int sleep_time; public CommandScheduler(int sleep_time) { this.sleep_time = sleep_time; }

@Override public void run() { try { while (true) { Command cmd = popCommand(); if (cmd != null) { String tag = cmd.getTag(); switch (tag) { case "screen": //System.out.println("receive screen capture cmd"); capture_thread.addCaptureCommandToQueue(cmd); break; case "swipe": System.out.println("正在分发滑动命令"); callback.dispatchCommand(cmd); break; case "jquery.js": String js_data = loadFile("jquery.js"); cmd.setResponse(js_data); break; case "remote.html": String html_data = loadFile("remote.html"); cmd.setResponse(html_data); break; } } Thread.sleep(sleep_time); } } catch (InterruptedException e) { e.printStackTrace(); } } } CommandScheduler scheduler = null;

// 命令执行线程基类 class CommandThread extends Thread { protected boolean is_running = true; protected int sleep_time; protected Command command = null;

public CommandThread(int sleep_time) { this.sleep_time = sleep_time; }

@Override public void run() { try { while (is_running) { execute(); Thread.sleep(sleep_time); } } catch (InterruptedException e) { e.printStackTrace(); } }

protected void execute() { System.out.println("start to execute screen capture cmd"); } public void setCommand(Command cmd) { this.command = cmd; } protected void stopRun () { this.is_running = false; } }

// 截图线程 private Vector capture_cmd_queue = new Vector<>(); class ScreenCaptureThread extends CommandThread { private String data = null; private long last_cmd_arrive_time = 0; public ScreenCaptureThread(int sleep_time) { super(sleep_time); }

@Override public void execute() { //super.execute(); if (mMediaProjection != null) { //System.out.println("媒体工程对象不为空!"); if (capture_cmd_queue.size() == 0) { //System.out.println("No capture command now"); return; } Command cmd = capture_cmd_queue.firstElement(); if (cmd == null) { //System.out.println("No capture command now"); return; } //System.out.println("start to capture screen..."); //setUpVirtualDisplay(); // 获取实时屏幕图片数据 byte[] bytes = getLastestImageContent(); if (bytes == null) { return; } System.out.println("调度线程命令总数: " + cmd_queue.size()); System.out.println("截图线程命令总数: " + capture_cmd_queue.size()); //cmd.setResponse(data); ((ScreenCommand) cmd).setBytes(bytes); //capture_cmd_queue.remove(cmd); System.out.println("调度线程命令总数: " + cmd_queue.size()); System.out.println("截图线程命令总数: " + capture_cmd_queue.size());

} else { //System.out.println("媒体工程对象为空!"); //Toast.makeText(HttpService.this, "虚拟显示对象为空!", Toast.LENGTH_SHORT).show(); } }

public void addCaptureCommandToQueue(Command cmd) { capture_cmd_queue.add((ScreenCommand)cmd); //System.out.println("向截图线程队列加入一条命令: " + capture_cmd_queue.size()); } } ScreenCaptureThread capture_thread = null;

//创建服务 @Override public void onCreate() { Toast.makeText(HttpService.this, "Http监听服务已创建", Toast.LENGTH_SHORT).show(); super.onCreate(); if (is_running == false) { local_ip = getIP(); http_server = new HttpServer(local_ip, 8080); http_server.registerService(HttpService.this); } else { Toast.makeText(HttpService.this, "Http服务已在8080端口监听", Toast.LENGTH_SHORT).show(); } try { if (is_running == false) { // 启动调度线程 scheduler = new CommandScheduler(10); scheduler.start(); // 启动截图线程 startCommandThread(); // 启动http服务 http_server.start(); is_running = true; Toast.makeText(HttpService.this, "Http监听服务开始执行", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(HttpService.this, "Http监听服务运行中", Toast.LENGTH_SHORT).show(); } } catch (IOException e) { Toast.makeText(HttpService.this, "出现IO异常", Toast.LENGTH_SHORT).show(); e.printStackTrace(); } }

@Override public int onStartCommand(Intent intent, int flags, int startId) { if (has_inited == false && intent != null) { mScreenDensity = intent.getIntExtra("screen_density", 0); screen_width = intent.getIntExtra("screen_width", 0); screen_height = intent.getIntExtra("screen_height", 0); mResultCode = intent.getIntExtra("result_code", 0); System.out.println("取得屏幕宽高: " + screen_width + ", " + screen_height); has_inited = true; } return super.onStartCommand(intent, flags, startId); }

public void addCommand(Command cmd) { cmd_queue.add(cmd); }

public Command popCommand() { if (cmd_queue.size() > 0) { return cmd_queue.remove(0); } return null; }

public boolean removeCommand(Command cmd) { if (capture_cmd_queue.size() > 0) { return capture_cmd_queue.remove(cmd); } return false; }

// 获取jquery.js或remote.html的内容 public String loadFile(String file_name) { InputStream in; if (file_name.equals("jquery.js")) { in = getResources().openRawResource(R.raw.jquery); } else { in = getResources().openRawResource(R.raw.remote); } InputStreamReader inputStreamReader = null; try { inputStreamReader = new InputStreamReader(in, "UTF-8"); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } BufferedReader reader = new BufferedReader(inputStreamReader); StringBuffer sb = new StringBuffer(); String line; try { while ((line = reader.readLine()) != null) { sb.append(line); sb.append("\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { inputStreamReader.close(); } catch(IOException e) { e.printStackTrace(); } } String s = sb.toString(); if (file_name.equals("remote.html")) { String res = s.replaceAll("\\{\\{screen_uri\\}\\}", "http://" + local_ip + ":8080"); return res; } else { return s; } }

//销毁服务时调用 @Override public void onDestroy() { if (is_running) { is_running = false; stopCommandThread(); stopVirtualDisplay(); tearDownMediaProjection(); http_server.stop(); Toast.makeText(HttpService.this, "Http监听服务已销毁", Toast.LENGTH_LONG).show(); } super.onDestroy(); }

// 获取虚拟显示对象 private void setUpVirtualDisplay() { mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture", screen_width, screen_height, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mSurface, null, null); try { System.out.println("睡眠开始"); Thread.sleep(5000); System.out.println("睡眠结束"); } catch (InterruptedException e) { e.printStackTrace(); } }

// 从ImageReader中读取最新截屏的字符串数据 public byte[] getLastestImageContent() { System.out.println("开始获取最近的截图"); Image image = image_reader.acquireLatestImage(); if (image == null) { return null; } int width = image.getWidth(); int height = image.getHeight(); final Image.Plane[] planes = image.getPlanes(); final ByteBuffer buffer = planes[0].getBuffer(); int pixelStride = planes[0].getPixelStride(); int rowStride = planes[0].getRowStride(); int rowPadding = rowStride - pixelStride * width; Bitmap bitmap; int width_value = width + rowPadding / pixelStride; System.out.println("截图宽高: " + width_value + ", " + height);

bitmap = Bitmap.createBitmap(width_value, height, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(buffer);

/* // 方案一 ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); image.close(); //byte[] arr = baos.toByteArray(); String image_buffer = null; try { image_buffer = baos.toString("utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //image_buffer = new String(arr); System.out.println("截图大小: " + image_buffer.length()); return image_buffer; */

// 方案二 ByteArrayOutputStream bStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 20, bStream); image.close(); byte[] bytes = null; try { bStream.flush(); bStream.close(); bytes = bStream.toByteArray(); } catch (IOException e) { e.printStackTrace(); } System.out.println("结束获取最近的截图"); return bytes; }

// 长时间没有命令请求时释放VirtualDisplay private void stopVirtualDisplay() { if (mVirtualDisplay == null) { return; } mVirtualDisplay.release(); mVirtualDisplay = null; }

// 释放MediaProjection对象 public void tearDownMediaProjection() { if (mMediaProjection != null) { Toast.makeText(HttpService.this, "媒体工程对象被销毁!", Toast.LENGTH_LONG).show(); System.out.println("媒体工程对象被销毁!"); mMediaProjection.stop(); mMediaProjection = null; } }

// 启动命令执行线程 public void startCommandThread() { capture_thread = new ScreenCaptureThread(50); capture_thread.start(); }

// 停止命令执行线程 public void stopCommandThread() { capture_thread.is_running = false; }

@Override public IBinder onBind(Intent intent) { System.out.println("--onBind()--"); return new ServiceBinder(); }

@Override public boolean onUnbind(Intent intent) { System.out.println("--onUnbind()--"); return super.onUnbind(intent); }

public class ServiceBinder extends Binder { HttpService getService() { return HttpService.this; }

// 设置ScreenCaptureIntent public void setScreenCaptureIntent(Intent intent) { screen_capture_intent = intent; image_reader = ImageReader.newInstance(screen_width, screen_height, PixelFormat.RGBA_8888, MAX_IMAGE_NUM); mSurface = image_reader.getSurface(); mMediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE); mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, screen_capture_intent); if (mMediaProjection == null) { System.out.println("媒体工程对象初始化失败!"); } else { System.out.println("媒体工程对象初始化成功!"); setUpVirtualDisplay(); if (mVirtualDisplay != null) { System.out.println("虚拟显示对象初始化成功!"); } else { System.out.println("虚拟显示对象初始化失败!"); } } } }

Callback callback = null; public Callback getCallback() { return callback; }

public void setCallback(Callback callback) { this.callback = callback; }

// 通过回调机制,将Service内部的变化传递到外部 public interface Callback { void dispatchCommand(Command cmd); }

// 获取本机IP地址 public String getIP(){ try { for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { InetAddress inetAddress = enumIpAddr.nextElement(); if (!inetAddress.isLoopbackAddress() && (inetAddress instanceof Inet4Address)) { return inetAddress.getHostAddress().toString(); } } } } catch (SocketException ex){ ex.printStackTrace(); } return null; } }

// 无障碍 service 服务类

package com.example.chinaso.appcrawlermaster;

import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.GestureDescription; import android.content.Context; import android.util.DisplayMetrics; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Toast; import android.graphics.Path;

public class BarrierFreeService extends AccessibilityService {

private static SwipeCommand current_cmd = null;

@Override public void onAccessibilityEvent(AccessibilityEvent event) { int eventType = event.getEventType(); switch (eventType) { case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: if (event.getPackageName().toString().equals("com.example.chinaso.appcrawlermaster") == false || event.getClassName().toString().equals("android.app.Notification") == false) { return; } System.out.println("类名: " + event.getClassName()); System.out.println("包名: " + event.getPackageName()); if (current_cmd != null) { System.out.println("无障碍服务正在执行滑动命令: " + current_cmd.getDuration()); int x1 = current_cmd.getUpLeftX(); int y1 = current_cmd.getUpLeftY(); int x2 = current_cmd.getDownRightX(); int y2 = current_cmd.getDownRightY(); int duration = current_cmd.getDuration(); current_cmd = null; swipe(x1, y1, x2, y2, duration); } } }

@Override public void onInterrupt() { Toast.makeText(BarrierFreeService.this, "无障碍服务关闭", Toast.LENGTH_LONG).show(); }

@Override protected void onServiceConnected() { Toast.makeText(BarrierFreeService.this, "无障碍服务开启", Toast.LENGTH_LONG).show(); }

// 重置当前命令 public static void setCurrentCommand(SwipeCommand cmd) { current_cmd = cmd; }

// 滑动 public void swipe(int x1, int y1, int x2, int y2, int delay) { Path path = new Path(); path.moveTo(scaleX(x1), scaleY(y1)); path.lineTo(scaleX(x1), scaleY(y1)); path.lineTo(scaleX(x2), scaleY(y2)); GestureDescription.StrokeDescription stroke_desc = new GestureDescription.StrokeDescription(path, 0, delay); GestureDescription.Builder builder = new GestureDescription.Builder(); builder.addStroke(stroke_desc); GestureDescription gesture_desc = builder.build(); boolean res = dispatchGesture(gesture_desc, null, null); if (res) { System.out.println("姿势分发成功!"); } else { System.out.println("姿势分发失败!"); } }

public int scaleX(int x) { return x / 2; }

public int scaleY(int y) { return y / 2; }

}

// 应用配置 package="com.example.chinaso.appcrawlermaster">

android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">

android:name=".BarrierFreeService" android:enabled="true" android:exported="false" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">

// 布局文件 xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity">

相关文章