blob: ec77289a219f60c411c18b4aa5e860c68c8f2e3f [file] [log] [blame]
Santos Cordondeb8c892014-05-30 01:38:03 -07001/*
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
Tyler Gunn7cc70b42014-09-12 22:17:27 -070017package com.android.server.telecom;
Santos Cordondeb8c892014-05-30 01:38:03 -070018
Santos Cordondeb8c892014-05-30 01:38:03 -070019import android.content.Context;
20import android.content.Intent;
RoboErik07920f62014-07-21 15:19:41 -070021import android.media.AudioAttributes;
Santos Cordondeb8c892014-05-30 01:38:03 -070022import android.media.session.MediaSession;
Ihab Awad731369c2015-05-19 09:23:21 -070023import android.os.Handler;
24import android.os.Looper;
25import android.os.Message;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070026import android.telecom.Log;
Santos Cordondeb8c892014-05-30 01:38:03 -070027import android.view.KeyEvent;
28
29/**
30 * Static class to handle listening to the headset media buttons.
31 */
Ihab Awad8de76912015-02-17 12:25:52 -080032public class HeadsetMediaButton extends CallsManagerListenerBase {
Santos Cordondeb8c892014-05-30 01:38:03 -070033
Santos Cordondeb8c892014-05-30 01:38:03 -070034 // Types of media button presses
35 static final int SHORT_PRESS = 1;
36 static final int LONG_PRESS = 2;
37
RoboErik07920f62014-07-21 15:19:41 -070038 private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
39 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
40 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build();
41
Ihab Awad731369c2015-05-19 09:23:21 -070042 private static final int MSG_MEDIA_SESSION_INITIALIZE = 0;
43 private static final int MSG_MEDIA_SESSION_SET_ACTIVE = 1;
44
Santos Cordondeb8c892014-05-30 01:38:03 -070045 private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
46 @Override
RoboErika6e9d752014-08-07 16:10:08 -070047 public boolean onMediaButtonEvent(Intent intent) {
Hall Liua1483242017-11-09 14:27:49 -080048 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 }
Ihab Awad731369c2015-05-19 09:23:21 -070060 }
Hall Liua1483242017-11-09 14:27:49 -080061 return true;
62 } finally {
63 Log.endSession();
Santos Cordondeb8c892014-05-30 01:38:03 -070064 }
65 }
66 };
67
Ihab Awad731369c2015-05-19 09:23:21 -070068 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;
Santos Cordondeb8c892014-05-30 01:38:03 -070099 private final CallsManager mCallsManager;
Ihab Awad731369c2015-05-19 09:23:21 -0700100 private final TelecomSystem.SyncRoot mLock;
101 private MediaSession mSession;
yifan.baic73ca8d2016-04-21 16:19:12 +0800102 private KeyEvent mLastHookEvent;
Santos Cordondeb8c892014-05-30 01:38:03 -0700103
Ihab Awad731369c2015-05-19 09:23:21 -0700104 public HeadsetMediaButton(
105 Context context,
106 CallsManager callsManager,
107 TelecomSystem.SyncRoot lock) {
108 mContext = context;
Santos Cordondeb8c892014-05-30 01:38:03 -0700109 mCallsManager = callsManager;
Ihab Awad731369c2015-05-19 09:23:21 -0700110 mLock = lock;
Santos Cordondeb8c892014-05-30 01:38:03 -0700111
RoboErikb771c432014-07-15 12:44:33 -0700112 // Create a MediaSession but don't enable it yet. This is a
Santos Cordondeb8c892014-05-30 01:38:03 -0700113 // replacement for MediaButtonReceiver
Ihab Awad731369c2015-05-19 09:23:21 -0700114 mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_INITIALIZE).sendToTarget();
Santos Cordondeb8c892014-05-30 01:38:03 -0700115 }
116
117 /**
118 * Handles the wired headset button while in-call.
119 *
120 * @return true if we consumed the event.
121 */
Andrew Chant04152202017-05-08 10:28:13 -0700122 private boolean handleCallMediaButton(KeyEvent event) {
123 Log.d(this, "handleCallMediaButton()...%s %s", event.getAction(), event.getRepeatCount());
Santos Cordondeb8c892014-05-30 01:38:03 -0700124
yifan.baic73ca8d2016-04-21 16:19:12 +0800125 // Save ACTION_DOWN Event temporarily.
126 if (event.getAction() == KeyEvent.ACTION_DOWN) {
127 mLastHookEvent = event;
128 }
129
Santos Cordondeb8c892014-05-30 01:38:03 -0700130 if (event.isLongPress()) {
131 return mCallsManager.onMediaButton(LONG_PRESS);
yifan.baic73ca8d2016-04-21 16:19:12 +0800132 } 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;
Santos Cordondeb8c892014-05-30 01:38:03 -0700143 }
144
145 return true;
146 }
Santos Cordon81289982014-06-03 16:03:08 -0700147
148 /** ${inheritDoc} */
149 @Override
150 public void onCallAdded(Call call) {
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700151 if (call.isExternalCall()) {
152 return;
153 }
Ihab Awad731369c2015-05-19 09:23:21 -0700154 mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
Santos Cordon81289982014-06-03 16:03:08 -0700155 }
156
157 /** ${inheritDoc} */
158 @Override
159 public void onCallRemoved(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700160 if (call.isExternalCall()) {
161 return;
162 }
Santos Cordon81289982014-06-03 16:03:08 -0700163 if (!mCallsManager.hasAnyCalls()) {
Ihab Awad731369c2015-05-19 09:23:21 -0700164 mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 0, 0).sendToTarget();
Santos Cordon81289982014-06-03 16:03:08 -0700165 }
166 }
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700167
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 }
Santos Cordondeb8c892014-05-30 01:38:03 -0700177}