blob: e08c004866ea1432f6648042d83a1f457ece5437 [file] [log] [blame]
Justin Kohd378ad72013-04-01 12:18:26 -07001/*
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
Jorim Jaggib10e33f2015-02-04 21:57:40 +010016package com.android.server.policy;
Justin Kohd378ad72013-04-01 12:18:26 -070017
18import android.content.ComponentName;
19import android.content.Context;
20import android.content.Intent;
21import android.content.res.Resources;
22import android.content.res.XmlResourceParser;
23import android.os.UserHandle;
24import android.util.Log;
25import android.util.SparseArray;
26import android.view.KeyEvent;
27
28import com.android.internal.util.XmlUtils;
29
30import org.xmlpull.v1.XmlPullParserException;
31
32import java.io.IOException;
Tim Kilbournd7c0c2e2014-05-29 16:08:10 -070033import java.io.PrintWriter;
Justin Kohd378ad72013-04-01 12:18:26 -070034
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 */
43final 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
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 */
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)
Justin Kohb0b823f2014-10-09 13:33:19 -070076 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
Justin Kohd378ad72013-04-01 12:18:26 -070077 .putExtra(Intent.EXTRA_KEY_EVENT, 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 */
88 boolean shouldHandleGlobalKey(int keyCode, KeyEvent event) {
89 return mKeyMapping.get(keyCode) != null;
90 }
91
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 }
Tim Kilbournd7c0c2e2014-05-29 16:08:10 -0700128
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 }
Justin Kohd378ad72013-04-01 12:18:26 -0700145}