Merge "Support global key can be dispatched from non-interactive" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
55bd70ffe1
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.policy;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class wrapped the Intent for global key ops.
|
||||||
|
*/
|
||||||
|
public final class GlobalKeyIntent {
|
||||||
|
private static final String EXTRA_BEGAN_FROM_NON_INTERACTIVE =
|
||||||
|
"EXTRA_BEGAN_FROM_NON_INTERACTIVE";
|
||||||
|
|
||||||
|
private final ComponentName mComponentName;
|
||||||
|
private final KeyEvent mKeyEvent;
|
||||||
|
private final boolean mBeganFromNonInteractive;
|
||||||
|
|
||||||
|
GlobalKeyIntent(@NonNull ComponentName componentName, @NonNull KeyEvent event,
|
||||||
|
boolean beganFromNonInteractive) {
|
||||||
|
mComponentName = componentName;
|
||||||
|
mKeyEvent = new KeyEvent(event);
|
||||||
|
mBeganFromNonInteractive = beganFromNonInteractive;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent getIntent() {
|
||||||
|
final Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON)
|
||||||
|
.setComponent(mComponentName)
|
||||||
|
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
|
||||||
|
.putExtra(Intent.EXTRA_KEY_EVENT, mKeyEvent)
|
||||||
|
.putExtra(EXTRA_BEGAN_FROM_NON_INTERACTIVE, mBeganFromNonInteractive);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link KeyEvent} information of {@link Intent#ACTION_GLOBAL_BUTTON}.
|
||||||
|
*/
|
||||||
|
public KeyEvent getKeyEvent() {
|
||||||
|
return mKeyEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if the global key is dispatched from non-interactive mode.
|
||||||
|
* Information of {@link Intent#ACTION_GLOBAL_BUTTON}.
|
||||||
|
*/
|
||||||
|
public boolean beganFromNonInteractive() {
|
||||||
|
return mBeganFromNonInteractive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a GlobalKeyIntent from {@link Intent}, the action must be
|
||||||
|
* {@link Intent#ACTION_GLOBAL_BUTTON}.
|
||||||
|
*
|
||||||
|
* @param intent The received intent of the global key.
|
||||||
|
*/
|
||||||
|
public static GlobalKeyIntent from(@NonNull Intent intent) {
|
||||||
|
if (intent.getAction() != Intent.ACTION_GLOBAL_BUTTON) {
|
||||||
|
Log.wtf("GlobalKeyIntent", "Intent should be ACTION_GLOBAL_BUTTON");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
||||||
|
final boolean fromNonInteractive =
|
||||||
|
intent.getBooleanExtra(EXTRA_BEGAN_FROM_NON_INTERACTIVE, false);
|
||||||
|
return new GlobalKeyIntent(intent.getComponent(), event, fromNonInteractive);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,9 @@ import java.io.PrintWriter;
|
|||||||
* broadcast to the specified component. The action of the intent will be
|
* broadcast to the specified component. The action of the intent will be
|
||||||
* {@link Intent#ACTION_GLOBAL_BUTTON} and the KeyEvent will be included in the intent with
|
* {@link Intent#ACTION_GLOBAL_BUTTON} and the KeyEvent will be included in the intent with
|
||||||
* {@link Intent#EXTRA_KEY_EVENT}.
|
* {@link Intent#EXTRA_KEY_EVENT}.
|
||||||
|
*
|
||||||
|
* Use {@link GlobalKeyIntent} to get detail information from received {@link Intent}, includes
|
||||||
|
* {@link KeyEvent} and the information about if the key is dispatched from non-interactive mode.
|
||||||
*/
|
*/
|
||||||
final class GlobalKeyManager {
|
final class GlobalKeyManager {
|
||||||
|
|
||||||
@@ -49,13 +52,15 @@ final class GlobalKeyManager {
|
|||||||
private static final String TAG_KEY = "key";
|
private static final String TAG_KEY = "key";
|
||||||
private static final String ATTR_KEY_CODE = "keyCode";
|
private static final String ATTR_KEY_CODE = "keyCode";
|
||||||
private static final String ATTR_COMPONENT = "component";
|
private static final String ATTR_COMPONENT = "component";
|
||||||
|
private static final String ATTR_DISPATCH_WHEN_NON_INTERACTIVE = "dispatchWhenNonInteractive";
|
||||||
|
|
||||||
private static final int GLOBAL_KEY_FILE_VERSION = 1;
|
private static final int GLOBAL_KEY_FILE_VERSION = 1;
|
||||||
|
|
||||||
private SparseArray<ComponentName> mKeyMapping;
|
private SparseArray<GlobalKeyAction> mKeyMapping;
|
||||||
|
private boolean mBeganFromNonInteractive = false;
|
||||||
|
|
||||||
public GlobalKeyManager(Context context) {
|
public GlobalKeyManager(Context context) {
|
||||||
mKeyMapping = new SparseArray<ComponentName>();
|
mKeyMapping = new SparseArray<>();
|
||||||
loadGlobalKeys(context);
|
loadGlobalKeys(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +74,15 @@ final class GlobalKeyManager {
|
|||||||
*/
|
*/
|
||||||
boolean handleGlobalKey(Context context, int keyCode, KeyEvent event) {
|
boolean handleGlobalKey(Context context, int keyCode, KeyEvent event) {
|
||||||
if (mKeyMapping.size() > 0) {
|
if (mKeyMapping.size() > 0) {
|
||||||
ComponentName component = mKeyMapping.get(keyCode);
|
GlobalKeyAction action = mKeyMapping.get(keyCode);
|
||||||
if (component != null) {
|
if (action != null) {
|
||||||
Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON)
|
final Intent intent = new GlobalKeyIntent(action.mComponentName, event,
|
||||||
.setComponent(component)
|
mBeganFromNonInteractive).getIntent();
|
||||||
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
|
|
||||||
.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(event));
|
|
||||||
context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null);
|
context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null);
|
||||||
|
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||||
|
mBeganFromNonInteractive = false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,10 +92,35 @@ final class GlobalKeyManager {
|
|||||||
/**
|
/**
|
||||||
* Returns {@code true} if the key will be handled globally.
|
* Returns {@code true} if the key will be handled globally.
|
||||||
*/
|
*/
|
||||||
boolean shouldHandleGlobalKey(int keyCode, KeyEvent event) {
|
boolean shouldHandleGlobalKey(int keyCode) {
|
||||||
return mKeyMapping.get(keyCode) != null;
|
return mKeyMapping.get(keyCode) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the key will be handled globally.
|
||||||
|
*/
|
||||||
|
boolean shouldDispatchFromNonInteractive(int keyCode) {
|
||||||
|
final GlobalKeyAction action = mKeyMapping.get(keyCode);
|
||||||
|
if (action == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return action.mDispatchWhenNonInteractive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBeganFromNonInteractive() {
|
||||||
|
mBeganFromNonInteractive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GlobalKeyAction {
|
||||||
|
private ComponentName mComponentName;
|
||||||
|
private boolean mDispatchWhenNonInteractive;
|
||||||
|
GlobalKeyAction(String componentName, String dispatchWhenNonInteractive) {
|
||||||
|
mComponentName = ComponentName.unflattenFromString(componentName);
|
||||||
|
mDispatchWhenNonInteractive = Boolean.valueOf(dispatchWhenNonInteractive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void loadGlobalKeys(Context context) {
|
private void loadGlobalKeys(Context context) {
|
||||||
XmlResourceParser parser = null;
|
XmlResourceParser parser = null;
|
||||||
try {
|
try {
|
||||||
@@ -105,10 +137,12 @@ final class GlobalKeyManager {
|
|||||||
if (TAG_KEY.equals(element)) {
|
if (TAG_KEY.equals(element)) {
|
||||||
String keyCodeName = parser.getAttributeValue(null, ATTR_KEY_CODE);
|
String keyCodeName = parser.getAttributeValue(null, ATTR_KEY_CODE);
|
||||||
String componentName = parser.getAttributeValue(null, ATTR_COMPONENT);
|
String componentName = parser.getAttributeValue(null, ATTR_COMPONENT);
|
||||||
|
String dispatchWhenNonInteractive =
|
||||||
|
parser.getAttributeValue(null, ATTR_DISPATCH_WHEN_NON_INTERACTIVE);
|
||||||
int keyCode = KeyEvent.keyCodeFromString(keyCodeName);
|
int keyCode = KeyEvent.keyCodeFromString(keyCodeName);
|
||||||
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
|
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
|
||||||
mKeyMapping.put(keyCode, ComponentName.unflattenFromString(
|
mKeyMapping.put(keyCode, new GlobalKeyAction(
|
||||||
componentName));
|
componentName, dispatchWhenNonInteractive));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +172,9 @@ final class GlobalKeyManager {
|
|||||||
pw.print(prefix);
|
pw.print(prefix);
|
||||||
pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i)));
|
pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i)));
|
||||||
pw.print("=");
|
pw.print("=");
|
||||||
pw.println(mKeyMapping.valueAt(i).flattenToString());
|
pw.print(mKeyMapping.valueAt(i).mComponentName.flattenToString());
|
||||||
|
pw.print(",dispatchWhenNonInteractive=");
|
||||||
|
pw.println(mKeyMapping.valueAt(i).mDispatchWhenNonInteractive);
|
||||||
}
|
}
|
||||||
pw.print(prefix); pw.println("}");
|
pw.print(prefix); pw.println("}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3434,7 +3434,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
|||||||
// If the key would be handled globally, just return the result, don't worry about special
|
// If the key would be handled globally, just return the result, don't worry about special
|
||||||
// key processing.
|
// key processing.
|
||||||
if (isValidGlobalKey(keyCode)
|
if (isValidGlobalKey(keyCode)
|
||||||
&& mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {
|
&& 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) {
|
if (isWakeKey) {
|
||||||
wakeUpFromWakeKey(event);
|
wakeUpFromWakeKey(event);
|
||||||
}
|
}
|
||||||
@@ -3988,6 +3997,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
|||||||
Slog.e(TAG, "RemoteException when checking if dreaming", e);
|
Slog.e(TAG, "RemoteException when checking if dreaming", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, consume events since the user can't see what is being
|
// Otherwise, consume events since the user can't see what is being
|
||||||
// interacted with.
|
// interacted with.
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user