content: /*
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.server.policy;
  
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.res.Resources;
  import android.content.res.XmlResourceParser;
  import android.os.UserHandle;
  import android.util.Log;
  import android.util.SparseArray;
  import android.view.KeyEvent;
  
  import com.android.internal.util.XmlUtils;
  
  import org.xmlpull.v1.XmlPullParserException;
  
  import java.io.IOException;
  import java.io.PrintWriter;
  
  /**
   * Stores a mapping of global keys.
   * <p>
   * A global key will NOT go to the foreground application and instead only ever be sent via targeted
   * broadcast to the specified component. The action of the intent will be
   * {@link Intent#ACTION_GLOBAL_BUTTON} and the KeyEvent will be included in the intent with
   * {@link Intent#EXTRA_KEY_EVENT}.
   */
  final class GlobalKeyManager {
  
      private static final String TAG = "GlobalKeyManager";
  
      private static final String TAG_GLOBAL_KEYS = "global_keys";
      private static final String ATTR_VERSION = "version";
      private static final String TAG_KEY = "key";
      private static final String ATTR_KEY_CODE = "keyCode";
      private static final String ATTR_COMPONENT = "component";
  
      private static final int GLOBAL_KEY_FILE_VERSION = 1;
  
      private SparseArray<ComponentName> mKeyMapping;
  
      public GlobalKeyManager(Context context) {
          mKeyMapping = new SparseArray<ComponentName>();
          loadGlobalKeys(context);
      }
  
      /**
       * Broadcasts an intent if the keycode is part of the global key mapping.
       *
       * @param context context used to broadcast the event
       * @param keyCode keyCode which triggered this function
       * @param event keyEvent which trigged this function
       * @return {@code true} if this was handled
       */
      boolean handleGlobalKey(Context context, int keyCode, KeyEvent event) {
          if (mKeyMapping.size() > 0) {
              ComponentName component = mKeyMapping.get(keyCode);
              if (component != null) {
                  Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON)
                          .setComponent(component)
                          .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                          .putExtra(Intent.EXTRA_KEY_EVENT, event);
                  context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null);
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Returns {@code true} if the key will be handled globally.
       */
      boolean shouldHandleGlobalKey(int keyCode, KeyEvent event) {
          return mKeyMapping.get(keyCode) != null;
      }
  
      private void loadGlobalKeys(Context context) {
          XmlResourceParser parser = null;
          try {
              parser = context.getResources().getXml(com.android.internal.R.xml.global_keys);
              XmlUtils.beginDocument(parser, TAG_GLOBAL_KEYS);
              int version = parser.getAttributeIntValue(null, ATTR_VERSION, 0);
              if (GLOBAL_KEY_FILE_VERSION == version) {
                  while (true) {
                      XmlUtils.nextElement(parser);
                      String element = parser.getName();
                      if (element == null) {
                          break;
                      }
                      if (TAG_KEY.equals(element)) {
                          String keyCodeName = parser.getAttributeValue(null, ATTR_KEY_CODE);
                          String componentName = parser.getAttributeValue(null, ATTR_COMPONENT);
                          int keyCode = KeyEvent.keyCodeFromString(keyCodeName);
                          if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
                              mKeyMapping.put(keyCode, ComponentName.unflattenFromString(
                                      componentName));
                          }
                      }
                  }
              }
          } catch (Resources.NotFoundException e) {
              Log.w(TAG, "global keys file not found", e);
          } catch (XmlPullParserException e) {
              Log.w(TAG, "XML parser exception reading global keys file", e);
          } catch (IOException e) {
              Log.w(TAG, "I/O exception reading global keys file", e);
          } finally {
              if (parser != null) {
                  parser.close();
              }
          }
      }
  
      public void dump(String prefix, PrintWriter pw) {
          final int numKeys = mKeyMapping.size();
          if (numKeys == 0) {
              pw.print(prefix); pw.println("mKeyMapping.size=0");
              return;
          }
          pw.print(prefix); pw.println("mKeyMapping={");
          for (int i = 0; i < numKeys; ++i) {
              pw.print("  ");
              pw.print(prefix);
              pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i)));
              pw.print("=");
              pw.println(mKeyMapping.valueAt(i).flattenToString());
          }
          pw.print(prefix); pw.println("}");
      }
  }
  