blob: 92570a07b2207646a7c6bf8aae58aeed4ff4b1b8 [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();
Tyler Gunn77ff6802018-03-29 07:05:02 +0000115
116 /**
117 * @return {@code True} if this call can receive focus, {@code false} otherwise.
118 */
119 boolean isFocusable();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800120 }
121
122 /** Interface define a call back for focus request event. */
123 public interface RequestFocusCallback {
124 /**
125 * Invokes after the focus request is done.
126 *
127 * @param call the call associated with the focus request.
128 */
129 void onRequestFocusDone(CallFocus call);
130 }
131
132 /**
133 * Interface define to allow the ConnectionServiceFocusManager to communicate with
134 * {@link CallsManager}.
135 */
136 public interface CallsManagerRequester {
137 /**
138 * Requests {@link CallsManager} to disconnect a {@link ConnectionServiceFocus}. This
139 * usually happen when the connection service doesn't respond to focus lost event.
140 */
141 void releaseConnectionService(ConnectionServiceFocus connectionService);
142
143 /**
144 * Sets the {@link com.android.server.telecom.CallsManager.CallsManagerListener} to listen
145 * the call event that ConnectionServiceFocusManager cares about.
146 */
147 void setCallsManagerListener(CallsManager.CallsManagerListener listener);
148 }
149
150 private static final int[] PRIORITY_FOCUS_CALL_STATE = new int[] {
151 CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING
152 };
153
154 private static final int MSG_REQUEST_FOCUS = 1;
155 private static final int MSG_RELEASE_CONNECTION_FOCUS = 2;
156 private static final int MSG_RELEASE_FOCUS_TIMEOUT = 3;
157 private static final int MSG_CONNECTION_SERVICE_DEATH = 4;
158 private static final int MSG_ADD_CALL = 5;
159 private static final int MSG_REMOVE_CALL = 6;
160 private static final int MSG_CALL_STATE_CHANGED = 7;
161
162 @VisibleForTesting
163 public static final int RELEASE_FOCUS_TIMEOUT_MS = 5000;
164
165 private final List<CallFocus> mCalls;
166
167 private final CallsManagerListenerBase mCallsManagerListener =
168 new CallsManagerListenerBase() {
169 @Override
170 public void onCallAdded(Call call) {
171 if (callShouldBeIgnored(call)) {
172 return;
173 }
174
Pengquan Meng13e0da62018-02-27 11:46:42 -0800175 mEventHandler
176 .obtainMessage(MSG_ADD_CALL,
177 new MessageArgs(
178 Log.createSubsession(),
179 "CSFM.oCA",
180 call))
181 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800182 }
183
184 @Override
185 public void onCallRemoved(Call call) {
186 if (callShouldBeIgnored(call)) {
187 return;
188 }
189
Pengquan Meng13e0da62018-02-27 11:46:42 -0800190 mEventHandler
191 .obtainMessage(MSG_REMOVE_CALL,
192 new MessageArgs(
193 Log.createSubsession(),
194 "CSFM.oCR",
195 call))
196 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800197 }
198
199 @Override
200 public void onCallStateChanged(Call call, int oldState, int newState) {
201 if (callShouldBeIgnored(call)) {
202 return;
203 }
204
Pengquan Meng13e0da62018-02-27 11:46:42 -0800205 mEventHandler
206 .obtainMessage(MSG_CALL_STATE_CHANGED, oldState, newState,
207 new MessageArgs(
208 Log.createSubsession(),
209 "CSFM.oCSS",
210 call))
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800211 .sendToTarget();
212 }
213
214 @Override
215 public void onExternalCallChanged(Call call, boolean isExternalCall) {
216 if (isExternalCall) {
Pengquan Meng13e0da62018-02-27 11:46:42 -0800217 mEventHandler
218 .obtainMessage(MSG_REMOVE_CALL,
219 new MessageArgs(
220 Log.createSubsession(),
221 "CSFM.oECC",
222 call))
223 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800224 } else {
Pengquan Meng13e0da62018-02-27 11:46:42 -0800225 mEventHandler
226 .obtainMessage(MSG_ADD_CALL,
227 new MessageArgs(
228 Log.createSubsession(),
229 "CSFM.oECC",
230 call))
231 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800232 }
233 }
234
235 boolean callShouldBeIgnored(Call call) {
236 return call.isExternalCall();
237 }
238 };
239
240 private final ConnectionServiceFocusListener mConnectionServiceFocusListener =
241 new ConnectionServiceFocusListener() {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800242 @Override
243 public void onConnectionServiceReleased(
244 ConnectionServiceFocus connectionServiceFocus) {
245 mEventHandler
Pengquan Meng13e0da62018-02-27 11:46:42 -0800246 .obtainMessage(MSG_RELEASE_CONNECTION_FOCUS,
247 new MessageArgs(
248 Log.createSubsession(),
249 "CSFM.oCSR",
250 connectionServiceFocus))
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800251 .sendToTarget();
252 }
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800253
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800254 @Override
255 public void onConnectionServiceDeath(
256 ConnectionServiceFocus connectionServiceFocus) {
257 mEventHandler
Pengquan Meng13e0da62018-02-27 11:46:42 -0800258 .obtainMessage(MSG_CONNECTION_SERVICE_DEATH,
259 new MessageArgs(
260 Log.createSubsession(),
261 "CSFM.oCSD",
262 connectionServiceFocus))
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800263 .sendToTarget();
264 }
265 };
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800266
267 private ConnectionServiceFocus mCurrentFocus;
268 private CallFocus mCurrentFocusCall;
269 private CallsManagerRequester mCallsManagerRequester;
270 private FocusRequest mCurrentFocusRequest;
271 private FocusManagerHandler mEventHandler;
272
273 public ConnectionServiceFocusManager(
274 CallsManagerRequester callsManagerRequester, Looper looper) {
275 mCallsManagerRequester = callsManagerRequester;
276 mCallsManagerRequester.setCallsManagerListener(mCallsManagerListener);
277 mEventHandler = new FocusManagerHandler(looper);
278 mCalls = new ArrayList<>();
279 }
280
281 /**
282 * Requests the call focus for the given call. The {@code callback} will be invoked once
283 * the request is done.
284 * @param focus the call need to be focus.
285 * @param callback the callback associated with this request.
286 */
287 public void requestFocus(CallFocus focus, RequestFocusCallback callback) {
Pengquan Meng13e0da62018-02-27 11:46:42 -0800288 mEventHandler.obtainMessage(MSG_REQUEST_FOCUS,
289 new MessageArgs(
290 Log.createSubsession(),
291 "CSFM.rF",
292 new FocusRequest(focus, callback)))
293 .sendToTarget();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800294 }
295
296 /**
297 * Returns the current focus call. The {@link android.telecom.ConnectionService} of the focus
298 * call is the current connection service focus. Also the state of the focus call must be one
299 * of {@link #PRIORITY_FOCUS_CALL_STATE}.
300 */
301 public CallFocus getCurrentFocusCall() {
302 return mCurrentFocusCall;
303 }
304
305 /** Returns the current connection service focus. */
306 public ConnectionServiceFocus getCurrentFocusConnectionService() {
307 return mCurrentFocus;
308 }
309
310 @VisibleForTesting
311 public Handler getHandler() {
312 return mEventHandler;
313 }
314
315 @VisibleForTesting
316 public List<CallFocus> getAllCall() { return mCalls; }
317
318 private void updateConnectionServiceFocus(ConnectionServiceFocus connSvrFocus) {
319 if (!Objects.equals(mCurrentFocus, connSvrFocus)) {
320 if (connSvrFocus != null) {
321 connSvrFocus.setConnectionServiceFocusListener(mConnectionServiceFocusListener);
322 connSvrFocus.connectionServiceFocusGained();
323 }
324 mCurrentFocus = connSvrFocus;
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800325 Log.d(this, "updateConnectionServiceFocus connSvr = %s", connSvrFocus);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800326 }
327 }
328
329 private void updateCurrentFocusCall() {
330 mCurrentFocusCall = null;
331
332 if (mCurrentFocus == null) {
333 return;
334 }
335
336 List<CallFocus> calls = mCalls
337 .stream()
Tyler Gunn77ff6802018-03-29 07:05:02 +0000338 .filter(call -> mCurrentFocus.equals(call.getConnectionServiceWrapper())
339 && call.isFocusable())
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800340 .collect(Collectors.toList());
341
342 for (int i = 0; i < PRIORITY_FOCUS_CALL_STATE.length; i++) {
343 for (CallFocus call : calls) {
344 if (call.getState() == PRIORITY_FOCUS_CALL_STATE[i]) {
345 mCurrentFocusCall = call;
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800346 Log.d(this, "updateCurrentFocusCall %s", mCurrentFocusCall);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800347 return;
348 }
349 }
350 }
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800351
352 Log.d(this, "updateCurrentFocusCall = null");
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800353 }
354
355 private void onRequestFocusDone(FocusRequest focusRequest) {
356 if (focusRequest.callback != null) {
357 focusRequest.callback.onRequestFocusDone(focusRequest.call);
358 }
359 }
360
361 private void handleRequestFocus(FocusRequest focusRequest) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800362 Log.d(this, "handleRequestFocus req = %s", focusRequest);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800363 if (mCurrentFocus == null
364 || mCurrentFocus.equals(focusRequest.call.getConnectionServiceWrapper())) {
365 updateConnectionServiceFocus(focusRequest.call.getConnectionServiceWrapper());
366 updateCurrentFocusCall();
367 onRequestFocusDone(focusRequest);
368 } else {
369 mCurrentFocus.connectionServiceFocusLost();
370 mCurrentFocusRequest = focusRequest;
Pengquan Meng13e0da62018-02-27 11:46:42 -0800371 Message msg = mEventHandler.obtainMessage(
372 MSG_RELEASE_FOCUS_TIMEOUT,
373 new MessageArgs(
374 Log.createSubsession(),
375 "CSFM.hRF",
376 focusRequest));
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800377 mEventHandler.sendMessageDelayed(msg, RELEASE_FOCUS_TIMEOUT_MS);
378 }
379 }
380
381 private void handleReleasedFocus(ConnectionServiceFocus connectionServiceFocus) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800382 Log.d(this, "handleReleasedFocus connSvr = %s", connectionServiceFocus);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800383 // The ConnectionService can call onConnectionServiceFocusReleased even if it's not the
384 // current focus connection service, nothing will be changed in this case.
385 if (Objects.equals(mCurrentFocus, connectionServiceFocus)) {
Pengquan Meng867a93d2018-03-06 11:54:30 -0800386 mEventHandler.removeMessages(MSG_RELEASE_FOCUS_TIMEOUT);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800387 ConnectionServiceFocus newCSF = null;
388 if (mCurrentFocusRequest != null) {
389 newCSF = mCurrentFocusRequest.call.getConnectionServiceWrapper();
390 }
391 updateConnectionServiceFocus(newCSF);
392 updateCurrentFocusCall();
393 if (mCurrentFocusRequest != null) {
394 onRequestFocusDone(mCurrentFocusRequest);
395 mCurrentFocusRequest = null;
396 }
397 }
398 }
399
400 private void handleReleasedFocusTimeout(FocusRequest focusRequest) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800401 Log.d(this, "handleReleasedFocusTimeout req = %s", focusRequest);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800402 mCallsManagerRequester.releaseConnectionService(mCurrentFocus);
403 updateConnectionServiceFocus(focusRequest.call.getConnectionServiceWrapper());
404 updateCurrentFocusCall();
405 onRequestFocusDone(focusRequest);
406 mCurrentFocusRequest = null;
407 }
408
409 private void handleConnectionServiceDeath(ConnectionServiceFocus connectionServiceFocus) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800410 Log.d(this, "handleConnectionServiceDeath %s", connectionServiceFocus);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800411 if (Objects.equals(connectionServiceFocus, mCurrentFocus)) {
412 updateConnectionServiceFocus(null);
413 updateCurrentFocusCall();
414 }
415 }
416
417 private void handleAddedCall(CallFocus call) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800418 Log.d(this, "handleAddedCall %s", call);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800419 if (!mCalls.contains(call)) {
420 mCalls.add(call);
421 }
422 if (Objects.equals(mCurrentFocus, call.getConnectionServiceWrapper())) {
423 updateCurrentFocusCall();
424 }
425 }
426
427 private void handleRemovedCall(CallFocus call) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800428 Log.d(this, "handleRemovedCall %s", call);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800429 mCalls.remove(call);
430 if (call.equals(mCurrentFocusCall)) {
431 updateCurrentFocusCall();
432 }
433 }
434
435 private void handleCallStateChanged(CallFocus call, int oldState, int newState) {
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800436 Log.d(this,
437 "handleCallStateChanged %s, oldState = %d, newState = %d",
438 call,
439 oldState,
440 newState);
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800441 if (mCalls.contains(call)
442 && Objects.equals(mCurrentFocus, call.getConnectionServiceWrapper())) {
443 updateCurrentFocusCall();
444 }
445 }
446
447 private final class FocusManagerHandler extends Handler {
448 FocusManagerHandler(Looper looper) {
449 super(looper);
450 }
451
452 @Override
453 public void handleMessage(Message msg) {
Pengquan Meng13e0da62018-02-27 11:46:42 -0800454 Session session = ((MessageArgs) msg.obj).logSession;
455 String shortName = ((MessageArgs) msg.obj).shortName;
456 if (TextUtils.isEmpty(shortName)) {
457 shortName = "hM";
458 }
459 Log.continueSession(session, shortName);
460 Object msgObj = ((MessageArgs) msg.obj).obj;
461
462 try {
463 switch (msg.what) {
464 case MSG_REQUEST_FOCUS:
465 handleRequestFocus((FocusRequest) msgObj);
466 break;
467 case MSG_RELEASE_CONNECTION_FOCUS:
468 handleReleasedFocus((ConnectionServiceFocus) msgObj);
469 break;
470 case MSG_RELEASE_FOCUS_TIMEOUT:
471 handleReleasedFocusTimeout((FocusRequest) msgObj);
472 break;
473 case MSG_CONNECTION_SERVICE_DEATH:
474 handleConnectionServiceDeath((ConnectionServiceFocus) msgObj);
475 break;
476 case MSG_ADD_CALL:
477 handleAddedCall((CallFocus) msgObj);
478 break;
479 case MSG_REMOVE_CALL:
480 handleRemovedCall((CallFocus) msgObj);
481 break;
482 case MSG_CALL_STATE_CHANGED:
483 handleCallStateChanged((CallFocus) msgObj, msg.arg1, msg.arg2);
484 break;
485 }
486 } finally {
487 Log.endSession();
Pengquan Mengb1a11c82017-11-21 17:52:42 -0800488 }
489 }
490 }
491
492 private static final class FocusRequest {
493 CallFocus call;
494 @Nullable RequestFocusCallback callback;
495
496 FocusRequest(CallFocus call, RequestFocusCallback callback) {
497 this.call = call;
498 this.callback = callback;
499 }
500 }
Pengquan Meng13e0da62018-02-27 11:46:42 -0800501
502 private static final class MessageArgs {
503 Session logSession;
504 String shortName;
505 Object obj;
506
507 MessageArgs(Session logSession, String shortName, Object obj) {
508 this.logSession = logSession;
509 this.shortName = shortName;
510 this.obj = obj;
511 }
512 }
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800513}