blob: 649a9d16e944b71e14a565aaee1a78fac8a11c16 [file] [log] [blame]
Pengquan Mengb1a11c82017-11-21 17:52:42 -08001/*
2 * Copyright 2017 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
17package com.android.server.telecom;
18
19import android.annotation.Nullable;
Tyler Gunnf4f05392018-03-26 18:57:59 +000020import android.content.ComponentName;
Pengquan Mengb1a11c82017-11-21 17:52:42 -080021import android.os.Handler;
22import android.os.Looper;
23import android.os.Message;
Pengquan Meng606ff7f2017-12-20 16:13:04 -080024import android.telecom.Log;
Pengquan Meng13e0da62018-02-27 11:46:42 -080025import android.telecom.Logging.Session;
26import android.text.TextUtils;
27
Pengquan Mengb1a11c82017-11-21 17:52:42 -080028import com.android.internal.annotations.VisibleForTesting;
29
30import java.util.ArrayList;
31import java.util.List;
32import java.util.Objects;
33import java.util.stream.Collectors;
34
35public class ConnectionServiceFocusManager {
Pengquan Meng606ff7f2017-12-20 16:13:04 -080036 private static final String TAG = "ConnectionSvrFocusMgr";
Pengquan Mengb1a11c82017-11-21 17:52:42 -080037
Pengquan Meng606ff7f2017-12-20 16:13:04 -080038 /** Factory interface used to create the {@link ConnectionServiceFocusManager} instance. */
39 public interface ConnectionServiceFocusManagerFactory {
40 ConnectionServiceFocusManager create(CallsManagerRequester requester, Looper looper);
41 }
Pengquan Mengb1a11c82017-11-21 17:52:42 -080042
43 /**
44 * Interface used by ConnectionServiceFocusManager to communicate with
45 * {@link ConnectionServiceWrapper}.
46 */
47 public interface ConnectionServiceFocus {
48 /**
49 * Notifies the {@link android.telecom.ConnectionService} that it has lose the connection
50 * service focus. It should release all call resource i.e camera, audio once it lost the
51 * focus.
52 */
53 void connectionServiceFocusLost();
54
55 /**
56 * Notifies the {@link android.telecom.ConnectionService} that it has gain the connection
57 * service focus. It can request the call resource i.e camera, audio as they expected to be
58 * free at the moment.
59 */
60 void connectionServiceFocusGained();
61
62 /**
63 * Sets the ConnectionServiceFocusListener.
64 *
65 * @see {@link ConnectionServiceFocusListener}.
66 */
67 void setConnectionServiceFocusListener(ConnectionServiceFocusListener listener);
Tyler Gunnf4f05392018-03-26 18:57:59 +000068
69 /**
70 * Get the {@link ComponentName} of the ConnectionService for logging purposes.
71 * @return the {@link ComponentName}.
72 */
73 ComponentName getComponentName();
Pengquan Mengb1a11c82017-11-21 17:52:42 -080074 }
75
76 /**
77 * Interface used to receive the changed of {@link android.telecom.ConnectionService} that
78 * ConnectionServiceFocusManager cares about.
79 */
80 public interface ConnectionServiceFocusListener {
81 /**
82 * Calls when {@link android.telecom.ConnectionService} has released the call resource. This
83 * usually happen after the {@link android.telecom.ConnectionService} lost the focus.
84 *
85 * @param connectionServiceFocus the {@link android.telecom.ConnectionService} that released
86 * the call resources.
87 */
88 void onConnectionServiceReleased(ConnectionServiceFocus connectionServiceFocus);
89
90 /**
91 * Calls when {@link android.telecom.ConnectionService} is disconnected.
92 *
93 * @param connectionServiceFocus the {@link android.telecom.ConnectionService} which is
94 * disconnected.
95 */
96 void onConnectionServiceDeath(ConnectionServiceFocus connectionServiceFocus);
97 }
98
99 /**
100 * Interface define to expose few information of {@link Call} that ConnectionServiceFocusManager
101 * cares about.
102 */
103 public interface CallFocus {
104 /**
105 * Returns the ConnectionService associated with the call.
106 */
107 ConnectionServiceFocus getConnectionServiceWrapper();
108
109 /**
110 * Returns the state of the call.
111 *
112 * @see {@link CallState}
113 */
114 int getState();
115 }
116
117 /** Interface define a call back for focus request event. */
118 public interface RequestFocusCallback {
119 /**
120 * Invokes after the focus request is done.
121 *
122 * @param call the call associated with the focus request.
123 */
124 void onRequestFocusDone(CallFocus call);
125 }
126
127 /**
128 * Interface define to allow the ConnectionServiceFocusManager to communicate with
129 * {@link CallsManager}.
130 */
131 public interface CallsManagerRequester {
132 /**
133 * Requests {@link CallsManager} to disconnect a {@link ConnectionServiceFocus}. This
134 * usually happen when the connection service doesn't respond to focus lost event.
135 */
136 void releaseConnectionService(ConnectionServiceFocus connectionService);
137
138 /**
139 * Sets the {@link com.android.server.telecom.CallsManager.CallsManagerListener} to listen
140 * the call event that ConnectionServiceFocusManager cares about.
141 */
142 void setCallsManagerListener(CallsManager.CallsManagerListener listener);
143 }
144
145 private static final int[] PRIORITY_FOCUS_CALL_STATE = new int[] {
146 CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING
147 };
148
149 private static final int MSG_REQUEST_FOCUS = 1;
150 private static final int MSG_RELEASE_CONNECTION_FOCUS = 2;
151 private static final int MSG_RELEASE_FOCUS_TIMEOUT = 3;
152 private static final int MSG_CONNECTION_SERVICE_DEATH = 4;
153 private static final int MSG_ADD_CALL = 5;
154 private static final int MSG_REMOVE_CALL = 6;
155 private static final int MSG_CALL_STATE_CHANGED = 7;
156
157 @VisibleForTesting
158 public static final int RELEASE_FOCUS_TIMEOUT_MS = 5000;
159
160 private final List<CallFocus> mCalls;
161
162 private final CallsManagerListenerBase mCallsManagerListener =
163 new CallsManagerListenerBase() {
164 @Override
165 public void onCallAdded(Call call) {
166 if (callShouldBeIgnored(call)) {
167 return;
168 }
169
Pengquan Meng13e0da62018-02-27 11:46:42 -0800170 mEventHandler
171 .obtainMessage(MSG_ADD_CALL,
172 new MessageArgs(
173 Log.createSubsession(),
174 "CSFM.oCA",
175 call))
176 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800177 }
178
179 @Override
180 public void onCallRemoved(Call call) {
181 if (callShouldBeIgnored(call)) {
182 return;
183 }
184
Pengquan Meng13e0da62018-02-27 11:46:42 -0800185 mEventHandler
186 .obtainMessage(MSG_REMOVE_CALL,
187 new MessageArgs(
188 Log.createSubsession(),
189 "CSFM.oCR",
190 call))
191 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800192 }
193
194 @Override
195 public void onCallStateChanged(Call call, int oldState, int newState) {
196 if (callShouldBeIgnored(call)) {
197 return;
198 }
199
Pengquan Meng13e0da62018-02-27 11:46:42 -0800200 mEventHandler
201 .obtainMessage(MSG_CALL_STATE_CHANGED, oldState, newState,
202 new MessageArgs(
203 Log.createSubsession(),
204 "CSFM.oCSS",
205 call))
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800206 .sendToTarget();
207 }
208
209 @Override
210 public void onExternalCallChanged(Call call, boolean isExternalCall) {
211 if (isExternalCall) {
Pengquan Meng13e0da62018-02-27 11:46:42 -0800212 mEventHandler
213 .obtainMessage(MSG_REMOVE_CALL,
214 new MessageArgs(
215 Log.createSubsession(),
216 "CSFM.oECC",
217 call))
218 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800219 } else {
Pengquan Meng13e0da62018-02-27 11:46:42 -0800220 mEventHandler
221 .obtainMessage(MSG_ADD_CALL,
222 new MessageArgs(
223 Log.createSubsession(),
224 "CSFM.oECC",
225 call))
226 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800227 }
228 }
229
230 boolean callShouldBeIgnored(Call call) {
231 return call.isExternalCall();
232 }
233 };
234
235 private final ConnectionServiceFocusListener mConnectionServiceFocusListener =
236 new ConnectionServiceFocusListener() {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800237 @Override
238 public void onConnectionServiceReleased(
239 ConnectionServiceFocus connectionServiceFocus) {
240 mEventHandler
Pengquan Meng13e0da62018-02-27 11:46:42 -0800241 .obtainMessage(MSG_RELEASE_CONNECTION_FOCUS,
242 new MessageArgs(
243 Log.createSubsession(),
244 "CSFM.oCSR",
245 connectionServiceFocus))
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800246 .sendToTarget();
247 }
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800248
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800249 @Override
250 public void onConnectionServiceDeath(
251 ConnectionServiceFocus connectionServiceFocus) {
252 mEventHandler
Pengquan Meng13e0da62018-02-27 11:46:42 -0800253 .obtainMessage(MSG_CONNECTION_SERVICE_DEATH,
254 new MessageArgs(
255 Log.createSubsession(),
256 "CSFM.oCSD",
257 connectionServiceFocus))
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800258 .sendToTarget();
259 }
260 };
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800261
262 private ConnectionServiceFocus mCurrentFocus;
263 private CallFocus mCurrentFocusCall;
264 private CallsManagerRequester mCallsManagerRequester;
265 private FocusRequest mCurrentFocusRequest;
266 private FocusManagerHandler mEventHandler;
267
268 public ConnectionServiceFocusManager(
269 CallsManagerRequester callsManagerRequester, Looper looper) {
270 mCallsManagerRequester = callsManagerRequester;
271 mCallsManagerRequester.setCallsManagerListener(mCallsManagerListener);
272 mEventHandler = new FocusManagerHandler(looper);
273 mCalls = new ArrayList<>();
274 }
275
276 /**
277 * Requests the call focus for the given call. The {@code callback} will be invoked once
278 * the request is done.
279 * @param focus the call need to be focus.
280 * @param callback the callback associated with this request.
281 */
282 public void requestFocus(CallFocus focus, RequestFocusCallback callback) {
Pengquan Meng13e0da62018-02-27 11:46:42 -0800283 mEventHandler.obtainMessage(MSG_REQUEST_FOCUS,
284 new MessageArgs(
285 Log.createSubsession(),
286 "CSFM.rF",
287 new FocusRequest(focus, callback)))
288 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800289 }
290
291 /**
292 * Returns the current focus call. The {@link android.telecom.ConnectionService} of the focus
293 * call is the current connection service focus. Also the state of the focus call must be one
294 * of {@link #PRIORITY_FOCUS_CALL_STATE}.
295 */
296 public CallFocus getCurrentFocusCall() {
297 return mCurrentFocusCall;
298 }
299
300 /** Returns the current connection service focus. */
301 public ConnectionServiceFocus getCurrentFocusConnectionService() {
302 return mCurrentFocus;
303 }
304
305 @VisibleForTesting
306 public Handler getHandler() {
307 return mEventHandler;
308 }
309
310 @VisibleForTesting
311 public List<CallFocus> getAllCall() { return mCalls; }
312
313 private void updateConnectionServiceFocus(ConnectionServiceFocus connSvrFocus) {
314 if (!Objects.equals(mCurrentFocus, connSvrFocus)) {
315 if (connSvrFocus != null) {
316 connSvrFocus.setConnectionServiceFocusListener(mConnectionServiceFocusListener);
317 connSvrFocus.connectionServiceFocusGained();
318 }
319 mCurrentFocus = connSvrFocus;
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800320 Log.d(this, "updateConnectionServiceFocus connSvr = %s", connSvrFocus);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800321 }
322 }
323
324 private void updateCurrentFocusCall() {
325 mCurrentFocusCall = null;
326
327 if (mCurrentFocus == null) {
328 return;
329 }
330
331 List<CallFocus> calls = mCalls
332 .stream()
333 .filter(call -> mCurrentFocus.equals(call.getConnectionServiceWrapper()))
334 .collect(Collectors.toList());
335
336 for (int i = 0; i < PRIORITY_FOCUS_CALL_STATE.length; i++) {
337 for (CallFocus call : calls) {
338 if (call.getState() == PRIORITY_FOCUS_CALL_STATE[i]) {
339 mCurrentFocusCall = call;
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800340 Log.d(this, "updateCurrentFocusCall %s", mCurrentFocusCall);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800341 return;
342 }
343 }
344 }
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800345
346 Log.d(this, "updateCurrentFocusCall = null");
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800347 }
348
349 private void onRequestFocusDone(FocusRequest focusRequest) {
350 if (focusRequest.callback != null) {
351 focusRequest.callback.onRequestFocusDone(focusRequest.call);
352 }
353 }
354
355 private void handleRequestFocus(FocusRequest focusRequest) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800356 Log.d(this, "handleRequestFocus req = %s", focusRequest);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800357 if (mCurrentFocus == null
358 || mCurrentFocus.equals(focusRequest.call.getConnectionServiceWrapper())) {
359 updateConnectionServiceFocus(focusRequest.call.getConnectionServiceWrapper());
360 updateCurrentFocusCall();
361 onRequestFocusDone(focusRequest);
362 } else {
363 mCurrentFocus.connectionServiceFocusLost();
364 mCurrentFocusRequest = focusRequest;
Pengquan Meng13e0da62018-02-27 11:46:42 -0800365 Message msg = mEventHandler.obtainMessage(
366 MSG_RELEASE_FOCUS_TIMEOUT,
367 new MessageArgs(
368 Log.createSubsession(),
369 "CSFM.hRF",
370 focusRequest));
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800371 mEventHandler.sendMessageDelayed(msg, RELEASE_FOCUS_TIMEOUT_MS);
372 }
373 }
374
375 private void handleReleasedFocus(ConnectionServiceFocus connectionServiceFocus) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800376 Log.d(this, "handleReleasedFocus connSvr = %s", connectionServiceFocus);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800377 // The ConnectionService can call onConnectionServiceFocusReleased even if it's not the
378 // current focus connection service, nothing will be changed in this case.
379 if (Objects.equals(mCurrentFocus, connectionServiceFocus)) {
Pengquan Meng867a93d2018-03-06 11:54:30 -0800380 mEventHandler.removeMessages(MSG_RELEASE_FOCUS_TIMEOUT);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800381 ConnectionServiceFocus newCSF = null;
382 if (mCurrentFocusRequest != null) {
383 newCSF = mCurrentFocusRequest.call.getConnectionServiceWrapper();
384 }
385 updateConnectionServiceFocus(newCSF);
386 updateCurrentFocusCall();
387 if (mCurrentFocusRequest != null) {
388 onRequestFocusDone(mCurrentFocusRequest);
389 mCurrentFocusRequest = null;
390 }
391 }
392 }
393
394 private void handleReleasedFocusTimeout(FocusRequest focusRequest) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800395 Log.d(this, "handleReleasedFocusTimeout req = %s", focusRequest);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800396 mCallsManagerRequester.releaseConnectionService(mCurrentFocus);
397 updateConnectionServiceFocus(focusRequest.call.getConnectionServiceWrapper());
398 updateCurrentFocusCall();
399 onRequestFocusDone(focusRequest);
400 mCurrentFocusRequest = null;
401 }
402
403 private void handleConnectionServiceDeath(ConnectionServiceFocus connectionServiceFocus) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800404 Log.d(this, "handleConnectionServiceDeath %s", connectionServiceFocus);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800405 if (Objects.equals(connectionServiceFocus, mCurrentFocus)) {
406 updateConnectionServiceFocus(null);
407 updateCurrentFocusCall();
408 }
409 }
410
411 private void handleAddedCall(CallFocus call) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800412 Log.d(this, "handleAddedCall %s", call);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800413 if (!mCalls.contains(call)) {
414 mCalls.add(call);
415 }
416 if (Objects.equals(mCurrentFocus, call.getConnectionServiceWrapper())) {
417 updateCurrentFocusCall();
418 }
419 }
420
421 private void handleRemovedCall(CallFocus call) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800422 Log.d(this, "handleRemovedCall %s", call);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800423 mCalls.remove(call);
424 if (call.equals(mCurrentFocusCall)) {
425 updateCurrentFocusCall();
426 }
427 }
428
429 private void handleCallStateChanged(CallFocus call, int oldState, int newState) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800430 Log.d(this,
431 "handleCallStateChanged %s, oldState = %d, newState = %d",
432 call,
433 oldState,
434 newState);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800435 if (mCalls.contains(call)
436 && Objects.equals(mCurrentFocus, call.getConnectionServiceWrapper())) {
437 updateCurrentFocusCall();
438 }
439 }
440
441 private final class FocusManagerHandler extends Handler {
442 FocusManagerHandler(Looper looper) {
443 super(looper);
444 }
445
446 @Override
447 public void handleMessage(Message msg) {
Pengquan Meng13e0da62018-02-27 11:46:42 -0800448 Session session = ((MessageArgs) msg.obj).logSession;
449 String shortName = ((MessageArgs) msg.obj).shortName;
450 if (TextUtils.isEmpty(shortName)) {
451 shortName = "hM";
452 }
453 Log.continueSession(session, shortName);
454 Object msgObj = ((MessageArgs) msg.obj).obj;
455
456 try {
457 switch (msg.what) {
458 case MSG_REQUEST_FOCUS:
459 handleRequestFocus((FocusRequest) msgObj);
460 break;
461 case MSG_RELEASE_CONNECTION_FOCUS:
462 handleReleasedFocus((ConnectionServiceFocus) msgObj);
463 break;
464 case MSG_RELEASE_FOCUS_TIMEOUT:
465 handleReleasedFocusTimeout((FocusRequest) msgObj);
466 break;
467 case MSG_CONNECTION_SERVICE_DEATH:
468 handleConnectionServiceDeath((ConnectionServiceFocus) msgObj);
469 break;
470 case MSG_ADD_CALL:
471 handleAddedCall((CallFocus) msgObj);
472 break;
473 case MSG_REMOVE_CALL:
474 handleRemovedCall((CallFocus) msgObj);
475 break;
476 case MSG_CALL_STATE_CHANGED:
477 handleCallStateChanged((CallFocus) msgObj, msg.arg1, msg.arg2);
478 break;
479 }
480 } finally {
481 Log.endSession();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800482 }
483 }
484 }
485
486 private static final class FocusRequest {
487 CallFocus call;
488 @Nullable RequestFocusCallback callback;
489
490 FocusRequest(CallFocus call, RequestFocusCallback callback) {
491 this.call = call;
492 this.callback = callback;
493 }
494 }
Pengquan Meng13e0da62018-02-27 11:46:42 -0800495
496 private static final class MessageArgs {
497 Session logSession;
498 String shortName;
499 Object obj;
500
501 MessageArgs(Session logSession, String shortName, Object obj) {
502 this.logSession = logSession;
503 this.shortName = shortName;
504 this.obj = obj;
505 }
506 }
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800507}