Home | History | Annotate | Download | only in policy
      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 
     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)
     76                         .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
     77                         .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     }
    128 
    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