1 /* 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.android.server.policy; 17 18 import android.content.ComponentName; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.res.Resources; 22 import android.content.res.XmlResourceParser; 23 import android.os.UserHandle; 24 import android.util.Log; 25 import android.util.SparseArray; 26 import android.view.KeyEvent; 27 28 import com.android.internal.util.XmlUtils; 29 30 import org.xmlpull.v1.XmlPullParserException; 31 32 import java.io.IOException; 33 import java.io.PrintWriter; 34 35 /** 36 * Stores a mapping of global keys. 37 * <p> 38 * A global key will NOT go to the foreground application and instead only ever be sent via targeted 39 * broadcast to the specified component. The action of the intent will be 40 * {@link Intent#ACTION_GLOBAL_BUTTON} and the KeyEvent will be included in the intent with 41 * {@link Intent#EXTRA_KEY_EVENT}. 42 * 43 * Use {@link GlobalKeyIntent} to get detail information from received {@link Intent}, includes 44 * {@link KeyEvent} and the information about if the key is dispatched from non-interactive mode. 45 */ 46 final class GlobalKeyManager { 47 48 private static final String TAG = "GlobalKeyManager"; 49 50 private static final String TAG_GLOBAL_KEYS = "global_keys"; 51 private static final String ATTR_VERSION = "version"; 52 private static final String TAG_KEY = "key"; 53 private static final String ATTR_KEY_CODE = "keyCode"; 54 private static final String ATTR_COMPONENT = "component"; 55 private static final String ATTR_DISPATCH_WHEN_NON_INTERACTIVE = "dispatchWhenNonInteractive"; 56 57 private static final int GLOBAL_KEY_FILE_VERSION = 1; 58 59 private SparseArray<GlobalKeyAction> mKeyMapping; 60 private boolean mBeganFromNonInteractive = false; 61 GlobalKeyManager(Context context)62 public GlobalKeyManager(Context context) { 63 mKeyMapping = new SparseArray<>(); 64 loadGlobalKeys(context); 65 } 66 67 /** 68 * Broadcasts an intent if the keycode is part of the global key mapping. 69 * 70 * @param context context used to broadcast the event 71 * @param keyCode keyCode which triggered this function 72 * @param event keyEvent which trigged this function 73 * @return {@code true} if this was handled 74 */ handleGlobalKey(Context context, int keyCode, KeyEvent event)75 boolean handleGlobalKey(Context context, int keyCode, KeyEvent event) { 76 if (mKeyMapping.size() > 0) { 77 GlobalKeyAction action = mKeyMapping.get(keyCode); 78 if (action != null) { 79 final Intent intent = new GlobalKeyIntent(action.mComponentName, event, 80 mBeganFromNonInteractive).getIntent(); 81 context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null); 82 83 if (event.getAction() == KeyEvent.ACTION_UP) { 84 mBeganFromNonInteractive = false; 85 } 86 return true; 87 } 88 } 89 return false; 90 } 91 92 /** 93 * Returns {@code true} if the key will be handled globally. 94 */ shouldHandleGlobalKey(int keyCode)95 boolean shouldHandleGlobalKey(int keyCode) { 96 return mKeyMapping.get(keyCode) != null; 97 } 98 99 /** 100 * Returns {@code true} if the key will be handled globally. 101 */ shouldDispatchFromNonInteractive(int keyCode)102 boolean shouldDispatchFromNonInteractive(int keyCode) { 103 final GlobalKeyAction action = mKeyMapping.get(keyCode); 104 if (action == null) { 105 return false; 106 } 107 108 return action.mDispatchWhenNonInteractive; 109 } 110 setBeganFromNonInteractive()111 void setBeganFromNonInteractive() { 112 mBeganFromNonInteractive = true; 113 } 114 115 class GlobalKeyAction { 116 private ComponentName mComponentName; 117 private boolean mDispatchWhenNonInteractive; GlobalKeyAction(String componentName, String dispatchWhenNonInteractive)118 GlobalKeyAction(String componentName, String dispatchWhenNonInteractive) { 119 mComponentName = ComponentName.unflattenFromString(componentName); 120 mDispatchWhenNonInteractive = Boolean.valueOf(dispatchWhenNonInteractive); 121 } 122 } 123 loadGlobalKeys(Context context)124 private void loadGlobalKeys(Context context) { 125 XmlResourceParser parser = null; 126 try { 127 parser = context.getResources().getXml(com.android.internal.R.xml.global_keys); 128 XmlUtils.beginDocument(parser, TAG_GLOBAL_KEYS); 129 int version = parser.getAttributeIntValue(null, ATTR_VERSION, 0); 130 if (GLOBAL_KEY_FILE_VERSION == version) { 131 while (true) { 132 XmlUtils.nextElement(parser); 133 String element = parser.getName(); 134 if (element == null) { 135 break; 136 } 137 if (TAG_KEY.equals(element)) { 138 String keyCodeName = parser.getAttributeValue(null, ATTR_KEY_CODE); 139 String componentName = parser.getAttributeValue(null, ATTR_COMPONENT); 140 String dispatchWhenNonInteractive = 141 parser.getAttributeValue(null, ATTR_DISPATCH_WHEN_NON_INTERACTIVE); 142 int keyCode = KeyEvent.keyCodeFromString(keyCodeName); 143 if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 144 mKeyMapping.put(keyCode, new GlobalKeyAction( 145 componentName, dispatchWhenNonInteractive)); 146 } 147 } 148 } 149 } 150 } catch (Resources.NotFoundException e) { 151 Log.w(TAG, "global keys file not found", e); 152 } catch (XmlPullParserException e) { 153 Log.w(TAG, "XML parser exception reading global keys file", e); 154 } catch (IOException e) { 155 Log.w(TAG, "I/O exception reading global keys file", e); 156 } finally { 157 if (parser != null) { 158 parser.close(); 159 } 160 } 161 } 162 dump(String prefix, PrintWriter pw)163 public void dump(String prefix, PrintWriter pw) { 164 final int numKeys = mKeyMapping.size(); 165 if (numKeys == 0) { 166 pw.print(prefix); pw.println("mKeyMapping.size=0"); 167 return; 168 } 169 pw.print(prefix); pw.println("mKeyMapping={"); 170 for (int i = 0; i < numKeys; ++i) { 171 pw.print(" "); 172 pw.print(prefix); 173 pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i))); 174 pw.print("="); 175 pw.print(mKeyMapping.valueAt(i).mComponentName.flattenToString()); 176 pw.print(",dispatchWhenNonInteractive="); 177 pw.println(mKeyMapping.valueAt(i).mDispatchWhenNonInteractive); 178 } 179 pw.print(prefix); pw.println("}"); 180 } 181 } 182