Home | History | Annotate | Download | only in telecom
      1 /*
      2  * Copyright 2014, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.telecom;
     18 
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.media.AudioAttributes;
     22 import android.media.session.MediaSession;
     23 import android.os.Handler;
     24 import android.os.Looper;
     25 import android.os.Message;
     26 import android.telecom.Log;
     27 import android.view.KeyEvent;
     28 
     29 /**
     30  * Static class to handle listening to the headset media buttons.
     31  */
     32 public class HeadsetMediaButton extends CallsManagerListenerBase {
     33 
     34     // Types of media button presses
     35     static final int SHORT_PRESS = 1;
     36     static final int LONG_PRESS = 2;
     37 
     38     private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
     39             .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
     40             .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build();
     41 
     42     private static final int MSG_MEDIA_SESSION_INITIALIZE = 0;
     43     private static final int MSG_MEDIA_SESSION_SET_ACTIVE = 1;
     44 
     45     private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
     46         @Override
     47         public boolean onMediaButtonEvent(Intent intent) {
     48             try {
     49                 Log.startSession("HMB.oMBE");
     50                 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
     51                 Log.v(this, "SessionCallback.onMediaButton()...  event = %s.", event);
     52                 if ((event != null) && ((event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK) ||
     53                         (event.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))) {
     54                     synchronized (mLock) {
     55                         Log.v(this, "SessionCallback: HEADSETHOOK/MEDIA_PLAY_PAUSE");
     56                         boolean consumed = handleCallMediaButton(event);
     57                         Log.v(this, "==> handleCallMediaButton(): consumed = %b.", consumed);
     58                         return consumed;
     59                     }
     60                 }
     61                 return true;
     62             } finally {
     63                 Log.endSession();
     64             }
     65         }
     66     };
     67 
     68     private final Handler mMediaSessionHandler = new Handler(Looper.getMainLooper()) {
     69         @Override
     70         public void handleMessage(Message msg) {
     71             switch (msg.what) {
     72                 case MSG_MEDIA_SESSION_INITIALIZE: {
     73                     MediaSession session = new MediaSession(
     74                             mContext,
     75                             HeadsetMediaButton.class.getSimpleName());
     76                     session.setCallback(mSessionCallback);
     77                     session.setFlags(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY
     78                             | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
     79                     session.setPlaybackToLocal(AUDIO_ATTRIBUTES);
     80                     mSession = session;
     81                     break;
     82                 }
     83                 case MSG_MEDIA_SESSION_SET_ACTIVE: {
     84                     if (mSession != null) {
     85                         boolean activate = msg.arg1 != 0;
     86                         if (activate != mSession.isActive()) {
     87                             mSession.setActive(activate);
     88                         }
     89                     }
     90                     break;
     91                 }
     92                 default:
     93                     break;
     94             }
     95         }
     96     };
     97 
     98     private final Context mContext;
     99     private final CallsManager mCallsManager;
    100     private final TelecomSystem.SyncRoot mLock;
    101     private MediaSession mSession;
    102     private KeyEvent mLastHookEvent;
    103 
    104     public HeadsetMediaButton(
    105             Context context,
    106             CallsManager callsManager,
    107             TelecomSystem.SyncRoot lock) {
    108         mContext = context;
    109         mCallsManager = callsManager;
    110         mLock = lock;
    111 
    112         // Create a MediaSession but don't enable it yet. This is a
    113         // replacement for MediaButtonReceiver
    114         mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_INITIALIZE).sendToTarget();
    115     }
    116 
    117     /**
    118      * Handles the wired headset button while in-call.
    119      *
    120      * @return true if we consumed the event.
    121      */
    122     private boolean handleCallMediaButton(KeyEvent event) {
    123         Log.d(this, "handleCallMediaButton()...%s %s", event.getAction(), event.getRepeatCount());
    124 
    125         // Save ACTION_DOWN Event temporarily.
    126         if (event.getAction() == KeyEvent.ACTION_DOWN) {
    127             mLastHookEvent = event;
    128         }
    129 
    130         if (event.isLongPress()) {
    131             return mCallsManager.onMediaButton(LONG_PRESS);
    132         } else if (event.getAction() == KeyEvent.ACTION_UP) {
    133             // We should not judge SHORT_PRESS by ACTION_UP event repeatCount, because it always
    134             // return 0.
    135             // Actually ACTION_DOWN event repeatCount only increases when LONG_PRESS performed.
    136             if (mLastHookEvent != null && mLastHookEvent.getRepeatCount() == 0) {
    137                 return mCallsManager.onMediaButton(SHORT_PRESS);
    138             }
    139         }
    140 
    141         if (event.getAction() != KeyEvent.ACTION_DOWN) {
    142             mLastHookEvent = null;
    143         }
    144 
    145         return true;
    146     }
    147 
    148     /** ${inheritDoc} */
    149     @Override
    150     public void onCallAdded(Call call) {
    151         if (call.isExternalCall()) {
    152             return;
    153         }
    154         mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
    155     }
    156 
    157     /** ${inheritDoc} */
    158     @Override
    159     public void onCallRemoved(Call call) {
    160         if (call.isExternalCall()) {
    161             return;
    162         }
    163         if (!mCallsManager.hasAnyCalls()) {
    164             mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 0, 0).sendToTarget();
    165         }
    166     }
    167 
    168     /** ${inheritDoc} */
    169     @Override
    170     public void onExternalCallChanged(Call call, boolean isExternalCall) {
    171         if (isExternalCall) {
    172             onCallRemoved(call);
    173         } else {
    174             onCallAdded(call);
    175         }
    176     }
    177 }
    178