对房屋等进行视频监控有较大的需求,现在手机较多,怎么样用手机去作为监控器实现这个功能呢?

目前市面上有些应用有这个功能,我试用了一些,有的很费电,需要一直保持摄像状态。

还有就是数据安全性,用户的隐私视频数据存在泄漏的风险。

比较便捷的一种方法是使用微信作为视频工具,需要查看的时候,向采集视频的手机发送视频请求,这样就可以随时进行连接查看,很省电,不必一直打开采集端。 

充分利用微信,就像借居蟹利用海螺一样


实时监控 实时监控对方手机屏幕_System



现在的问题是,如何让手机自动接听视频通话请求? 

微信视频请求只需要点击接听按钮,怎样去实现自动点击屏幕呢?

查找资料发现,adb shell input tap X Y 就可以实现点击 
X Y 为坐标数值。 
现在考虑写一个服务,简单点处理吧,接收SCREEN_ON广播,当手机屏幕亮的时候,就点击屏幕上指定的一个点,来进行模拟点击,实现接通视频。

尝试在代码中使用调用 
Runtime.getRuntime().exec(“input tap 322 667”); 
但是发现,不能够跨进行去进行点击,只能在启动服务的应用里进行点击。 
04-29 13:44:47.922: W/InputDispatcher(1092): Permission denied: injecting event from pid 12694 uid 10164 to window Window{ed6134b u0 com.*} owned by uid 10063 
04-29 13:44:47.922: W/InputManager(1092): Input event injection from pid 12694 permission denied.

查询资料, 
把手机root后 
Process process = Runtime.getRuntime().exec(“su”);

OutputStream outputStream = process.getOutputStream();
    DataOutputStream dataOutputStream = new DataOutputStream(
         outputStream);
    dataOutputStream.writeBytes(cmd);
    dataOutputStream.flush();
    dataOutputStream.close();
    outputStream.close();

Android 6的手机上测试,报错 

04-28 16:36:40.487: W/System.err(21863): Java.io.IOException: Error running exec(). Command: [su] Working Directory: null Environment: null 
 04-28 16:36:40.487: W/System.err(21863): at java.lang.ProcessManager.exec(ProcessManager.java:211) 
 04-28 16:36:40.487: W/System.err(21863): at java.lang.Runtime.exec(Runtime.java:174) 
 04-28 16:36:40.487: W/System.err(21863): at java.lang.Runtime.exec(Runtime.java:247) 
 04-28 16:36:40.487: W/System.err(21863): at java.lang.Runtime.exec(Runtime.java:190) 
 04-28 16:36:40.487: W/System.err(21863): at com.example.myservice.MyService.execShellCmd(MyService.java:38) 
 04-28 16:36:40.487: W/System.err(21863): at com.example.myservice.MyService.access$0(MyService.java:34)

在Android 6的adb shell中,su命令识别不了。

看来root手机的方式有些问题, 
还是考虑直接采用 
Runtime.getRuntime().exec(“input tap 322 667”);的方式。

对于Input event injection from pid 12694 permission denied的问题 
网上有方法说是要增加权限,在系统源码里进行编译。

这里采用另一种方式,考虑屏蔽系统里的这个权限判断。

查看log 
find . -name *.java | xargs grep ‘Input event injectio’ 
搜索得到InputManagerService.java

private boolean injectInputEventInternal(InputEvent event, int displayId, int mode) { 
 if (event == null) { 
 throw new IllegalArgumentException(“event must not be null”); 
 } 
 if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC 
 && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH 
 && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) { 
 throw new IllegalArgumentException(“mode is invalid”); 
 }
final int pid = Binder.getCallingPid();
    final int uid = Binder.getCallingUid();
    final long ident = Binder.clearCallingIdentity();
    final int result;
    try {
        result = nativeInjectInputEvent(mPtr, event, displayId, pid, uid, mode,

搜索nativeInjectInputEvent 
find . -name .c|xargs grep ‘nativeInjectInputEvent’

./frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp:static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */, 
 ./frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp: { “nativeInjectInputEvent”, “(JLandroid/view/InputEvent;IIIIII)I”, 
 ./frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp: (void*) nativeInjectInputEvent }, 
vi ./frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputEventObj, jint displayId, jint injectorPid, jint injectorUid,
        jint syncMode, jint timeoutMillis, jint policyFlags) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
    if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
        KeyEvent keyEvent;
        status_t status = android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);
        if (status) {
            jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
            return INPUT_EVENT_INJECTION_FAILED;
        }
 
        return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(
                & keyEvent, displayId, injectorPid, injectorUid, syncMode, timeoutMillis,
                uint32_t(policyFlags));
    } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
        const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
        if (!motionEvent) {
            jniThrowRuntimeException(env, "Could not read contents of MotionEvent object.");
            return INPUT_EVENT_INJECTION_FAILED;
        }
 
injectInputEvent(
                motionEvent, displayId, injectorPid, injectorUid, syncMode, timeoutMillis,
                uint32_t(policyFlags));
    } else {
        jniThrowRuntimeException(env, "Invalid input event type.");
        return INPUT_EVENT_INJECTION_FAILED;
    }
}

 

查看

frameworks\native\services\inputflinger\InputDispatcher.cpp
 
int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displayId,
        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
        uint32_t policyFlags) {
 
ALOGD("=====<<<<<>>>>>5-4 policyFlags |= POLICY_FLAG_TRUSTED");
//    if (hasInjectionPermission(injectorPid, injectorUid)) {
        policyFlags |= POLICY_FLAG_TRUSTED;
//    }
 
 
bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
//test 
ALOGD("hasInjectionPermission called");
return true;
//    return injectorUid == 0
//            || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
}

这样就可以进行跨进程,跨页面进行点击了


在源码中编译ap来获得系统权限也可以实现,这个方式适用性更好



把服务的userId改成系统级别的,在Androidmanifest加上:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:sharedUserId="android.uid.system" >

编译后即可