Implementation of external keyboard detection in Android

Time:2021-3-9

Today came a problem: the soft keyboard can’t pop up. After analysis, because the system judges that there is an external hard keyboard, the soft keyboard will be hidden. However, the actual situation is not so simple. This problem only occurs occasionally under specific conditions. The specific analysis process is not mentioned. It is just a logic problem in the support of hardware and software keyboards. Take this opportunity to sort out the process of keyboard detection.

Configuration

In Android system, the value of keyboard in configuration is read to judge whether there is an external keyboard. The definition of keyboard type in configuration is as follows,

public static final int KEYBOARD_ Undefined = 0; // undefined keyboard
  public static final int KEYBOARD_ Nokeys = 1; // Keyless keyboard. This type is used when there is no external keyboard
  public static final int KEYBOARD_ QWERTY = 2; // standard external keyboard
  public static final int KEYBOARD_ 12key = 3; // 12 key keyboard

In the most common case, when the external keyboard is not connected, the value of keyboard is keyword_ Nokeys, when keyboard connection is detected, the value of keyboard will be updated to keyboard_ QWERTY 。 The application can judge whether there is an external keyboard according to the value of the keyboard, InputMethodService.java There is a similar judgment code in.

//Can the software disk be displayed
  public boolean onEvaluateInputViewShown() {
    Configuration config = getResources().getConfiguration();
    return config.keyboard == Configuration.KEYBOARD_NOKEYS
        || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
  }

Now the problem turns to how to update the configuration’s keyboard. stay WindowManagerService.java The configuration will be updated when the application starts. The relevant codes are as follows.

boolean computeScreenConfigurationLocked(Configuration config) {
    ......
    if (config != null) {
      // Update the configuration based on available input devices, lid switch,
      // and platform configuration.
      config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
      //The default value is keyboard_ NOKEYS
      config.keyboard = Configuration.KEYBOARD_NOKEYS;
      config.navigation = Configuration.NAVIGATION_NONAV;
      
      int keyboardPresence = 0;
      int navigationPresence = 0;
      final InputDevice[] devices = mInputManager.getInputDevices();
      final int len = devices.length;
      //Traverse input device
      for (int i = 0; i < len; i++) {
        InputDevice device = devices[i];
        //If it is not a virtual input device, the configuration will be updated according to the flags of the input device
        if (!device.isVirtual()) {
          ......
          //If the keyboard type of the input device is keyword_ TYPE_ For alphabetic, set the keyboard to keyword_ QWERTY
          if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            keyboardPresence |= presenceFlag;
          }
        }
      }
      ......
      // Determine whether a hard keyboard is available and enabled.
      boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
      //Update hardware keyboard status
      if (hardKeyboardAvailable != mHardKeyboardAvailable) {
        mHardKeyboardAvailable = hardKeyboardAvailable;
        mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
        mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
      }
      //If show in setting_ IME_ WITH_ HARD_ Keyboard is set, and keyboard is set to keyboard_ Nokeys, so that the software disk can be displayed
      if (mShowImeWithHardKeyboard) {
        config.keyboard = Configuration.KEYBOARD_NOKEYS;
      }
      ......
    }

The values that affect the keyboard in configuration are,

  • The default value is keyboard_ Nokeys, which means there is no external keyboard.
  • When the input device is keyboard_ TYPE_ Update to keyword when using alphabetic_ QWERTY, a standard keyboard.
  • When Settings.Secure.SHOW_ IME_ WITH_ HARD_ When keyword is 1, it is set to keyword_ Nokeys, the purpose is to make the soft keyboard display.

inputflinger

Next, we need to pay attention to when keyboard is set when input device_ TYPE_ Alphabetic. As you can see from the search code, this flag is set in the native code, and the code is in the inputlinker/ InputReader.cpp In the middle. Native and Java use the same definition value. If you modify the definition, you need to pay attention to modify it at the same time. The name in native is ainput_ KEYBOARD_ TYPE_ ALPHABETIC。


InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
    const InputDeviceIdentifier& identifier, uint32_t classes) {
  InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
      controllerNumber, identifier, classes);
  ......
  if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
    keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
  }
  ......
  return device;
}

When inputreader adds a device, it sets the keyboard type according to the flag of classes. This flag is in the EventHub.cpp Set in.


status_t EventHub::openDeviceLocked(const char *devicePath) {
  ......
  // Configure the keyboard, gamepad or virtual keyboard.
  if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { 
    // 'Q' key support = cheap test of whether this is an alpha-capable kbd
    if (hasKeycodeLocked(device, AKEYCODE_Q)) {
      device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
    }
  ......
}

If the input device is a keyboard with a ‘Q’ key, it is considered as a standard external keyboard. But it’s not clear why to judge the ‘Q’ key.

keylayout

As mentioned above, the ‘Q’ key is used to determine whether it is an external keyboard. The ‘Q’ key is the key value of Android, and the existence of the key value is determined by a keylayout file. KL files are stored in / system / usr / keylayout / of the target system. The system can have multiple KL files, named according to the device ID. When the system loads the keyboard device, it will find the KL file under / system / usr / keylayout / according to the vendor ID and product ID of the device. For example, a KL file is called vendor_ 0c45_ Product_ KL “, indicating that the vendor ID of the device is 0c45 and the product ID is 1109. An example of KL is as follows,


key 1   BACK
key 28  DPAD_CENTER
key 102  HOME

key 103  DPAD_UP
key 105  DPAD_LEFT
key 106  DPAD_RIGHT
key 108  DPAD_DOWN

key 113  VOLUME_MUTE
key 114  VOLUME_DOWN
key 115  VOLUME_UP

key 142  POWER

The key value mapping needs to be declared with the key, followed by the number defined as the key value in the linux driver, and the string followed by the name of the key in Android. The existence of the ‘Q’ key depends entirely on whether there is a mapping in the KL file, rather than whether the actual physical key exists. There is also a rule for searching KL files. The order of searching is as follows,


/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl

/system/usr/keylayout/DEVICE_NAME.kl

/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl

/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl

/data/system/devices/keylayout/DEVICE_NAME.kl

/system/usr/keylayout/Generic.kl

/data/system/devices/keylayout/Generic.kl

Support both hard and soft keyboard

With the above knowledge, we can give a solution to support both hard and soft keyboard.

  • Modify the source logic and set the value of keyboard in configuration as keyword_ NOKEYS。 This kind of hack is actually not good. It destroys the original logic and lacks portability. If you have to change this way, you can increase the judgment of the device. Only the specific keyboard device is set to keyword_ Nokeys to reduce side effects.
  • Modify the keylayout to remove the ‘Q’ key mapping. Sometimes the KL file is not standard. In order to be general, all the key mappings are written, but the actual hardware keys are very few. This is our case. KL files should be written as real hardware.
  • set up Settings.Secure.SHOW_ IME_ WITH_ HARD_ Keyboard is 1. I think this is the most standard way to modify, and it is also very convenient.

There are two ways to modify the third scheme. One is to modify the default setting value in the file frameworks / base / packages / settingsprovider / RES / values/ defaults.xml In addition,


<integer name="def_show_ime_with_hard_keyboard">1</integer>

Another way is to set the interface in the code when the system starts.


Settings.Secure.putInt(context.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1);

The above is the whole content of this article, I hope to help you learn, and I hope you can support developer more.