• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HeadsetMediaButton( Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock)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      */
handleCallMediaButton(KeyEvent event)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
onCallAdded(Call call)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
onCallRemoved(Call call)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
onExternalCallChanged(Call call, boolean isExternalCall)170     public void onExternalCallChanged(Call call, boolean isExternalCall) {
171         if (isExternalCall) {
172             onCallRemoved(call);
173         } else {
174             onCallAdded(call);
175         }
176     }
177 }
178