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