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.view.KeyEvent;
27 
28 /**
29  * Static class to handle listening to the headset media buttons.
30  */
31 public class HeadsetMediaButton extends CallsManagerListenerBase {
32 
33     // Types of media button presses
34     static final int SHORT_PRESS = 1;
35     static final int LONG_PRESS = 2;
36 
37     private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
38             .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
39             .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build();
40 
41     private static final int MSG_MEDIA_SESSION_INITIALIZE = 0;
42     private static final int MSG_MEDIA_SESSION_SET_ACTIVE = 1;
43 
44     private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
45         @Override
46         public boolean onMediaButtonEvent(Intent intent) {
47             KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
48             Log.v(this, "SessionCallback.onMediaButton()...  event = %s.", event);
49             if ((event != null) && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
50                 synchronized (mLock) {
51                     Log.v(this, "SessionCallback: HEADSETHOOK");
52                     boolean consumed = handleHeadsetHook(event);
53                     Log.v(this, "==> handleHeadsetHook(): consumed = %b.", consumed);
54                     return consumed;
55                 }
56             }
57             return true;
58         }
59     };
60 
61     private final Handler mMediaSessionHandler = new Handler(Looper.getMainLooper()) {
62         @Override
63         public void handleMessage(Message msg) {
64             switch (msg.what) {
65                 case MSG_MEDIA_SESSION_INITIALIZE: {
66                     MediaSession session = new MediaSession(
67                             mContext,
68                             HeadsetMediaButton.class.getSimpleName());
69                     session.setCallback(mSessionCallback);
70                     session.setFlags(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY
71                             | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
72                     session.setPlaybackToLocal(AUDIO_ATTRIBUTES);
73                     mSession = session;
74                     break;
75                 }
76                 case MSG_MEDIA_SESSION_SET_ACTIVE: {
77                     if (mSession != null) {
78                         boolean activate = msg.arg1 != 0;
79                         if (activate != mSession.isActive()) {
80                             mSession.setActive(activate);
81                         }
82                     }
83                     break;
84                 }
85                 default:
86                     break;
87             }
88         }
89     };
90 
91     private final Context mContext;
92     private final CallsManager mCallsManager;
93     private final TelecomSystem.SyncRoot mLock;
94     private MediaSession mSession;
95 
HeadsetMediaButton( Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock)96     public HeadsetMediaButton(
97             Context context,
98             CallsManager callsManager,
99             TelecomSystem.SyncRoot lock) {
100         mContext = context;
101         mCallsManager = callsManager;
102         mLock = lock;
103 
104         // Create a MediaSession but don't enable it yet. This is a
105         // replacement for MediaButtonReceiver
106         mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_INITIALIZE).sendToTarget();
107     }
108 
109     /**
110      * Handles the wired headset button while in-call.
111      *
112      * @return true if we consumed the event.
113      */
handleHeadsetHook(KeyEvent event)114     private boolean handleHeadsetHook(KeyEvent event) {
115         Log.d(this, "handleHeadsetHook()...%s %s", event.getAction(), event.getRepeatCount());
116 
117         if (event.isLongPress()) {
118             return mCallsManager.onMediaButton(LONG_PRESS);
119         } else if (event.getAction() == KeyEvent.ACTION_UP && event.getRepeatCount() == 0) {
120             return mCallsManager.onMediaButton(SHORT_PRESS);
121         }
122 
123         return true;
124     }
125 
126     /** ${inheritDoc} */
127     @Override
onCallAdded(Call call)128     public void onCallAdded(Call call) {
129         if (call.isExternalCall()) {
130             return;
131         }
132         mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
133     }
134 
135     /** ${inheritDoc} */
136     @Override
onCallRemoved(Call call)137     public void onCallRemoved(Call call) {
138         if (!mCallsManager.hasAnyCalls()) {
139             mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 0, 0).sendToTarget();
140         }
141     }
142 
143     /** ${inheritDoc} */
144     @Override
onExternalCallChanged(Call call, boolean isExternalCall)145     public void onExternalCallChanged(Call call, boolean isExternalCall) {
146         if (isExternalCall) {
147             onCallRemoved(call);
148         } else {
149             onCallAdded(call);
150         }
151     }
152 }
153