Android 解决不同进程发送KeyEvent 的问题

本文介绍了一种在Android中实现跨进程模拟按键的方法。通过修改应用的userId为系统级别,并使用特定配置编译,解决了因权限不足导致无法在后台模拟按键的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做有关于Remote Controller 的功能,该功能把手机做成TV的遥控器来处理。在手机的客户端发送消息到TV的android 服务端,服务端接收到客户端的请求消息,模拟KeyEvent命令,发送Key值。

 最简单的发送命令为如下代码:

 

  

复制代码
public static void simulateKeystroke(final int KeyCode) {

 

        new Thread(new Runnable() {
            
            public void run() {
                // TODO Auto-generated method stub
                try {
                    
                    Instrumentation inst=new Instrumentation();
                    inst.sendKeyDownUpSync(KeyCode);
                } catch (Exception e) {
                    // TODO: handle exception
                }
            }
        }).start();
    }
复制代码

这种方法在当前的界面和相同的进程上是没有问题的,可以实现的基本的需求。但当我还是把服务开启着,按HOME将服务或者界面退出到后台时,再通过客户端向服务端服务发送消息使其模拟按键时,不幸的事情发送了:

Injecting to another application requires INJECT_EVENTS permission

提示没有 INJECT_EVENTS这个权限。没则加之,在AndroidManifest.xml文件里面添加该权限,再运行,问题还是没有解决,原因是上面代码最终还是调用的WindowsManagerService 里面的injectKeyEvent方法,该方法会去验证你当前的程序的pid和uid,如果两者在分发key 键时返回-1则会提示上面的error.

 

 好了,废话一大堆,下面到了真正解决这一问题的方法了。

网上各种google 各种百度,找不到自己需要的答案。 

 想过一个方法是(尚未验证):

通过jni的方法将kernel 的发送keyevent的方法用NDK封装成方法,做成库给java调用,从而绕过Android WindowsManagerService 的验证,这是我初期想到的解决思路,但尚未验证。

另外一个通过验证的方法为:

将你的服务的userId改成系统级别的,在manifest加如下代码:

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.xuzhitech.remote.server"
    android:versionCode
="1"
    android:versionName
="1.0"
    android:sharedUserId
="android.uid.system" >

 加上这一代码,需要在源码里面编译才能生效,添加Android.mk文件:

复制代码
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := RemoteAndroidServer
LOCAL_CERTIFICATE := platform
#LOCAL_CERTIFICATE := share

LOCAL_OVERRIDES_PACKAGES := Home
include $(BUILD_PACKAGE) 
复制代码

这里的 LOCAL_CERTIFICATE 要使用platform编译,而不是share编译。

到了这里,你就可以跨进程模拟按键了。

Best Regards .

<think>我们正在处理用户的问题:通过ADB发送广播但未收到,可能缺少参数或配置问题。用户的问题androidadbbroadcast sendno parametersnot received根据之前的引用和系统级指令,我们需要:1.使用中文回答2.结构清晰,逐步解决问题3.生成相关问题4.在回答中引用站内引用时,在末尾添加引用标识分析问题:用户使用adb发送广播,但没有接收到,可能的原因包括:-广播的action不正确-广播没有正确发送(例如权限问题)-接收器没有正确注册(例如在AndroidManifest.xml中声明或动态注册)-发送广播时缺少必要的参数(例如组件名或extra数据)但用户提到“noparameters”,所以可能是没有带参数导致接收器无法处理?然而,用户特别提到“noparameters”,所以问题可能在于发送的广播没有携带参数,而接收端需要某些参数?但更常见的问题是广播的action不匹配,或者接收器没有被触发。我们首先回顾一下adb发送广播的命令格式:基本命令:adbshell ambroadcast -a <ACTION>其中,<ACTION>是广播的action字符串。如果广播需要携带参数,可以使用--es,--ei,--ez等添加extra数据,但用户说没有参数,所以可能只是发送一个简单的广播。可能的原因及解决方案:1.检查广播的action是否正确:-确保发送的action与接收器中注册的action完全一致(包括大小写)。2.检查接收器是否注册:-静态注册:在AndroidManifest.xml中声明,确保<intent-filter>包含该action。-动态注册:在代码中注册,确保注册的时机正确(例如在Activity的onResume中注册,在onPause中注销?),并且当前应用处于活动状态。3.权限问题:-发送广播时,如果需要权限,可以在命令中添加权限(例如--receiver-permission),但通常不需要。-接收器可能需要权限,例如在清单中声明了<receiver>时使用了android:permission属性,那么发送方需要具有该权限。4.广播的类型:-有序广播和无序广播:如果发送的是有序广播(通过添加`--ordered`参数),那么接收器可能会中断广播,或者优先级问题导致没有收到。-但是默认发送的是无序广播。5.系统广播与普通广播:-一些系统广播需要特定的权限,或者只能由系统发送。6.用户状态:-如果接收器在另一个用户空间(例如多用户),可能需要指定用户:`--user <USER_ID>`,其中<USER_ID>通常是0(主用户)。7.包名限制:-如果接收器指定了包名,那么发送广播时可以通过`-p<package_name>`来指定包名,或者使用`--receiver-include-package`指定接收器的包名。8.其他:日志查看-使用adblogcat查看广播发送和接收的日志,过滤关键字(如Broadcast、ActivityManager)可以帮助诊断问题。根据引用[1]和[2]中提到的adb logcat的用法,我们可以使用不同的格式来查看日志。例如:adb logcat -v time#显示时间adb logcat -v tag#显示标签和优先级我们可以建议用户查看日志,过滤广播相关的信息,例如:adblogcat| grep"Broadcast"但是注意,在Windows上可能没有grep,可以使用findstr(在Windows命令行中)或者直接在日志中查找。另外,引用[3]提到清空日志缓存:adb logcat -c,然后重新发送广播,再使用adb logcat -d输出日志。具体步骤建议:1.确认接收器的注册情况(静态或动态)。2.检查发送的action字符串是否完全匹配。3.尝试在发送广播时指定接收器的包名(如果接收器在特定应用中)。4.查看日志,确认广播是否被发送以及是否被接收。发送广播的完整命令示例(带包名):adbshell ambroadcast -a com.example.MY_ACTION -p com.example.myapp如果接收器是系统应用,可能需要使用系统权限,例如:adb shellam broadcast-acom.example.MY_ACTION--receiver-permission android.permission.BROADCAST_SYSTEM如果以上都不行,尝试发送一个系统已知的广播(如android.intent.action.BOOT_COMPLETED)来测试,但注意这个广播需要接收器有接收开机广播的权限,并且只能由系统发送(在非系统应用上可能无法收到,除非在系统应用中使用)。但是,用户的问题是没有参数,所以我们先不考虑参数问题。回答结构:1.分析可能原因2.提供解决方案步骤3.建议使用logcat调试4.生成相关问题注意:我们引用了站内内容,在回答末尾需要添加引用标识。开始回答:</think>### 解决ADB发送广播未接收的问题 当通过ADB发送广播(尤其是无参数的广播)未收到响应时,通常由以下原因导致。请按顺序排查: --- #### 1. **广播Action未匹配** - **问题**:接收方注册的Intent Filter与发送的Action字符串不匹配(区分大小写)。 - **验证方法**: ```bash adb shell am broadcast -a YOUR_ACTION_NAME ``` 检查接收方代码中的`<intent-filter>`是否包含完全相同的`YOUR_ACTION_NAME`。 --- #### 2. **接收器注册问题** - **静态注册**:在`AndroidManifest.xml`中声明`<receiver>`,但未启用或权限不足: ```xml <receiver android:name=".MyReceiver" android:exported="true"> <intent-filter> <action android:name="YOUR_ACTION_NAME" /> </intent-filter> </receiver> ``` - 确保`android:exported="true"`(允许外部广播)。 - 若目标为Android 8+,需避免使用隐式广播([限制说明](https://developer.android.com/guide/components/broadcast-exceptions))。 - **动态注册**:在代码中注册的接收器需保证注册时所属组件(如Activity)处于活跃状态。 --- #### 3. **权限缺失** - **发送方权限**:发送广播时添加权限声明: ```bash adb shell am broadcast -a YOUR_ACTION_NAME --receiver-permission YOUR_PERMISSION ``` - **接收方权限**:在`AndroidManifest.xml`中声明所需权限: ```xml <uses-permission android:name="YOUR_PERMISSION" /> ``` --- #### 4. **用户/设备状态问题** - **多用户设备**:指定用户ID(主用户通常为`0`): ```bash adb shell am broadcast --user 0 -a YOUR_ACTION_NAME ``` - **设备休眠**:唤醒设备后再发送广播(`adb shell input keyevent KEYCODE_WAKEUP`)。 --- #### 5. **日志诊断** - 清空日志并重新触发广播: ```bash adb logcat -c # 清空缓存 adb shell am broadcast -a YOUR_ACTION_NAME adb logcat -d # 输出日志 ``` - 过滤关键日志: ```bash adb logcat | grep -E "ActivityManager|Broadcast" ``` 观察是否出现: - `Broadcast completed: result=0` → 广播已投递。 - `Skipping receiver` → 接收器未注册/权限不足[^1][^2][^3]。 --- #### 6. **其他可能原因** - **系统广播限制**:部分系统Action(如`BOOT_COMPLETED`)需系统签名或特殊权限。 - **进程死亡**:接收方应用进程未运行(后台限制或强制停止)。 - **Intent格式错误**:即使无参数,也需确保Action字符串无拼写错误。 --- ### 相关问题 1. 如何通过ADB发送带参数的广播(如Extra数据)? 2. Android 8.0以上隐式广播限制如何绕过? 3. 如何静态注册系统广播(如开机启动)? 4. `adb logcat`如何过滤特定应用的日志? [^1]: 引用[1] [^2]: 引用[2] [^3]: 引用[3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值