对房屋等进行视频监控有较大的需求,现在手机较多,怎么样用手机去作为监控器实现这个功能呢?
目前市面上有些应用有这个功能,我试用了一些,有的很费电,需要一直保持摄像状态。
还有就是数据安全性,用户的隐私视频数据存在泄漏的风险。
比较便捷的一种方法是使用微信作为视频工具,需要查看的时候,向采集视频的手机发送视频请求,这样就可以随时进行连接查看,很省电,不必一直打开采集端。
充分利用微信,就像借居蟹利用海螺一样
现在的问题是,如何让手机自动接听视频通话请求?
微信视频请求只需要点击接听按钮,怎样去实现自动点击屏幕呢?
查找资料发现,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" >
编译后即可