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