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 final class GlobalKeyManager { 44 45 private static final String TAG = "GlobalKeyManager"; 46 47 private static final String TAG_GLOBAL_KEYS = "global_keys"; 48 private static final String ATTR_VERSION = "version"; 49 private static final String TAG_KEY = "key"; 50 private static final String ATTR_KEY_CODE = "keyCode"; 51 private static final String ATTR_COMPONENT = "component"; 52 53 private static final int GLOBAL_KEY_FILE_VERSION = 1; 54 55 private SparseArray<ComponentName> mKeyMapping; 56 GlobalKeyManager(Context context)57 public GlobalKeyManager(Context context) { 58 mKeyMapping = new SparseArray<ComponentName>(); 59 loadGlobalKeys(context); 60 } 61 62 /** 63 * Broadcasts an intent if the keycode is part of the global key mapping. 64 * 65 * @param context context used to broadcast the event 66 * @param keyCode keyCode which triggered this function 67 * @param event keyEvent which trigged this function 68 * @return {@code true} if this was handled 69 */ handleGlobalKey(Context context, int keyCode, KeyEvent event)70 boolean handleGlobalKey(Context context, int keyCode, KeyEvent event) { 71 if (mKeyMapping.size() > 0) { 72 ComponentName component = mKeyMapping.get(keyCode); 73 if (component != null) { 74 Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON) 75 .setComponent(component) 76 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND) 77 .putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(event)); 78 context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null); 79 return true; 80 } 81 } 82 return false; 83 } 84 85 /** 86 * Returns {@code true} if the key will be handled globally. 87 */ shouldHandleGlobalKey(int keyCode, KeyEvent event)88 boolean shouldHandleGlobalKey(int keyCode, KeyEvent event) { 89 return mKeyMapping.get(keyCode) != null; 90 } 91 loadGlobalKeys(Context context)92 private void loadGlobalKeys(Context context) { 93 XmlResourceParser parser = null; 94 try { 95 parser = context.getResources().getXml(com.android.internal.R.xml.global_keys); 96 XmlUtils.beginDocument(parser, TAG_GLOBAL_KEYS); 97 int version = parser.getAttributeIntValue(null, ATTR_VERSION, 0); 98 if (GLOBAL_KEY_FILE_VERSION == version) { 99 while (true) { 100 XmlUtils.nextElement(parser); 101 String element = parser.getName(); 102 if (element == null) { 103 break; 104 } 105 if (TAG_KEY.equals(element)) { 106 String keyCodeName = parser.getAttributeValue(null, ATTR_KEY_CODE); 107 String componentName = parser.getAttributeValue(null, ATTR_COMPONENT); 108 int keyCode = KeyEvent.keyCodeFromString(keyCodeName); 109 if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 110 mKeyMapping.put(keyCode, ComponentName.unflattenFromString( 111 componentName)); 112 } 113 } 114 } 115 } 116 } catch (Resources.NotFoundException e) { 117 Log.w(TAG, "global keys file not found", e); 118 } catch (XmlPullParserException e) { 119 Log.w(TAG, "XML parser exception reading global keys file", e); 120 } catch (IOException e) { 121 Log.w(TAG, "I/O exception reading global keys file", e); 122 } finally { 123 if (parser != null) { 124 parser.close(); 125 } 126 } 127 } 128 dump(String prefix, PrintWriter pw)129 public void dump(String prefix, PrintWriter pw) { 130 final int numKeys = mKeyMapping.size(); 131 if (numKeys == 0) { 132 pw.print(prefix); pw.println("mKeyMapping.size=0"); 133 return; 134 } 135 pw.print(prefix); pw.println("mKeyMapping={"); 136 for (int i = 0; i < numKeys; ++i) { 137 pw.print(" "); 138 pw.print(prefix); 139 pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i))); 140 pw.print("="); 141 pw.println(mKeyMapping.valueAt(i).flattenToString()); 142 } 143 pw.print(prefix); pw.println("}"); 144 } 145 } 146