MTK-Android12-适配蓝牙遥控器按键

提示:适配蓝牙遥控器按键

文章目录


前言-需求

实际场景

如下,产品是TV相关产品、闺蜜机相关产品,配置了遥控器。 但是每个客户可能有自己的配套遥控器,但是主板是我们自己的。

  • 这就导致有些遥控器按键点击没有反应,需要系统来适配让按键点击有反应。
  • 还有一种情况点击按键是有反应的,但是要执行客户自己的业务,比如跳转到客户自己的App 、应用程序、执行客户自己定制的业务逻辑。

需求

  • 点击首页必须回到系统的HOME程序
  • 点击设置,回到客户自己的应用

备注:正常情况下遥控器的大多数按键值都应该是标准的,比如常用的。客户定制的按键要么新增,要么适配。

  • Home 按键无用: 这里 HOME按键正常情况下是通用的,那么为什么点击没用? 可能定制的,可能Android系统根本没有设置主屏应用,多Launcher场景。

  • 系统设置按键无用问题: 客户有自己的定义功能,跳转到客户自己的应用里面去,所以 这个设置实际上可能根本不是标注你的按键值。

一、 参考资料

Android蓝牙遥控按键适配全攻略:从kl文件修改到KeyEvent映射

手把手教你为Android设备添加自定义蓝牙遥控按键(附KeyEvent代码示例)

rk3568 Android11/12 适配蓝牙遥控器

如果你懂一些基本的驱动,可以看看之前自己有一篇相关的 输入子系统香瓜简要知识点:
Linux驱动Input子系统开篇-输入设备和节点

二、修改文件

java 复制代码
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
/kernel-4.19-lc/drivers/hid/hid-input.c

三、解决方案

1、HID协议层-配置16位HID码值

路径:/kernel-4.19-lc/drivers/hid/hid-input.c

java 复制代码
case HID_UP_CONSUMER:	/* USB HUT v1.12, pages 75-84 */
		switch (usage->hid & HID_USAGE) {

    case 0xEB:  map_key_clear(KEY_CONFIG);      break; 
    .....
  }

2、Android框架层-将Linux事件映射为Android键码(KeyEvent)

路径:/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

java 复制代码
  // TODO(b/117479243): handle it in InputPolicy
    /** {@inheritDoc} */
    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        final int keyCode = event.getKeyCode();
      .............

  
  	// modify by fangchen start 
            case KeyEvent.KEYCODE_MUSIC: {
				Log.d(TAG,"KEYCODE_MUSIC    down:"+down);
                result = 0;
                if (down) {
                } else {
                   	 Log.d(TAG,"KEYCODE_MUSIC   to YL SETTING:");
                    sendYLSettingActicity();
                }
                break;
            }
			// modify by fangchen end  
    }



    private void sendYLSettingActicity(){
        Log.d(TAG,"sendYLSettingActicity");
        Intent intent = new Intent();
        intent.setClassName("com.komect.ajsettings","com.komect.ajsettings.MainSettingsActivity");
        if(intent==null){
        }else{
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mContext.startActivity(intent);
        }
    }
    


四、知识点延伸

1、按键框架知识点 - 三层总览

  • 框架基本常识: 首先要搞清楚框架,在Android 体系生态里面,Android是最顶层的一层,是基于Linux 体系之上。所以 Linux 中有一层事件、然后传递到Android层 大家能够感知到的各种KeyEvent 事件。 那不同的按键类型、协议 对应不同的

a、HID 层

  • 代表:0x0C (消费类)/0x07 (键盘) + Usage ID(如 0x0066)
  • 来源:USB HID、BLE HID、2.4G HID(带 USB dongle)
  • 对应文档:遥控器说明书里的 HID Usage Table

b、Linux 层

  • 核心:input_event(type/code/value),/dev/input/eventX
  • 工具:getevent 看原始事件
  • 驱动:hid-input.c 把 HID → Linux 标准键值(KEY_*)

c、Android 层

  • 核心:KeyEvent、KEYCODE_*(如 KEYCODE_VOLUME_UP)
  • 映射:.kl 文件把 Linux KEY_ → Android KEYCODE_*

2、如何获取蓝牙按键的Linux 输入事件

直接 getEvent

java 复制代码
/dev/input/event8: 0004 0004 000c00eb  ← HID 原始值
/dev/input/event8: 0001 00ab 00000001  ← 按键按下 KEY_AB
/dev/input/event8: 0000 0000 00000000  ← 同步
/dev/input/event8: 0004 0004 000c00eb  ← HID 原始值
/dev/input/event8: 0001 00ab 00000000  ← 按键松开
/dev/input/event8: 0000 0000 00000000  ←同步

这里的 000c00eb 就是 Linux 层的按键反馈,那我们看看 最终Android 端是否有反馈:点击遥控器各个按键,犯下如下打印:

路径:/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

也就是没有定义功能的按键,打印的code 是0 ,那么就是说 在Linux 层和Android层没有形成映射关系。

3、配置Linux 层 Hid 映射

java 复制代码
	case HID_UP_CONSUMER:	/* USB HUT v1.12, pages 75-84 */
		switch (usage->hid & HID_USAGE) {
       case 0x000: goto ignore;
		case 0x030: map_key_clear(KEY_POWER);		break;
		case 0x031: map_key_clear(KEY_RESTART);		break;
		case 0x032: map_key_clear(KEY_SLEEP);		break;
		case 0x034: map_key_clear(KEY_SLEEP);		break;
		case 0x035: map_key_clear(KEY_KBDILLUMTOGGLE);	break;
		case 0x036: map_key_clear(BTN_MISC);		break;

		case 0x040: map_key_clear(KEY_MENU);		break; /* Menu */
		case 0x041: map_key_clear(KEY_SELECT);		break; /* Menu Pick */
		case 0x042: map_key_clear(KEY_UP);		break; /* Menu Up */
		case 0x043: map_key_clear(KEY_DOWN);		break; /* Menu Down */
		case 0x044: map_key_clear(KEY_LEFT);		break; /* Menu Left */
		case 0x045: map_key_clear(KEY_RIGHT);		break; /* Menu Right */
		case 0x046: map_key_clear(KEY_ESC);		break; /* Menu Escape */
		case 0x047: map_key_clear(KEY_KPPLUS);		break; /* Menu Value Increase */
		case 0x048: map_key_clear(KEY_KPMINUS);		break; /* Menu Value Decrease */

		case 0x060: map_key_clear(KEY_INFO);		break; /* Data On Screen */
		case 0x061: map_key_clear(KEY_SUBTITLE);	break; /* Closed Caption */
		case 0x063: map_key_clear(KEY_VCR);		break; /* VCR/TV */
		case 0x065: map_key_clear(KEY_CAMERA);		break; /* Snapshot */
		case 0x069: map_key_clear(KEY_RED);		break;
		..............
		

根据自己的业务需求,配置实际按键映射。 那么这些按键值是在哪里定义的呢?

在头文件:kernel-4.19/include/uapi/linux/input-event-codes.h 定义,

比如我们设置,其实是没有的,我们用 KEY_CONFIG 按键代替。

4、Android 层适配业务逻辑

路径:/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

这里就是按键的处理逻辑了,最终走到Android 框架层,来处理,核心方法: interceptKeyBeforeQueueing,在里面映射按键处理逻辑就可以了,根据日志打印,然后找到 KeyEvent.某个Code 对应就可以了。

java 复制代码
   // TODO(b/117479243): handle it in InputPolicy
    /** {@inheritDoc} */
    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        final int keyCode = event.getKeyCode();
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                || event.isWakeKey();

        if (!mSystemBooted) {
            // If we have not yet booted, don't let key events do anything.
            // Exception: Wake and power key events are forwarded to PowerManager to allow it to
            // wake from quiescent mode during boot.
            if (down && (keyCode == KeyEvent.KEYCODE_POWER
                    || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
                wakeUpFromPowerKey(event.getDownTime());
            } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
                    && isWakeKeyWhenScreenOff(keyCode)) {
                wakeUpFromWakeKey(event);
            }
            return 0;
        }

        final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
        final boolean canceled = event.isCanceled();
        final int displayId = event.getDisplayId();
        final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;

        final boolean keyguardActive = (mKeyguardDelegate != null
                && (interactive ? isKeyguardShowingAndNotOccluded() :
                mKeyguardDelegate.isShowing()));
        if (DEBUG_INPUT) {
            // If screen is off then we treat the case where the keyguard is open but hidden
            // the same as if it were open and in front.
            // This will prevent any keys other than the power button from waking the screen
            // when the keyguard is hidden by another activity.
            Log.d(TAG, "interceptKeyTq keycode=" + keyCode
                    + " interactive=" + interactive + " keyguardActive=" + keyguardActive
                    + " policyFlags=" + Integer.toHexString(policyFlags));
        }

        // Basic policy based on interactive state.
        int result;
        if (interactive || (isInjected && !isWakeKey)) {
            // When the device is interactive or the key is injected pass the
            // key to the application.
            result = ACTION_PASS_TO_USER;
            isWakeKey = false;

            if (interactive) {
                // If the screen is awake, but the button pressed was the one that woke the device
                // then don't pass it to the application
                if (keyCode == mPendingWakeKey && !down) {
                    result = 0;
                }
                // Reset the pending key
                mPendingWakeKey = PENDING_KEY_NULL;
            }
        } else if (shouldDispatchInputWhenNonInteractive(displayId, keyCode)) {
            // If we're currently dozing with the screen on and the keyguard showing, pass the key
            // to the application but preserve its wake key status to make sure we still move
            // from dozing to fully interactive if we would normally go from off to fully
            // interactive.
            result = ACTION_PASS_TO_USER;
            // Since we're dispatching the input, reset the pending key
            mPendingWakeKey = PENDING_KEY_NULL;
        } else {
            // When the screen is off and the key is not injected, determine whether
            // to wake the device but don't pass the key to the application.
            result = 0;
            if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
                isWakeKey = false;
            }
            // Cache the wake key on down event so we can also avoid sending the up event to the app
            if (isWakeKey && down) {
                mPendingWakeKey = keyCode;
            }
        }

        // If the key would be handled globally, just return the result, don't worry about special
        // key processing.
        if (isValidGlobalKey(keyCode)
                && mGlobalKeyManager.shouldHandleGlobalKey(keyCode)) {
            // Dispatch if global key defined dispatchWhenNonInteractive.
            if (!interactive && isWakeKey && down
                    && mGlobalKeyManager.shouldDispatchFromNonInteractive(keyCode)) {
                mGlobalKeyManager.setBeganFromNonInteractive();
                result = ACTION_PASS_TO_USER;
                // Since we're dispatching the input, reset the pending key
                mPendingWakeKey = PENDING_KEY_NULL;
            }

            if (isWakeKey) {
                wakeUpFromWakeKey(event);
            }
            return result;
        }

        // Alternate TV power to power key for Android TV device.
        final HdmiControlManager hdmiControlManager = getHdmiControlManager();
        if (keyCode == KeyEvent.KEYCODE_TV_POWER && mHasFeatureLeanback
                && (hdmiControlManager == null || !hdmiControlManager.shouldHandleTvPowerKey())) {
            event = KeyEvent.obtain(
                    event.getDownTime(), event.getEventTime(),
                    event.getAction(), KeyEvent.KEYCODE_POWER,
                    event.getRepeatCount(), event.getMetaState(),
                    event.getDeviceId(), event.getScanCode(),
                    event.getFlags(), event.getSource(), event.getDisplayId(), null);
            return interceptKeyBeforeQueueing(event, policyFlags);
        }

        // This could prevent some wrong state in multi-displays environment,
        // the default display may turned off but interactive is true.
        final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
        final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            handleKeyGesture(event, interactiveAndOn);
        }

        // Enable haptics if down and virtual key without multiple repetitions. If this is a hard
        // virtual key such as a navigation bar button, only vibrate if flag is enabled.
        final boolean isNavBarVirtKey = ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0);
        boolean useHapticFeedback = down
                && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
                && (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled)
                && event.getRepeatCount() == 0;


        /// M: Add more log at WMS
        if (mWindowManagerDebugger.WMS_DEBUG_ENG || mWindowManagerDebugger.WMS_DEBUG_USER_DEBUG) {
            mWindowManagerDebugger.debugInterceptKeyBeforeQueueing(TAG, keyCode, interactive,
                    keyguardActive, policyFlags, down, canceled, isWakeKey,
                    result, useHapticFeedback, isInjected);
        }

        // Handle special keys.
		
		Log.d(TAG, "begin to switchKeyCode  interceptKeyTq keycode=" + keyCode+"      down:"+down);
        switch (keyCode) {
            case KeyEvent.KEYCODE_F5: {
                result = 0;
                if (down) {
                } else {
                    //huanghb
                    CustomFling();
                    onFiseFling(screenWidth, screenHeight+200, "up");
                    //Log.d("huanghb","KeyEvent KEYCODE_F5 up");
                }
                break;
            }
            case KeyEvent.KEYCODE_F9: {
                result = 0;
                if (down) {
                } else {
                    //huanghb
                    CustomFling();
                    onFiseFling(screenWidth, screenHeight-200, "down");
                    //Log.d("huanghb","KeyEvent 186 up");
                }
                break;
            }
            case KeyEvent.KEYCODE_MENU: {
                result = 0;
                if (down) {
                } else {
                    //huanghb
                    sendSettingActicity();
                }
                break;
            }
			// modify by fangchen start 
            case KeyEvent.KEYCODE_MUSIC: {
				Log.d(TAG,"KEYCODE_MUSIC    down:"+down);
                result = 0;
                if (down) {
                } else {
                   	 Log.d(TAG,"KEYCODE_MUSIC   to YL SETTING:");
                    sendYLSettingActicity();
                }
                break;
            }
			// modify by fangchen end  
            case KeyEvent.KEYCODE_BACK: {
                if (down) {
                    mBackKeyHandled = false;
                } else {
                    if (!hasLongPressOnBackBehavior()) {
                        mBackKeyHandled |= backKeyPress();
                    }
                    // Don't pass back press to app if we've already handled it via long press
                    if (mBackKeyHandled) {
                        result &= ~ACTION_PASS_TO_USER;
                    }
                }
                break;
            }

            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                if (down) {
                    sendSystemKeyToStatusBarAsync(event.getKeyCode());

                    NotificationManager nm = getNotificationService();
                    if (nm != null && !mHandleVolumeKeysInWM) {
                        nm.silenceNotificationSound();
                    }

                    TelecomManager telecomManager = getTelecommService();
                    if (telecomManager != null && !mHandleVolumeKeysInWM) {
                        // When {@link #mHandleVolumeKeysInWM} is set, volume key events
                        // should be dispatched to WM.
                        if (telecomManager.isRinging()) {
                            // If an incoming call is ringing, either VOLUME key means
                            // "silence ringer".  We handle these keys here, rather than
                            // in the InCallScreen, to make sure we'll respond to them
                            // even if the InCallScreen hasn't come to the foreground yet.
                            // Look for the DOWN event here, to agree with the "fallback"
                            // behavior in the InCallScreen.
                            Log.i(TAG, "interceptKeyBeforeQueueing:"
                                  + " VOLUME key-down while ringing: Silence ringer!");

                            // Silence the ringer.  (It's safe to call this
                            // even if the ringer has already been silenced.)
                            telecomManager.silenceRinger();

                            // And *don't* pass this key thru to the current activity
                            // (which is probably the InCallScreen.)
                            result &= ~ACTION_PASS_TO_USER;
                            break;
                        }
                    }
                    int audioMode = AudioManager.MODE_NORMAL;
                    try {
                        audioMode = getAudioService().getMode();
                    } catch (Exception e) {
                        Log.e(TAG, "Error getting AudioService in interceptKeyBeforeQueueing.", e);
                    }
                    boolean isInCall = (telecomManager != null && telecomManager.isInCall()) ||
                            audioMode == AudioManager.MODE_IN_COMMUNICATION;
                    if (isInCall && (result & ACTION_PASS_TO_USER) == 0) {
                        // If we are in call but we decided not to pass the key to
                        // the application, just pass it to the session service.
                        MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
                                event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
                        break;
                    }
                }
                if (mUseTvRouting || mHandleVolumeKeysInWM) {
                    // Defer special key handlings to
                    // {@link interceptKeyBeforeDispatching()}.
                    result |= ACTION_PASS_TO_USER;
                } else if ((result & ACTION_PASS_TO_USER) == 0) {
                    // If we aren't passing to the user and no one else
                    // handled it send it to the session manager to
                    // figure out.
                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
                            event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
                }
                break;
            }

            case KeyEvent.KEYCODE_ENDCALL: {
                result &= ~ACTION_PASS_TO_USER;
                if (down) {
                    TelecomManager telecomManager = getTelecommService();
                    boolean hungUp = false;
                    if (telecomManager != null) {
                        hungUp = telecomManager.endCall();
                    }
                    if (interactive && !hungUp) {
                        mEndCallKeyHandled = false;
                        mHandler.postDelayed(mEndCallLongPress,
                                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                    } else {
                        mEndCallKeyHandled = true;
                    }
                } else {
                    if (!mEndCallKeyHandled) {
                        mHandler.removeCallbacks(mEndCallLongPress);
                        if (!canceled) {
                            if ((mEndcallBehavior
                                    & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
                                if (goHome()) {
                                    break;
                                }
                            }
                            if ((mEndcallBehavior
                                    & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
                                sleepDefaultDisplay(event.getEventTime(),
                                        PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                                isWakeKey = false;
                            }
                        }
                    }
                }
                break;
            }

            case KeyEvent.KEYCODE_TV_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down && hdmiControlManager != null) {
                    hdmiControlManager.toggleAndFollowTvPower();
                }
                break;
            }

            case KeyEvent.KEYCODE_POWER: {
                EventLogTags.writeInterceptPower(
                        KeyEvent.actionToString(event.getAction()),
                        mPowerKeyHandled ? 1 : 0,
                        mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
                // Any activity on the power button stops the accessibility shortcut
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    interceptPowerKeyDown(event, interactiveAndOn);
                } else {
                    interceptPowerKeyUp(event, canceled);
                }
                break;
            }

            case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
                // fall through
            case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
                // fall through
            case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
                // fall through
            case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
                result &= ~ACTION_PASS_TO_USER;
                interceptSystemNavigationKey(event);
                break;
            }

            case KeyEvent.KEYCODE_SLEEP: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false;
                if (!mPowerManager.isInteractive()) {
                    useHapticFeedback = false; // suppress feedback if already non-interactive
                }
                if (down) {
                    sleepPress();
                } else {
                    sleepRelease(event.getEventTime());
                }
                break;
            }

            case KeyEvent.KEYCODE_SOFT_SLEEP: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false;
                if (!down) {
                    mPowerManagerInternal.setUserInactiveOverrideFromWindowManager();
                }
                break;
            }

            case KeyEvent.KEYCODE_WAKEUP: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = true;
                break;
            }

            case KeyEvent.KEYCODE_MEDIA_PLAY:
            case KeyEvent.KEYCODE_MEDIA_PAUSE:
            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
            case KeyEvent.KEYCODE_HEADSETHOOK:
            case KeyEvent.KEYCODE_MUTE:
            case KeyEvent.KEYCODE_MEDIA_STOP:
            case KeyEvent.KEYCODE_MEDIA_NEXT:
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
            case KeyEvent.KEYCODE_MEDIA_REWIND:
            case KeyEvent.KEYCODE_MEDIA_RECORD:
            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
                    // If the global session is active pass all media keys to it
                    // instead of the active window.
                    result &= ~ACTION_PASS_TO_USER;
                }
                if ((result & ACTION_PASS_TO_USER) == 0) {
                    // Only do this if we would otherwise not pass it to the user. In that
                    // case, the PhoneWindow class will do the same thing, except it will
                    // only do it if the showing app doesn't process the key on its own.
                    // Note that we need to make a copy of the key event here because the
                    // original key event will be recycled when we return.
                    mBroadcastWakeLock.acquire();
                    Message msg = mHandler.obtainMessage(MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,
                            new KeyEvent(event));
                    msg.setAsynchronous(true);
                    msg.sendToTarget();
                }
                break;
            }

            case KeyEvent.KEYCODE_CALL: {
                if (down) {
                    TelecomManager telecomManager = getTelecommService();
                    if (telecomManager != null) {
                        if (telecomManager.isRinging()) {
                            Log.i(TAG, "interceptKeyBeforeQueueing:"
                                  + " CALL key-down while ringing: Answer the call!");
                            telecomManager.acceptRingingCall();

                            // And *don't* pass this key thru to the current activity
                            // (which is presumably the InCallScreen.)
                            result &= ~ACTION_PASS_TO_USER;
                        }
                    }
                }
                break;
            }
            case KeyEvent.KEYCODE_ASSIST: {
                final boolean longPressed = event.getRepeatCount() > 0;
                if (down && !longPressed) {
                    Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST, event.getDeviceId(),
                            0 /* unused */, event.getEventTime() /* eventTime */);
                    msg.setAsynchronous(true);
                    msg.sendToTarget();
                }
                result &= ~ACTION_PASS_TO_USER;
                break;
            }
            case KeyEvent.KEYCODE_VOICE_ASSIST: {
                if (!down) {
                    mBroadcastWakeLock.acquire();
                    Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
                    msg.setAsynchronous(true);
                    msg.sendToTarget();
                }
                result &= ~ACTION_PASS_TO_USER;
                break;
            }
            case KeyEvent.KEYCODE_WINDOW: {
                if (mShortPressOnWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) {
                    if (mPictureInPictureVisible) {
                        // Consumes the key only if picture-in-picture is visible to show
                        // picture-in-picture control menu. This gives a chance to the foreground
                        // activity to customize PIP key behavior.
                        if (!down) {
                            showPictureInPictureMenu(event);
                        }
                        result &= ~ACTION_PASS_TO_USER;
                    }
                }
                break;
            }
        }

        // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
        if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked())) {
            switch (keyCode) {
                case KeyEvent.KEYCODE_Z: {
                    if (down && event.isCtrlPressed() && event.isAltPressed()) {
                        mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
                        result &= ~ACTION_PASS_TO_USER;
                    }
                    break;
                }
            }
        }

        if (useHapticFeedback) {
            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                    "Virtual Key - Press");
        }

        if (isWakeKey) {
            wakeUpFromWakeKey(event);
        }

        if ((result & ACTION_PASS_TO_USER) != 0) {
            // If the key event is targeted to a specific display, then the user is interacting with
            // that display. Therefore, give focus to the display that the user is interacting with.
            if (!mPerDisplayFocusEnabled
                    && displayId != INVALID_DISPLAY && displayId != mTopFocusedDisplayId) {
                // An event is targeting a non-focused display. Move the display to top so that
                // it can become the focused display to interact with the user.
                // This should be done asynchronously, once the focus logic is fully moved to input
                // from windowmanager. Currently, we need to ensure the setInputWindows completes,
                // which would force the focus event to be queued before the current key event.
                // TODO(b/70668286): post call to 'moveDisplayToTop' to mHandler instead
                Log.i(TAG, "Moving non-focused display " + displayId + " to top "
                        + "because a key is targeting it");
                mWindowManagerFuncs.moveDisplayToTop(displayId);
            }
        }

        return result;
    }

总结

  • 这里的需求就是适配处理一个遥控器按键,重点核心是知道按键是如何映射到上层的
  • 输入事件 在Linux 里面也是一个知识点,知道思路和解决方案、流程很重要
  • 红外遥控器的按键流程,如何映射到上层有点区别的,这里不讲解