android屏幕共享及远程控制原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq909157370/article/details/69210431

今天用了下Vysor,可以实现屏幕共享和远程控制,并且不需要root,而且可以兼容所有版本Android,功能很是强大,反编译了,下面是根据Vysor源码精简的功能。

demo看这  https://github.com/android-notes/androidScreenShareAndControl

 

import android.graphics.Bitmap;
import android.graphics.Point;
import android.hardware.input.InputManager;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.view.InputDeviceCompat;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Created by wanjian on 2017/4/4.
 */

public class Main {

    private static InputManager im;
    private static Method injectInputEventMethod;
    private static long downTime;

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, FileNotFoundException {


        if ("screenshot".equals(args[0])) {
            screenShot();
            return;
        }
        initEvent();
        if ("home".equals(args[0])) {
            System.out.println("home");
            pressHome();
            return;
        }
        if ("touch".equals(args[0])) {
            System.out.println("slide");
            slideScreen();
            return;
        }

        if ("back".equals(args[0])) {
            System.out.println("back");
            back();
            return;

        }
    }

    private static void screenShot() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, FileNotFoundException {

        System.out.println("started screenshot");
        Point size = new Point();
        size.x = 1080;
        size.y = 1920;
        String surfaceClassName;
        if (Build.VERSION.SDK_INT <= 17) {
            surfaceClassName = "android.view.Surface";
        } else {
            surfaceClassName = "android.view.SurfaceControl";
        }
        Bitmap b = (Bitmap) Class.forName(surfaceClassName).getDeclaredMethod("screenshot", new Class[]{Integer.TYPE, Integer.TYPE}).invoke(null, new Object[]{Integer.valueOf(size.x), Integer.valueOf(size.y)});

        System.out.println(b);
        File file = new File("/sdcard/myscreenshot");
        if (!file.exists()) {
            file.mkdirs();
        }
        b.compress(Bitmap.CompressFormat.JPEG, 60, new FileOutputStream(new File(file, System.currentTimeMillis() + ".jpg")));

        System.out.println("finished   " + file.getAbsolutePath());
    }

    private static void back() throws InvocationTargetException, IllegalAccessException {
        sendKeyEvent(im, injectInputEventMethod, InputDeviceCompat.SOURCE_KEYBOARD, 4, false);
    }

    private static void slideScreen() throws InvocationTargetException, IllegalAccessException {
        final float clientX = 900;
        final float clientY = 200;

        touchDown(clientX, clientY);

        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            int i = 0;
            float mClientX = clientX;

            @Override
            public void run() {
                if (i++ > 300) {
                    try {
                        touchUp(mClientX, clientY);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return;
                }
                mClientX -= 3;
                try {
                    touchMove(mClientX, clientY);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                handler.postDelayed(this, 10);
            }
        }, 10);

    }

    private static void touchUp(float clientX, float clientY) throws InvocationTargetException, IllegalAccessException {
        injectMotionEvent(im, injectInputEventMethod, InputDeviceCompat.SOURCE_TOUCHSCREEN, 1, downTime, SystemClock.uptimeMillis() - downTime, clientX, clientY, 1.0f);
    }

    private static void touchMove(float clientX, float clientY) throws InvocationTargetException, IllegalAccessException {
        injectMotionEvent(im, injectInputEventMethod, InputDeviceCompat.SOURCE_TOUCHSCREEN, 2, downTime, SystemClock.uptimeMillis() - downTime, clientX, clientY, 1.0f);
    }

    private static void touchDown(float clientX, float clientY) throws InvocationTargetException, IllegalAccessException {
        downTime = SystemClock.uptimeMillis();
        injectMotionEvent(im, injectInputEventMethod, InputDeviceCompat.SOURCE_TOUCHSCREEN, 0, downTime, downTime, clientX, clientY, 1.0f);

    }


    private static void pressHome() throws InvocationTargetException, IllegalAccessException {
        //home键
        sendKeyEvent(im, injectInputEventMethod, InputDeviceCompat.SOURCE_KEYBOARD, 3, false);

    }

    private static void initEvent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        im = (InputManager) InputManager.class.getDeclaredMethod("getInstance", new Class[0]).invoke(null, new Object[0]);
        MotionEvent.class.getDeclaredMethod("obtain", new Class[0]).setAccessible(true);
        injectInputEventMethod = InputManager.class.getMethod("injectInputEvent", new Class[]{InputEvent.class, Integer.TYPE});

    }

    private static void injectMotionEvent(InputManager im, Method injectInputEventMethod, int inputSource, int action, long downTime, long eventTime, float x, float y, float pressure) throws InvocationTargetException, IllegalAccessException {
        MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, pressure, 1.0f, 0, 1.0f, 1.0f, 0, 0);
        event.setSource(inputSource);
        injectInputEventMethod.invoke(im, new Object[]{event, Integer.valueOf(0)});
    }

    private static void injectKeyEvent(InputManager im, Method injectInputEventMethod, KeyEvent event) throws InvocationTargetException, IllegalAccessException {
        injectInputEventMethod.invoke(im, new Object[]{event, Integer.valueOf(0)});
    }


    private static void sendKeyEvent(InputManager im, Method injectInputEventMethod, int inputSource, int keyCode, boolean shift) throws InvocationTargetException, IllegalAccessException {
        long now = SystemClock.uptimeMillis();
        int meta = shift ? 1 : 0;
        injectKeyEvent(im, injectInputEventMethod, new KeyEvent(now, now, 0, keyCode, 0, meta, -1, 0, 0, inputSource));
        injectKeyEvent(im, injectInputEventMethod, new KeyEvent(now, now, 1, keyCode, 0, meta, -1, 0, 0, inputSource));
    }

}

 

 


首先把这端代码编译成class文件,然后转成dex文件

 

dx --dex --output=Main.dex  Main.class


把转换好的dex文件发送到手机上

 

 

adb push Main.dex /sdcard/Main.dex

 

进入shell

 

adb shell


执行命令

 

 

export CLASSPATH=/sdcard/Main.dex 


执行截屏命令,执行后会放到/sdcard/myscreenshot文件夹下,vysor就是通过不停的截屏实现的屏幕共享

 

 

exec app_process /sdcard Main screenshot


执行按下home按键

 

 

exec app_process /sdcard Main home

 

执行按下返回按键

 

exec app_process /sdcard Main back


执行触屏事件

 

 

exec app_process /sdcard Main touch

 

 

 

 

 

完整命令如下:

 

MGJwanjian:sss wanjian$ dx --dex --output=Main.dex  Main.class
MGJwanjian:sss wanjian$ adb push Main.dex /sdcard/Main.dex 
[100%] /sdcard/Main.dex



 MGJwanjian:sss wanjian$ adb shell
shell@mx5:/ $ export CLASSPATH=/sdcard/Main.dex 
shell@mx5:/ $ exec app_process /sdcard Main screenshot
started screenshot
android.graphics.Bitmap@13c1ffa9
finished   /sdcard/myscreenshot



 MGJwanjian:sss wanjian$ adb shell
shell@mx5:/ $ export CLASSPATH=/sdcard/Main.dex 
shell@mx5:/ $ exec app_process /sdcard Main home
home



 MGJwanjian:sss wanjian$ adb shell
shell@mx5:/ $ export CLASSPATH=/sdcard/Main.dex 
shell@mx5:/ $ exec app_process /sdcard Main back
back



 MGJwanjian:sss wanjian$ adb shell
shell@mx5:/ $ export CLASSPATH=/sdcard/Main.dex 
shell@mx5:/ $ exec app_process /sdcard Main touch
slide



 MGJwanjian:sss wanjian$ adb shell
shell@mx5:/ $ export CLASSPATH=/sdcard/Main.dex 
shell@mx5:/ $ exec app_process /sdcard Main touch
slide
 

 



补:关于如何把java文件打包成class文件


最简单的方式:
在android studio中右击com.wanjian.puppet.Main这个文件,选择 run Main.main(),编译后的class文件就会自动保存到  
androidScreenShareAndControl/shareandcontrollib/build/intermediates/classes/debug 这个目录中 




方式2:
把 android sdk目录下的android.jar和supportv4.jar拷贝到   
androidScreenShareAndControl/shareandcontrollib/src/main/java
目录下,同时在这个目录下新建classes文件夹,用于保存编译后的class文件,并把命令行切换到这个目录
执行如下命令,其中android.jar和support-v4-23.4.0-sources.jar 是android sdk中的jar包,一个在platforms/android-xx文件夹下,一个在extras/android/m2repository/com/android/support/support-v4下
中间用:分割,windows的话需要用;分割
javac -cp android.jar:support-v4-23.4.0-sources.jar:./  com/wanjian/puppet/Main.java  -d classes
这样就会在classes文件夹中生成class文件了(JDK版本不能太高,不然会提示 unsupported class file version 52.0)

 

 

 

e-mail :    foreverzqh@foxmail.com  

展开阅读全文

没有更多推荐了,返回首页