blob: 9d20d4a437bb6d1c4cb85d4c10df60b53fa0194f [file] [log] [blame]
Santos Cordone3d76ab2014-01-28 17:25:20 -08001/*
2 * Copyright (C) 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 Cordone3d76ab2014-01-28 17:25:20 -080018
Tyler Gunn61b92102014-08-19 07:42:20 -070019import android.Manifest;
Santos Cordone3d76ab2014-01-28 17:25:20 -080020import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.ServiceConnection;
Tyler Gunn61b92102014-08-19 07:42:20 -070024import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.content.pm.ServiceInfo;
Santos Cordonfdfcafa2014-06-26 14:49:05 -070027import android.content.res.Resources;
Tyler Gunnd45e6d92016-03-10 20:15:39 -080028import android.os.Bundle;
Roshan Pius4995e782015-06-26 15:30:30 -070029import android.os.Handler;
Santos Cordone3d76ab2014-01-28 17:25:20 -080030import android.os.IBinder;
Roshan Pius4995e782015-06-26 15:30:30 -070031import android.os.Looper;
Santos Cordone3d76ab2014-01-28 17:25:20 -080032import android.os.RemoteException;
Yorke Leee4a9c412014-11-14 16:59:42 -080033import android.os.Trace;
Amith Yamasani60e75842014-05-23 10:09:14 -070034import android.os.UserHandle;
Yorke Lee2a66f7b2015-05-13 14:21:19 -070035import android.telecom.CallAudioState;
Tyler Gunn961694a2016-03-21 16:01:40 -070036import android.telecom.ConnectionService;
Yorke Lee439c9882015-04-24 15:29:24 -070037import android.telecom.DefaultDialerManager;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070038import android.telecom.InCallService;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070039import android.telecom.Log;
40import android.telecom.Logging.Runnable;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070041import android.telecom.ParcelableCall;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070042import android.telecom.TelecomManager;
Sailesh Nepalc07b8e12016-01-23 16:43:10 -080043import android.text.TextUtils;
Tyler Gunn61b92102014-08-19 07:42:20 -070044import android.util.ArrayMap;
Sailesh Nepala439e1b2014-03-11 18:19:58 -070045
Santos Cordonf0f99f32016-02-18 16:13:57 -080046import com.android.internal.annotations.VisibleForTesting;
Tyler Gunn91d43cf2014-09-17 12:19:39 -070047// TODO: Needed for move to system service: import com.android.internal.R;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070048import com.android.internal.telecom.IInCallService;
Tyler Gunn9787e0e2014-10-14 14:36:12 -070049import com.android.internal.util.IndentingPrintWriter;
Santos Cordonf78a72f2016-01-21 22:10:32 +000050import com.android.server.telecom.SystemStateProvider.SystemStateListener;
Tyler Gunn9787e0e2014-10-14 14:36:12 -070051
Santos Cordona1610702014-06-04 20:22:56 -070052import java.util.ArrayList;
Santos Cordon4bc02452014-11-19 15:51:12 -080053import java.util.Collection;
Santos Cordonf78a72f2016-01-21 22:10:32 +000054import java.util.LinkedList;
Santos Cordona1610702014-06-04 20:22:56 -070055import java.util.List;
Tyler Gunn61b92102014-08-19 07:42:20 -070056import java.util.Map;
Santos Cordon8c9ec002015-06-22 15:02:31 -070057import java.util.Objects;
Santos Cordona1610702014-06-04 20:22:56 -070058
Santos Cordone3d76ab2014-01-28 17:25:20 -080059/**
60 * Binds to {@link IInCallService} and provides the service to {@link CallsManager} through which it
61 * can send updates to the in-call app. This class is created and owned by CallsManager and retains
Sailesh Nepale59bb192014-04-01 18:33:59 -070062 * a binding to the {@link IInCallService} (implemented by the in-call app).
Santos Cordone3d76ab2014-01-28 17:25:20 -080063 */
Hall Liu136f4c92017-04-04 13:35:18 -070064public class InCallController extends CallsManagerListenerBase {
Santos Cordon501b9b32016-03-07 14:40:07 -080065
66 public class InCallServiceConnection {
Tyler Gunnca766d22017-03-08 08:51:00 -080067 /**
68 * Indicates that a call to {@link #connect(Call)} has succeeded and resulted in a
69 * connection to an InCallService.
70 */
71 public static final int CONNECTION_SUCCEEDED = 1;
72 /**
73 * Indicates that a call to {@link #connect(Call)} has failed because of a binding issue.
74 */
75 public static final int CONNECTION_FAILED = 2;
76 /**
77 * Indicates that a call to {@link #connect(Call)} has been skipped because the
78 * IncallService does not support the type of call..
79 */
80 public static final int CONNECTION_NOT_SUPPORTED = 3;
81
Santos Cordon501b9b32016-03-07 14:40:07 -080082 public class Listener {
83 public void onDisconnect(InCallServiceConnection conn) {}
Santos Cordone3d76ab2014-01-28 17:25:20 -080084 }
85
Santos Cordon501b9b32016-03-07 14:40:07 -080086 protected Listener mListener;
87
Tyler Gunnca766d22017-03-08 08:51:00 -080088 public int connect(Call call) { return CONNECTION_FAILED; }
Santos Cordon501b9b32016-03-07 14:40:07 -080089 public void disconnect() {}
Tyler Gunnca766d22017-03-08 08:51:00 -080090 public boolean isConnected() { return false; }
Santos Cordon501b9b32016-03-07 14:40:07 -080091 public void setHasEmergency(boolean hasEmergency) {}
92 public void setListener(Listener l) {
93 mListener = l;
94 }
Hall Liudd68bc32017-01-25 17:14:23 -080095 public InCallServiceInfo getInfo() { return null; }
Santos Cordon501b9b32016-03-07 14:40:07 -080096 public void dump(IndentingPrintWriter pw) {}
97 }
98
Tyler Gunnf15dc332016-06-07 16:01:41 -070099 private class InCallServiceInfo {
Hall Liu9d15ca42016-08-30 17:18:36 -0700100 private final ComponentName mComponentName;
Tyler Gunnf15dc332016-06-07 16:01:41 -0700101 private boolean mIsExternalCallsSupported;
Tyler Gunn37e782b2017-02-10 09:42:03 -0800102 private boolean mIsSelfManagedCallsSupported;
Hall Liu9d15ca42016-08-30 17:18:36 -0700103 private final int mType;
Tyler Gunnf15dc332016-06-07 16:01:41 -0700104
Hall Liu9d15ca42016-08-30 17:18:36 -0700105 public InCallServiceInfo(ComponentName componentName,
106 boolean isExternalCallsSupported,
Tyler Gunn37e782b2017-02-10 09:42:03 -0800107 boolean isSelfManageCallsSupported,
Hall Liu9d15ca42016-08-30 17:18:36 -0700108 int type) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700109 mComponentName = componentName;
110 mIsExternalCallsSupported = isExternalCallsSupported;
Tyler Gunn37e782b2017-02-10 09:42:03 -0800111 mIsSelfManagedCallsSupported = isSelfManageCallsSupported;
Hall Liu9d15ca42016-08-30 17:18:36 -0700112 mType = type;
Tyler Gunnf15dc332016-06-07 16:01:41 -0700113 }
114
115 public ComponentName getComponentName() {
116 return mComponentName;
117 }
118
119 public boolean isExternalCallsSupported() {
120 return mIsExternalCallsSupported;
121 }
122
Tyler Gunn37e782b2017-02-10 09:42:03 -0800123 public boolean isSelfManagedCallsSupported() {
124 return mIsSelfManagedCallsSupported;
125 }
126
Hall Liu9d15ca42016-08-30 17:18:36 -0700127 public int getType() {
128 return mType;
129 }
130
Tyler Gunnf15dc332016-06-07 16:01:41 -0700131 @Override
132 public boolean equals(Object o) {
133 if (this == o) {
134 return true;
135 }
136 if (o == null || getClass() != o.getClass()) {
137 return false;
138 }
139
140 InCallServiceInfo that = (InCallServiceInfo) o;
141
142 if (mIsExternalCallsSupported != that.mIsExternalCallsSupported) {
143 return false;
144 }
Hall Liu47222c02017-03-06 16:12:43 -0800145 if (mIsSelfManagedCallsSupported != that.mIsSelfManagedCallsSupported) {
Tyler Gunn37e782b2017-02-10 09:42:03 -0800146 return false;
147 }
Tyler Gunnf15dc332016-06-07 16:01:41 -0700148 return mComponentName.equals(that.mComponentName);
149
150 }
151
152 @Override
153 public int hashCode() {
Tyler Gunn37e782b2017-02-10 09:42:03 -0800154 return Objects.hash(mComponentName, mIsExternalCallsSupported,
155 mIsSelfManagedCallsSupported);
Tyler Gunnf15dc332016-06-07 16:01:41 -0700156 }
157
158 @Override
159 public String toString() {
Tyler Gunn37e782b2017-02-10 09:42:03 -0800160 return "[" + mComponentName + " supportsExternal? " + mIsExternalCallsSupported +
161 " supportsSelfMg?" + mIsSelfManagedCallsSupported + "]";
Tyler Gunnf15dc332016-06-07 16:01:41 -0700162 }
163 }
164
Santos Cordon501b9b32016-03-07 14:40:07 -0800165 private class InCallServiceBindingConnection extends InCallServiceConnection {
166
167 private final ServiceConnection mServiceConnection = new ServiceConnection() {
168 @Override
169 public void onServiceConnected(ComponentName name, IBinder service) {
170 Log.startSession("ICSBC.oSC");
Sanket Agarwal23eb57a2016-06-15 13:11:37 -0700171 synchronized (mLock) {
172 try {
173 Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
174 mIsBound = true;
175 if (mIsConnected) {
176 // Only proceed if we are supposed to be connected.
177 onConnected(service);
178 }
179 } finally {
180 Log.endSession();
Santos Cordon501b9b32016-03-07 14:40:07 -0800181 }
Santos Cordon501b9b32016-03-07 14:40:07 -0800182 }
183 }
184
185 @Override
186 public void onServiceDisconnected(ComponentName name) {
187 Log.startSession("ICSBC.oSD");
Sanket Agarwal23eb57a2016-06-15 13:11:37 -0700188 synchronized (mLock) {
189 try {
190 Log.d(this, "onDisconnected: %s", name);
191 mIsBound = false;
192 onDisconnected();
193 } finally {
194 Log.endSession();
195 }
Santos Cordon501b9b32016-03-07 14:40:07 -0800196 }
197 }
198 };
199
Tyler Gunnf15dc332016-06-07 16:01:41 -0700200 private final InCallServiceInfo mInCallServiceInfo;
Santos Cordon501b9b32016-03-07 14:40:07 -0800201 private boolean mIsConnected = false;
202 private boolean mIsBound = false;
203
Tyler Gunnf15dc332016-06-07 16:01:41 -0700204 public InCallServiceBindingConnection(InCallServiceInfo info) {
205 mInCallServiceInfo = info;
Santos Cordon501b9b32016-03-07 14:40:07 -0800206 }
207
208 @Override
Tyler Gunnca766d22017-03-08 08:51:00 -0800209 public int connect(Call call) {
Santos Cordon501b9b32016-03-07 14:40:07 -0800210 if (mIsConnected) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700211 Log.addEvent(call, LogUtils.Events.INFO, "Already connected, ignoring request.");
Tyler Gunnca766d22017-03-08 08:51:00 -0800212 return CONNECTION_SUCCEEDED;
213 }
214
Tyler Gunnc4678372017-04-06 08:48:07 -0700215 if (call != null && call.isSelfManaged() &&
216 !mInCallServiceInfo.isSelfManagedCallsSupported()) {
Tyler Gunnca766d22017-03-08 08:51:00 -0800217 Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
218 mInCallServiceInfo);
219 mIsConnected = false;
220 return CONNECTION_NOT_SUPPORTED;
Santos Cordon501b9b32016-03-07 14:40:07 -0800221 }
222
223 Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
Tyler Gunnf15dc332016-06-07 16:01:41 -0700224 intent.setComponent(mInCallServiceInfo.getComponentName());
Tyler Gunn4fe861d2016-03-29 19:33:56 -0700225 if (call != null && !call.isIncoming() && !call.isExternalCall()){
Santos Cordon501b9b32016-03-07 14:40:07 -0800226 intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
227 call.getIntentExtras());
228 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
229 call.getTargetPhoneAccount());
230 }
231
Tyler Gunnf15dc332016-06-07 16:01:41 -0700232 Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
Hall Liu9ecbb1c2016-04-14 14:35:48 -0700233 mIsConnected = true;
234 if (!mContext.bindServiceAsUser(intent, mServiceConnection,
Santos Cordon501b9b32016-03-07 14:40:07 -0800235 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
236 UserHandle.CURRENT)) {
Hall Liu9ecbb1c2016-04-14 14:35:48 -0700237 Log.w(this, "Failed to connect.");
238 mIsConnected = false;
Santos Cordon501b9b32016-03-07 14:40:07 -0800239 }
240
Hall Liu9d15ca42016-08-30 17:18:36 -0700241 if (call != null && mIsConnected) {
242 call.getAnalytics().addInCallService(
243 mInCallServiceInfo.getComponentName().flattenToShortString(),
244 mInCallServiceInfo.getType());
245 }
246
Tyler Gunnca766d22017-03-08 08:51:00 -0800247 return mIsConnected ? CONNECTION_SUCCEEDED : CONNECTION_FAILED;
Santos Cordon501b9b32016-03-07 14:40:07 -0800248 }
249
250 @Override
Hall Liudd68bc32017-01-25 17:14:23 -0800251 public InCallServiceInfo getInfo() {
252 return mInCallServiceInfo;
253 }
254
255 @Override
Santos Cordon501b9b32016-03-07 14:40:07 -0800256 public void disconnect() {
257 if (mIsConnected) {
258 mContext.unbindService(mServiceConnection);
259 mIsConnected = false;
260 } else {
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700261 Log.addEvent(null, LogUtils.Events.INFO, "Already disconnected, ignoring request.");
Santos Cordon501b9b32016-03-07 14:40:07 -0800262 }
263 }
264
265 @Override
Tyler Gunnca766d22017-03-08 08:51:00 -0800266 public boolean isConnected() {
267 return mIsConnected;
268 }
269
270 @Override
Santos Cordon501b9b32016-03-07 14:40:07 -0800271 public void dump(IndentingPrintWriter pw) {
272 pw.append("BindingConnection [");
273 pw.append(mIsConnected ? "" : "not ").append("connected, ");
274 pw.append(mIsBound ? "" : "not ").append("bound]\n");
275 }
276
277 protected void onConnected(IBinder service) {
278 boolean shouldRemainConnected =
Tyler Gunnf15dc332016-06-07 16:01:41 -0700279 InCallController.this.onConnected(mInCallServiceInfo, service);
Santos Cordon501b9b32016-03-07 14:40:07 -0800280 if (!shouldRemainConnected) {
281 // Sometimes we can opt to disconnect for certain reasons, like if the
Hall Liu28b82f02016-07-26 17:38:56 -0700282 // InCallService rejected our initialization step, or the calls went away
Santos Cordon501b9b32016-03-07 14:40:07 -0800283 // in the time it took us to bind to the InCallService. In such cases, we go
284 // ahead and disconnect ourselves.
285 disconnect();
286 }
287 }
288
289 protected void onDisconnected() {
xulichengd733a732017-06-16 09:09:01 +0800290 InCallController.this.onDisconnected(mInCallServiceInfo);
Santos Cordon501b9b32016-03-07 14:40:07 -0800291 disconnect(); // Unbind explicitly if we get disconnected.
292 if (mListener != null) {
293 mListener.onDisconnect(InCallServiceBindingConnection.this);
294 }
295 }
296 }
297
298 /**
299 * A version of the InCallServiceBindingConnection that proxies all calls to a secondary
300 * connection until it finds an emergency call, or the other connection dies. When one of those
301 * two things happen, this class instance will take over the connection.
302 */
303 private class EmergencyInCallServiceConnection extends InCallServiceBindingConnection {
304 private boolean mIsProxying = true;
305 private boolean mIsConnected = false;
306 private final InCallServiceConnection mSubConnection;
307
308 private Listener mSubListener = new Listener() {
309 @Override
310 public void onDisconnect(InCallServiceConnection subConnection) {
311 if (subConnection == mSubConnection) {
312 if (mIsConnected && mIsProxying) {
313 // At this point we know that we need to be connected to the InCallService
314 // and we are proxying to the sub connection. However, the sub-connection
315 // just died so we need to stop proxying and connect to the system in-call
316 // service instead.
317 mIsProxying = false;
318 connect(null);
319 }
320 }
321 }
322 };
323
324 public EmergencyInCallServiceConnection(
Tyler Gunnf15dc332016-06-07 16:01:41 -0700325 InCallServiceInfo info, InCallServiceConnection subConnection) {
326
327 super(info);
Santos Cordon501b9b32016-03-07 14:40:07 -0800328 mSubConnection = subConnection;
329 if (mSubConnection != null) {
330 mSubConnection.setListener(mSubListener);
331 }
332 mIsProxying = (mSubConnection != null);
333 }
334
335 @Override
Tyler Gunnca766d22017-03-08 08:51:00 -0800336 public int connect(Call call) {
Santos Cordon501b9b32016-03-07 14:40:07 -0800337 mIsConnected = true;
338 if (mIsProxying) {
Tyler Gunnca766d22017-03-08 08:51:00 -0800339 int result = mSubConnection.connect(call);
340 mIsConnected = result == CONNECTION_SUCCEEDED;
341 if (result != CONNECTION_FAILED) {
342 return result;
Santos Cordon501b9b32016-03-07 14:40:07 -0800343 }
344 // Could not connect to child, stop proxying.
345 mIsProxying = false;
346 }
347
mike dooley66f26d12016-12-19 13:25:47 -0800348 mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
mike dooley21fb1af2016-12-05 14:25:53 -0800349 mCallsManager.getCurrentUserHandle());
350
mike dooley66f26d12016-12-19 13:25:47 -0800351 if (call != null && call.isIncoming()
352 && mEmergencyCallHelper.getLastEmergencyCallTimeMillis() > 0) {
353 // Add the last emergency call time to the call
354 Bundle extras = new Bundle();
355 extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
356 mEmergencyCallHelper.getLastEmergencyCallTimeMillis());
357 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
358 }
359
Santos Cordon501b9b32016-03-07 14:40:07 -0800360 // If we are here, we didn't or could not connect to child. So lets connect ourselves.
361 return super.connect(call);
362 }
363
364 @Override
365 public void disconnect() {
366 Log.i(this, "Disconnect forced!");
367 if (mIsProxying) {
368 mSubConnection.disconnect();
369 } else {
370 super.disconnect();
mike dooley66f26d12016-12-19 13:25:47 -0800371 mEmergencyCallHelper.maybeRevokeTemporaryLocationPermission();
Santos Cordon501b9b32016-03-07 14:40:07 -0800372 }
373 mIsConnected = false;
374 }
375
376 @Override
377 public void setHasEmergency(boolean hasEmergency) {
378 if (hasEmergency) {
379 takeControl();
380 }
381 }
382
383 @Override
Hall Liudd68bc32017-01-25 17:14:23 -0800384 public InCallServiceInfo getInfo() {
385 if (mIsProxying) {
386 return mSubConnection.getInfo();
387 } else {
388 return super.getInfo();
389 }
390 }
391 @Override
Santos Cordon501b9b32016-03-07 14:40:07 -0800392 protected void onDisconnected() {
393 // Save this here because super.onDisconnected() could force us to explicitly
394 // disconnect() as a cleanup step and that sets mIsConnected to false.
395 boolean shouldReconnect = mIsConnected;
396 super.onDisconnected();
397 // We just disconnected. Check if we are expected to be connected, and reconnect.
398 if (shouldReconnect && !mIsProxying) {
399 connect(null); // reconnect
400 }
401 }
402
403 @Override
404 public void dump(IndentingPrintWriter pw) {
Tyler Gunnca766d22017-03-08 08:51:00 -0800405 pw.print("Emergency ICS Connection [");
406 pw.append(mIsProxying ? "" : "not ").append("proxying, ");
407 pw.append(mIsConnected ? "" : "not ").append("connected]\n");
Santos Cordon501b9b32016-03-07 14:40:07 -0800408 pw.increaseIndent();
409 pw.print("Emergency: ");
410 super.dump(pw);
411 if (mSubConnection != null) {
412 pw.print("Default-Dialer: ");
413 mSubConnection.dump(pw);
414 }
415 pw.decreaseIndent();
416 }
417
418 /**
419 * Forces the connection to take control from it's subConnection.
420 */
421 private void takeControl() {
422 if (mIsProxying) {
423 mIsProxying = false;
424 if (mIsConnected) {
425 mSubConnection.disconnect();
426 super.connect(null);
427 }
428 }
429 }
430 }
431
432 /**
433 * A version of InCallServiceConnection which switches UI between two separate sub-instances of
434 * InCallServicesConnections.
435 */
436 private class CarSwappingInCallServiceConnection extends InCallServiceConnection {
437 private final InCallServiceConnection mDialerConnection;
438 private final InCallServiceConnection mCarModeConnection;
439 private InCallServiceConnection mCurrentConnection;
440 private boolean mIsCarMode = false;
441 private boolean mIsConnected = false;
442
443 public CarSwappingInCallServiceConnection(
444 InCallServiceConnection dialerConnection,
445 InCallServiceConnection carModeConnection) {
446 mDialerConnection = dialerConnection;
447 mCarModeConnection = carModeConnection;
448 mCurrentConnection = getCurrentConnection();
449 }
450
451 public synchronized void setCarMode(boolean isCarMode) {
452 Log.i(this, "carmodechange: " + mIsCarMode + " => " + isCarMode);
453 if (isCarMode != mIsCarMode) {
454 mIsCarMode = isCarMode;
455 InCallServiceConnection newConnection = getCurrentConnection();
456 if (newConnection != mCurrentConnection) {
457 if (mIsConnected) {
458 mCurrentConnection.disconnect();
Tyler Gunnca766d22017-03-08 08:51:00 -0800459 int result = newConnection.connect(null);
460 mIsConnected = result == CONNECTION_SUCCEEDED;
Santos Cordon501b9b32016-03-07 14:40:07 -0800461 }
462 mCurrentConnection = newConnection;
463 }
464 }
465 }
466
467 @Override
Tyler Gunnca766d22017-03-08 08:51:00 -0800468 public int connect(Call call) {
Santos Cordon501b9b32016-03-07 14:40:07 -0800469 if (mIsConnected) {
470 Log.i(this, "already connected");
Tyler Gunnca766d22017-03-08 08:51:00 -0800471 return CONNECTION_SUCCEEDED;
Santos Cordon501b9b32016-03-07 14:40:07 -0800472 } else {
Tyler Gunnca766d22017-03-08 08:51:00 -0800473 int result = mCurrentConnection.connect(call);
474 if (result != CONNECTION_FAILED) {
475 mIsConnected = result == CONNECTION_SUCCEEDED;
476 return result;
Santos Cordon501b9b32016-03-07 14:40:07 -0800477 }
478 }
479
Tyler Gunnca766d22017-03-08 08:51:00 -0800480 return CONNECTION_FAILED;
Santos Cordon501b9b32016-03-07 14:40:07 -0800481 }
482
483 @Override
484 public void disconnect() {
485 if (mIsConnected) {
486 mCurrentConnection.disconnect();
487 mIsConnected = false;
488 } else {
489 Log.i(this, "already disconnected");
490 }
491 }
492
493 @Override
Tyler Gunnca766d22017-03-08 08:51:00 -0800494 public boolean isConnected() {
495 return mIsConnected;
496 }
497
498 @Override
Santos Cordon501b9b32016-03-07 14:40:07 -0800499 public void setHasEmergency(boolean hasEmergency) {
500 if (mDialerConnection != null) {
501 mDialerConnection.setHasEmergency(hasEmergency);
502 }
503 if (mCarModeConnection != null) {
504 mCarModeConnection.setHasEmergency(hasEmergency);
505 }
506 }
507
508 @Override
Hall Liudd68bc32017-01-25 17:14:23 -0800509 public InCallServiceInfo getInfo() {
510 return mCurrentConnection.getInfo();
511 }
512
513 @Override
Santos Cordon501b9b32016-03-07 14:40:07 -0800514 public void dump(IndentingPrintWriter pw) {
Tyler Gunnca766d22017-03-08 08:51:00 -0800515 pw.print("Car Swapping ICS [");
516 pw.append(mIsConnected ? "" : "not ").append("connected]\n");
Santos Cordon501b9b32016-03-07 14:40:07 -0800517 pw.increaseIndent();
518 if (mDialerConnection != null) {
519 pw.print("Dialer: ");
520 mDialerConnection.dump(pw);
521 }
522 if (mCarModeConnection != null) {
523 pw.print("Car Mode: ");
524 mCarModeConnection.dump(pw);
525 }
526 }
527
528 private InCallServiceConnection getCurrentConnection() {
529 if (mIsCarMode && mCarModeConnection != null) {
530 return mCarModeConnection;
531 } else {
532 return mDialerConnection;
533 }
534 }
535 }
536
537 private class NonUIInCallServiceConnectionCollection extends InCallServiceConnection {
538 private final List<InCallServiceBindingConnection> mSubConnections;
539
540 public NonUIInCallServiceConnectionCollection(
541 List<InCallServiceBindingConnection> subConnections) {
542 mSubConnections = subConnections;
543 }
544
545 @Override
Tyler Gunnca766d22017-03-08 08:51:00 -0800546 public int connect(Call call) {
Santos Cordon501b9b32016-03-07 14:40:07 -0800547 for (InCallServiceBindingConnection subConnection : mSubConnections) {
548 subConnection.connect(call);
549 }
Tyler Gunnca766d22017-03-08 08:51:00 -0800550 return CONNECTION_SUCCEEDED;
Santos Cordon501b9b32016-03-07 14:40:07 -0800551 }
552
553 @Override
554 public void disconnect() {
555 for (InCallServiceBindingConnection subConnection : mSubConnections) {
Tyler Gunnca766d22017-03-08 08:51:00 -0800556 if (subConnection.isConnected()) {
557 subConnection.disconnect();
558 }
Santos Cordon501b9b32016-03-07 14:40:07 -0800559 }
560 }
561
562 @Override
Tyler Gunnca766d22017-03-08 08:51:00 -0800563 public boolean isConnected() {
564 boolean connected = false;
565 for (InCallServiceBindingConnection subConnection : mSubConnections) {
566 connected = connected || subConnection.isConnected();
567 }
568 return connected;
569 }
570
571 @Override
Santos Cordon501b9b32016-03-07 14:40:07 -0800572 public void dump(IndentingPrintWriter pw) {
573 pw.println("Non-UI Connections:");
574 pw.increaseIndent();
575 for (InCallServiceBindingConnection subConnection : mSubConnections) {
576 subConnection.dump(pw);
577 }
578 pw.decreaseIndent();
Santos Cordone3d76ab2014-01-28 17:25:20 -0800579 }
580 }
581
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700582 private final Call.Listener mCallListener = new Call.ListenerBase() {
583 @Override
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800584 public void onConnectionCapabilitiesChanged(Call call) {
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700585 updateCall(call);
586 }
587
588 @Override
Hall Liudd68bc32017-01-25 17:14:23 -0800589 public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {
590 updateCall(call, false /* includeVideoProvider */, didRttChange);
Tyler Gunn571d5e62016-03-15 15:55:18 -0700591 }
592
593 @Override
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700594 public void onCannedSmsResponsesLoaded(Call call) {
595 updateCall(call);
596 }
597
598 @Override
Andrew Lee3bcf9352014-07-23 12:36:05 -0700599 public void onVideoCallProviderChanged(Call call) {
Hall Liudd68bc32017-01-25 17:14:23 -0800600 updateCall(call, true /* videoProviderChanged */, false);
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700601 }
602
603 @Override
604 public void onStatusHintsChanged(Call call) {
605 updateCall(call);
606 }
607
Tyler Gunn961694a2016-03-21 16:01:40 -0700608 /**
609 * Listens for changes to extras reported by a Telecom {@link Call}.
610 *
611 * Extras changes can originate from a {@link ConnectionService} or an {@link InCallService}
612 * so we will only trigger an update of the call information if the source of the extras
613 * change was a {@link ConnectionService}.
614 *
615 * @param call The call.
616 * @param source The source of the extras change ({@link Call#SOURCE_CONNECTION_SERVICE} or
617 * {@link Call#SOURCE_INCALL_SERVICE}).
618 * @param extras The extras.
619 */
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700620 @Override
Tyler Gunn961694a2016-03-21 16:01:40 -0700621 public void onExtrasChanged(Call call, int source, Bundle extras) {
622 // Do not inform InCallServices of changes which originated there.
623 if (source == Call.SOURCE_INCALL_SERVICE) {
624 return;
625 }
626 updateCall(call);
627 }
628
629 /**
630 * Listens for changes to extras reported by a Telecom {@link Call}.
631 *
632 * Extras changes can originate from a {@link ConnectionService} or an {@link InCallService}
633 * so we will only trigger an update of the call information if the source of the extras
634 * change was a {@link ConnectionService}.
635 * @param call The call.
636 * @param source The source of the extras change ({@link Call#SOURCE_CONNECTION_SERVICE} or
637 * {@link Call#SOURCE_INCALL_SERVICE}).
638 * @param keys The extra key removed
639 */
640 @Override
641 public void onExtrasRemoved(Call call, int source, List<String> keys) {
642 // Do not inform InCallServices of changes which originated there.
643 if (source == Call.SOURCE_INCALL_SERVICE) {
644 return;
645 }
Santos Cordonb3907b32015-05-27 17:39:59 -0700646 updateCall(call);
647 }
648
649 @Override
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700650 public void onHandleChanged(Call call) {
651 updateCall(call);
652 }
653
654 @Override
655 public void onCallerDisplayNameChanged(Call call) {
656 updateCall(call);
657 }
Andrew Lee4a796602014-07-11 17:23:03 -0700658
659 @Override
Hall Liu9696c212016-06-24 16:09:02 -0700660 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {
Andrew Lee4a796602014-07-11 17:23:03 -0700661 updateCall(call);
662 }
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700663
664 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700665 public void onTargetPhoneAccountChanged(Call call) {
Ihab Awad69eb0f52014-07-18 11:20:37 -0700666 updateCall(call);
667 }
Santos Cordon12d61822014-07-29 16:02:20 -0700668
669 @Override
670 public void onConferenceableCallsChanged(Call call) {
671 updateCall(call);
672 }
Tyler Gunnd45e6d92016-03-10 20:15:39 -0800673
674 @Override
675 public void onConnectionEvent(Call call, String event, Bundle extras) {
676 notifyConnectionEvent(call, event, extras);
677 }
Hall Liuaeece4e2017-02-14 16:42:12 -0800678
679 @Override
Sanket Padaweabda6d12017-11-21 12:49:43 -0800680 public void onHandoverFailed(Call call, int error) {
681 notifyHandoverFailed(call, error);
682 }
683
684 @Override
Tyler Gunn37a4dca2018-01-18 15:00:41 -0800685 public void onHandoverComplete(Call call) {
686 notifyHandoverComplete(call);
687 }
688
689 @Override
Hall Liuaeece4e2017-02-14 16:42:12 -0800690 public void onRttInitiationFailure(Call call, int reason) {
691 notifyRttInitiationFailure(call, reason);
692 updateCall(call, false, true);
693 }
694
695 @Override
696 public void onRemoteRttRequest(Call call, int requestId) {
697 notifyRemoteRttRequest(call, requestId);
698 }
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700699 };
700
Santos Cordonf78a72f2016-01-21 22:10:32 +0000701 private final SystemStateListener mSystemStateListener = new SystemStateListener() {
702 @Override
703 public void onCarModeChanged(boolean isCarMode) {
Santos Cordon501b9b32016-03-07 14:40:07 -0800704 if (mInCallServiceConnection != null) {
705 mInCallServiceConnection.setCarMode(shouldUseCarModeUI());
706 }
Santos Cordonf78a72f2016-01-21 22:10:32 +0000707 }
708 };
709
710 private static final int IN_CALL_SERVICE_TYPE_INVALID = 0;
711 private static final int IN_CALL_SERVICE_TYPE_DIALER_UI = 1;
712 private static final int IN_CALL_SERVICE_TYPE_SYSTEM_UI = 2;
713 private static final int IN_CALL_SERVICE_TYPE_CAR_MODE_UI = 3;
714 private static final int IN_CALL_SERVICE_TYPE_NON_UI = 4;
715
Tyler Gunn61b92102014-08-19 07:42:20 -0700716 /** The in-call app implementations, see {@link IInCallService}. */
Tyler Gunnf15dc332016-06-07 16:01:41 -0700717 private final Map<InCallServiceInfo, IInCallService> mInCallServices = new ArrayMap<>();
Santos Cordone3d76ab2014-01-28 17:25:20 -0800718
Santos Cordon8c9ec002015-06-22 15:02:31 -0700719 /**
720 * The {@link ComponentName} of the bound In-Call UI Service.
721 */
722 private ComponentName mInCallUIComponentName;
723
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700724 private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getId);
Sailesh Nepale59bb192014-04-01 18:33:59 -0700725
Tyler Gunn12bccad2014-08-21 13:52:03 -0700726 /** The {@link ComponentName} of the default InCall UI. */
Santos Cordon8c9ec002015-06-22 15:02:31 -0700727 private final ComponentName mSystemInCallComponentName;
Tyler Gunn61b92102014-08-19 07:42:20 -0700728
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700729 private final Context mContext;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700730 private final TelecomSystem.SyncRoot mLock;
Ihab Awad8de76912015-02-17 12:25:52 -0800731 private final CallsManager mCallsManager;
Santos Cordonf78a72f2016-01-21 22:10:32 +0000732 private final SystemStateProvider mSystemStateProvider;
Hall Liu28b82f02016-07-26 17:38:56 -0700733 private final Timeouts.Adapter mTimeoutsAdapter;
Hall Liu7c928322016-12-06 18:15:39 -0800734 private final DefaultDialerCache mDefaultDialerCache;
mike dooley66f26d12016-12-19 13:25:47 -0800735 private final EmergencyCallHelper mEmergencyCallHelper;
Santos Cordon501b9b32016-03-07 14:40:07 -0800736 private CarSwappingInCallServiceConnection mInCallServiceConnection;
737 private NonUIInCallServiceConnectionCollection mNonUIInCallServiceConnections;
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700738
Santos Cordonf78a72f2016-01-21 22:10:32 +0000739 public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
Santos Cordon501b9b32016-03-07 14:40:07 -0800740 SystemStateProvider systemStateProvider,
mike dooley21fb1af2016-12-05 14:25:53 -0800741 DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
mike dooley66f26d12016-12-19 13:25:47 -0800742 EmergencyCallHelper emergencyCallHelper) {
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700743 mContext = context;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700744 mLock = lock;
Ihab Awad8de76912015-02-17 12:25:52 -0800745 mCallsManager = callsManager;
Santos Cordonf78a72f2016-01-21 22:10:32 +0000746 mSystemStateProvider = systemStateProvider;
Hall Liu28b82f02016-07-26 17:38:56 -0700747 mTimeoutsAdapter = timeoutsAdapter;
Hall Liu7c928322016-12-06 18:15:39 -0800748 mDefaultDialerCache = defaultDialerCache;
mike dooley66f26d12016-12-19 13:25:47 -0800749 mEmergencyCallHelper = emergencyCallHelper;
Brad Ebinger88cda842016-01-21 21:09:55 +0000750
Santos Cordonf78a72f2016-01-21 22:10:32 +0000751 Resources resources = mContext.getResources();
Santos Cordon8c9ec002015-06-22 15:02:31 -0700752 mSystemInCallComponentName = new ComponentName(
Tyler Gunn61b92102014-08-19 07:42:20 -0700753 resources.getString(R.string.ui_default_package),
754 resources.getString(R.string.incall_default_class));
Santos Cordonf78a72f2016-01-21 22:10:32 +0000755
756 mSystemStateProvider.addListener(mSystemStateListener);
Santos Cordone3d76ab2014-01-28 17:25:20 -0800757 }
758
Sailesh Nepal810735e2014-03-18 18:15:46 -0700759 @Override
760 public void onCallAdded(Call call) {
Tyler Gunnca766d22017-03-08 08:51:00 -0800761 if (!isBoundAndConnectedToServices()) {
762 Log.i(this, "onCallAdded: %s; not bound or connected.", call);
763 // We are not bound, or we're not connected.
Santos Cordon8c9ec002015-06-22 15:02:31 -0700764 bindToServices(call);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700765 } else {
Tyler Gunnca766d22017-03-08 08:51:00 -0800766 // We are bound, and we are connected.
Santos Cordone3015e82015-06-24 12:49:37 -0700767 adjustServiceBindingsForEmergency();
768
mike dooley03902b72017-09-21 14:16:35 -0700769 // This is in case an emergency call is added while there is an existing call.
770 mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
771 mCallsManager.getCurrentUserHandle());
772
Yorke Leeaaa88dd2014-09-03 13:48:56 -0700773 Log.i(this, "onCallAdded: %s", call);
Tyler Gunn61b92102014-08-19 07:42:20 -0700774 // Track the call if we don't already know about it.
775 addCall(call);
776
Tyler Gunnca766d22017-03-08 08:51:00 -0800777 Log.i(this, "mInCallServiceConnection isConnected=%b",
778 mInCallServiceConnection.isConnected());
779
Tyler Gunn1e37be52016-07-11 08:54:23 -0700780 List<ComponentName> componentsUpdated = new ArrayList<>();
Tyler Gunnf15dc332016-06-07 16:01:41 -0700781 for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
782 InCallServiceInfo info = entry.getKey();
783
784 if (call.isExternalCall() && !info.isExternalCallsSupported()) {
785 continue;
786 }
787
Tyler Gunn115c06e2017-03-02 09:29:07 -0800788 if (call.isSelfManaged() && !info.isSelfManagedCallsSupported()) {
789 continue;
790 }
791
Hall Liudd68bc32017-01-25 17:14:23 -0800792 // Only send the RTT call if it's a UI in-call service
793 boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
794
Tyler Gunn1e37be52016-07-11 08:54:23 -0700795 componentsUpdated.add(info.getComponentName());
Tyler Gunn12bccad2014-08-21 13:52:03 -0700796 IInCallService inCallService = entry.getValue();
Tyler Gunnf15dc332016-06-07 16:01:41 -0700797
Sailesh Nepalae925952016-01-24 18:56:58 -0800798 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
Tyler Gunn1e37be52016-07-11 08:54:23 -0700799 true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
Hall Liudd68bc32017-01-25 17:14:23 -0800800 info.isExternalCallsSupported(), includeRttCall);
Ihab Awadff7493a2014-06-10 13:47:44 -0700801 try {
Tyler Gunn61b92102014-08-19 07:42:20 -0700802 inCallService.addCall(parcelableCall);
Ihab Awadff7493a2014-06-10 13:47:44 -0700803 } catch (RemoteException ignored) {
804 }
Santos Cordone3d76ab2014-01-28 17:25:20 -0800805 }
Tyler Gunn1e37be52016-07-11 08:54:23 -0700806 Log.i(this, "Call added to components: %s", componentsUpdated);
Santos Cordon049b7b62014-01-30 05:34:26 -0800807 }
808 }
809
Sailesh Nepal810735e2014-03-18 18:15:46 -0700810 @Override
811 public void onCallRemoved(Call call) {
Yorke Leeaaa88dd2014-09-03 13:48:56 -0700812 Log.i(this, "onCallRemoved: %s", call);
Ihab Awad8de76912015-02-17 12:25:52 -0800813 if (mCallsManager.getCalls().isEmpty()) {
Roshan Pius4995e782015-06-26 15:30:30 -0700814 /** Let's add a 2 second delay before we send unbind to the services to hopefully
815 * give them enough time to process all the pending messages.
816 */
817 Handler handler = new Handler(Looper.getMainLooper());
Brad Ebingerf5e06662016-08-25 16:16:27 -0700818 handler.postDelayed(new Runnable("ICC.oCR", mLock) {
Roshan Pius4995e782015-06-26 15:30:30 -0700819 @Override
Brad Ebingere62e9e82016-02-01 18:26:40 -0800820 public void loggedRun() {
Brad Ebingerf5e06662016-08-25 16:16:27 -0700821 // Check again to make sure there are no active calls.
822 if (mCallsManager.getCalls().isEmpty()) {
823 unbindFromServices();
mike dooley03902b72017-09-21 14:16:35 -0700824
825 mEmergencyCallHelper.maybeRevokeTemporaryLocationPermission();
Roshan Pius4995e782015-06-26 15:30:30 -0700826 }
827 }
Hall Liu28b82f02016-07-26 17:38:56 -0700828 }.prepare(), mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(
Roshan Pius4995e782015-06-26 15:30:30 -0700829 mContext.getContentResolver()));
Santos Cordon049b7b62014-01-30 05:34:26 -0800830 }
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700831 call.removeListener(mCallListener);
Sailesh Nepale59bb192014-04-01 18:33:59 -0700832 mCallIdMapper.removeCall(call);
Santos Cordon049b7b62014-01-30 05:34:26 -0800833 }
834
Sailesh Nepal810735e2014-03-18 18:15:46 -0700835 @Override
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700836 public void onExternalCallChanged(Call call, boolean isExternalCall) {
837 Log.i(this, "onExternalCallChanged: %s -> %b", call, isExternalCall);
Tyler Gunn1e37be52016-07-11 08:54:23 -0700838
839 List<ComponentName> componentsUpdated = new ArrayList<>();
840 if (!isExternalCall) {
841 // The call was external but it is no longer external. We must now add it to any
842 // InCallServices which do not support external calls.
843 for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
844 InCallServiceInfo info = entry.getKey();
845
846 if (info.isExternalCallsSupported()) {
847 // For InCallServices which support external calls, the call will have already
848 // been added to the connection service, so we do not need to add it again.
849 continue;
850 }
851
Tyler Gunn37e782b2017-02-10 09:42:03 -0800852 if (call.isSelfManaged() && !info.isSelfManagedCallsSupported()) {
853 continue;
854 }
855
Tyler Gunn1e37be52016-07-11 08:54:23 -0700856 componentsUpdated.add(info.getComponentName());
857 IInCallService inCallService = entry.getValue();
858
Hall Liudd68bc32017-01-25 17:14:23 -0800859 // Only send the RTT call if it's a UI in-call service
860 boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
861
Tyler Gunn1e37be52016-07-11 08:54:23 -0700862 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
863 true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
Hall Liudd68bc32017-01-25 17:14:23 -0800864 info.isExternalCallsSupported(), includeRttCall);
Tyler Gunn1e37be52016-07-11 08:54:23 -0700865 try {
866 inCallService.addCall(parcelableCall);
867 } catch (RemoteException ignored) {
868 }
869 }
870 Log.i(this, "Previously external call added to components: %s", componentsUpdated);
871 } else {
872 // The call was regular but it is now external. We must now remove it from any
873 // InCallServices which do not support external calls.
874 // Remove the call by sending a call update indicating the call was disconnected.
875 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
876 call,
877 false /* includeVideoProvider */,
878 mCallsManager.getPhoneAccountRegistrar(),
879 false /* supportsExternalCalls */,
Hall Liudd68bc32017-01-25 17:14:23 -0800880 android.telecom.Call.STATE_DISCONNECTED /* overrideState */,
881 false /* includeRttCall */);
Tyler Gunn1e37be52016-07-11 08:54:23 -0700882
883 Log.i(this, "Removing external call %s ==> %s", call, parcelableCall);
884 for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
885 InCallServiceInfo info = entry.getKey();
886 if (info.isExternalCallsSupported()) {
887 // For InCallServices which support external calls, we do not need to remove
888 // the call.
889 continue;
890 }
891
892 componentsUpdated.add(info.getComponentName());
893 IInCallService inCallService = entry.getValue();
894
895 try {
896 inCallService.updateCall(parcelableCall);
897 } catch (RemoteException ignored) {
898 }
899 }
900 Log.i(this, "External call removed from components: %s", componentsUpdated);
901 }
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700902 }
903
904 @Override
Ihab Awad6fb37c82014-08-07 19:48:57 -0700905 public void onCallStateChanged(Call call, int oldState, int newState) {
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700906 updateCall(call);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700907 }
908
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700909 @Override
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700910 public void onConnectionServiceChanged(
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700911 Call call,
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700912 ConnectionServiceWrapper oldService,
913 ConnectionServiceWrapper newService) {
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700914 updateCall(call);
915 }
916
917 @Override
Yorke Lee2a66f7b2015-05-13 14:21:19 -0700918 public void onCallAudioStateChanged(CallAudioState oldCallAudioState,
919 CallAudioState newCallAudioState) {
Tyler Gunn61b92102014-08-19 07:42:20 -0700920 if (!mInCallServices.isEmpty()) {
Yorke Lee2a66f7b2015-05-13 14:21:19 -0700921 Log.i(this, "Calling onAudioStateChanged, audioState: %s -> %s", oldCallAudioState,
922 newCallAudioState);
Tyler Gunn61b92102014-08-19 07:42:20 -0700923 for (IInCallService inCallService : mInCallServices.values()) {
924 try {
Yorke Lee2a66f7b2015-05-13 14:21:19 -0700925 inCallService.onCallAudioStateChanged(newCallAudioState);
Tyler Gunn61b92102014-08-19 07:42:20 -0700926 } catch (RemoteException ignored) {
927 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700928 }
929 }
930 }
931
Santos Cordon91bd74c2014-11-07 16:10:22 -0800932 @Override
933 public void onCanAddCallChanged(boolean canAddCall) {
934 if (!mInCallServices.isEmpty()) {
935 Log.i(this, "onCanAddCallChanged : %b", canAddCall);
936 for (IInCallService inCallService : mInCallServices.values()) {
937 try {
938 inCallService.onCanAddCallChanged(canAddCall);
939 } catch (RemoteException ignored) {
940 }
941 }
942 }
943 }
944
Evan Charlton352105c2014-06-03 14:10:54 -0700945 void onPostDialWait(Call call, String remaining) {
Tyler Gunn61b92102014-08-19 07:42:20 -0700946 if (!mInCallServices.isEmpty()) {
Evan Charlton352105c2014-06-03 14:10:54 -0700947 Log.i(this, "Calling onPostDialWait, remaining = %s", remaining);
Tyler Gunn61b92102014-08-19 07:42:20 -0700948 for (IInCallService inCallService : mInCallServices.values()) {
949 try {
950 inCallService.setPostDialWait(mCallIdMapper.getCallId(call), remaining);
951 } catch (RemoteException ignored) {
952 }
Evan Charlton352105c2014-06-03 14:10:54 -0700953 }
954 }
955 }
956
Santos Cordona1610702014-06-04 20:22:56 -0700957 @Override
Santos Cordona1610702014-06-04 20:22:56 -0700958 public void onIsConferencedChanged(Call call) {
Santos Cordon0fbe6322014-08-14 04:04:25 -0700959 Log.d(this, "onIsConferencedChanged %s", call);
Santos Cordona1610702014-06-04 20:22:56 -0700960 updateCall(call);
961 }
962
Santos Cordonf3671a62014-05-29 21:51:53 -0700963 void bringToForeground(boolean showDialpad) {
Tyler Gunn61b92102014-08-19 07:42:20 -0700964 if (!mInCallServices.isEmpty()) {
965 for (IInCallService inCallService : mInCallServices.values()) {
966 try {
967 inCallService.bringToForeground(showDialpad);
968 } catch (RemoteException ignored) {
969 }
Santos Cordonf3671a62014-05-29 21:51:53 -0700970 }
971 } else {
972 Log.w(this, "Asking to bring unbound in-call UI to foreground.");
973 }
974 }
975
Sailesh Nepalc07b8e12016-01-23 16:43:10 -0800976 void silenceRinger() {
977 if (!mInCallServices.isEmpty()) {
978 for (IInCallService inCallService : mInCallServices.values()) {
979 try {
980 inCallService.silenceRinger();
981 } catch (RemoteException ignored) {
982 }
983 }
984 }
985 }
986
Tyler Gunnd45e6d92016-03-10 20:15:39 -0800987 private void notifyConnectionEvent(Call call, String event, Bundle extras) {
988 if (!mInCallServices.isEmpty()) {
989 for (IInCallService inCallService : mInCallServices.values()) {
990 try {
Brad Ebinger0d402552016-05-27 16:02:53 -0700991 Log.i(this, "notifyConnectionEvent {Call: %s, Event: %s, Extras:[%s]}",
992 (call != null ? call.toString() :"null"),
993 (event != null ? event : "null") ,
994 (extras != null ? extras.toString() : "null"));
Tyler Gunnd45e6d92016-03-10 20:15:39 -0800995 inCallService.onConnectionEvent(mCallIdMapper.getCallId(call), event, extras);
996 } catch (RemoteException ignored) {
997 }
998 }
999 }
1000 }
1001
Hall Liuaeece4e2017-02-14 16:42:12 -08001002 private void notifyRttInitiationFailure(Call call, int reason) {
1003 if (!mInCallServices.isEmpty()) {
1004 mInCallServices.entrySet().stream()
1005 .filter((entry) -> entry.getKey().equals(mInCallServiceConnection.getInfo()))
1006 .forEach((entry) -> {
1007 try {
1008 Log.i(this, "notifyRttFailure, call %s, incall %s",
1009 call, entry.getKey());
1010 entry.getValue().onRttInitiationFailure(mCallIdMapper.getCallId(call),
1011 reason);
1012 } catch (RemoteException ignored) {
1013 }
1014 });
1015 }
1016 }
1017
1018 private void notifyRemoteRttRequest(Call call, int requestId) {
1019 if (!mInCallServices.isEmpty()) {
1020 mInCallServices.entrySet().stream()
1021 .filter((entry) -> entry.getKey().equals(mInCallServiceConnection.getInfo()))
1022 .forEach((entry) -> {
1023 try {
1024 Log.i(this, "notifyRemoteRttRequest, call %s, incall %s",
1025 call, entry.getKey());
1026 entry.getValue().onRttUpgradeRequest(
1027 mCallIdMapper.getCallId(call), requestId);
1028 } catch (RemoteException ignored) {
1029 }
1030 });
1031 }
1032 }
Sanket Padaweabda6d12017-11-21 12:49:43 -08001033
1034 private void notifyHandoverFailed(Call call, int error) {
1035 if (!mInCallServices.isEmpty()) {
1036 for (IInCallService inCallService : mInCallServices.values()) {
1037 try {
1038 inCallService.onHandoverFailed(mCallIdMapper.getCallId(call), error);
1039 } catch (RemoteException ignored) {
1040 }
1041 }
1042 }
1043 }
1044
Tyler Gunn37a4dca2018-01-18 15:00:41 -08001045 private void notifyHandoverComplete(Call call) {
1046 if (!mInCallServices.isEmpty()) {
1047 for (IInCallService inCallService : mInCallServices.values()) {
1048 try {
1049 inCallService.onHandoverComplete(mCallIdMapper.getCallId(call));
1050 } catch (RemoteException ignored) {
1051 }
1052 }
1053 }
1054 }
1055
Santos Cordone3d76ab2014-01-28 17:25:20 -08001056 /**
1057 * Unbinds an existing bound connection to the in-call app.
Santos Cordone3d76ab2014-01-28 17:25:20 -08001058 */
Santos Cordon8c9ec002015-06-22 15:02:31 -07001059 private void unbindFromServices() {
Tyler Gunnca766d22017-03-08 08:51:00 -08001060 if (mInCallServiceConnection != null) {
1061 mInCallServiceConnection.disconnect();
1062 mInCallServiceConnection = null;
1063 }
1064 if (mNonUIInCallServiceConnections != null) {
1065 mNonUIInCallServiceConnections.disconnect();
1066 mNonUIInCallServiceConnections = null;
Santos Cordone3d76ab2014-01-28 17:25:20 -08001067 }
xulichengd733a732017-06-16 09:09:01 +08001068 mInCallServices.clear();
Santos Cordone3d76ab2014-01-28 17:25:20 -08001069 }
1070
1071 /**
Santos Cordon8c9ec002015-06-22 15:02:31 -07001072 * Binds to all the UI-providing InCallService as well as system-implemented non-UI
Tyler Gunnca766d22017-03-08 08:51:00 -08001073 * InCallServices. Method-invoker must check {@link #isBoundAndConnectedToServices()} before invoking.
Yorke Lee9b71ea82014-11-26 11:15:13 -08001074 *
1075 * @param call The newly added call that triggered the binding to the in-call services.
Santos Cordon049b7b62014-01-30 05:34:26 -08001076 */
Santos Cordonf0f99f32016-02-18 16:13:57 -08001077 @VisibleForTesting
1078 public void bindToServices(Call call) {
Tyler Gunnca766d22017-03-08 08:51:00 -08001079 if (mInCallServiceConnection == null) {
1080 InCallServiceConnection dialerInCall = null;
1081 InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent();
1082 Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
1083 if (defaultDialerComponentInfo != null &&
1084 !defaultDialerComponentInfo.getComponentName().equals(
1085 mSystemInCallComponentName)) {
1086 dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
1087 }
1088 Log.i(this, "defaultDialer: " + dialerInCall);
Santos Cordonf78a72f2016-01-21 22:10:32 +00001089
Tyler Gunnca766d22017-03-08 08:51:00 -08001090 InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
1091 mSystemInCallComponentName, IN_CALL_SERVICE_TYPE_SYSTEM_UI);
1092 EmergencyInCallServiceConnection systemInCall =
1093 new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
1094 systemInCall.setHasEmergency(mCallsManager.hasEmergencyCall());
Santos Cordon501b9b32016-03-07 14:40:07 -08001095
Tyler Gunnca766d22017-03-08 08:51:00 -08001096 InCallServiceConnection carModeInCall = null;
1097 InCallServiceInfo carModeComponentInfo = getCarModeComponent();
1098 if (carModeComponentInfo != null &&
1099 !carModeComponentInfo.getComponentName().equals(mSystemInCallComponentName)) {
1100 carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);
1101 }
1102
1103 mInCallServiceConnection =
1104 new CarSwappingInCallServiceConnection(systemInCall, carModeInCall);
Santos Cordon501b9b32016-03-07 14:40:07 -08001105 }
1106
Santos Cordon501b9b32016-03-07 14:40:07 -08001107 mInCallServiceConnection.setCarMode(shouldUseCarModeUI());
Santos Cordon501b9b32016-03-07 14:40:07 -08001108
Tyler Gunnca766d22017-03-08 08:51:00 -08001109 // Actually try binding to the UI InCallService. If the response
1110 if (mInCallServiceConnection.connect(call) ==
1111 InCallServiceConnection.CONNECTION_SUCCEEDED) {
1112 // Only connect to the non-ui InCallServices if we actually connected to the main UI
1113 // one.
1114 connectToNonUiInCallServices(call);
1115 } else {
1116 Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
1117 }
1118 }
1119
1120 private void connectToNonUiInCallServices(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -07001121 List<InCallServiceInfo> nonUIInCallComponents =
1122 getInCallServiceComponents(IN_CALL_SERVICE_TYPE_NON_UI);
Santos Cordon501b9b32016-03-07 14:40:07 -08001123 List<InCallServiceBindingConnection> nonUIInCalls = new LinkedList<>();
Tyler Gunnf15dc332016-06-07 16:01:41 -07001124 for (InCallServiceInfo serviceInfo : nonUIInCallComponents) {
1125 nonUIInCalls.add(new InCallServiceBindingConnection(serviceInfo));
Santos Cordon501b9b32016-03-07 14:40:07 -08001126 }
1127 mNonUIInCallServiceConnections = new NonUIInCallServiceConnectionCollection(nonUIInCalls);
1128 mNonUIInCallServiceConnections.connect(call);
1129 }
1130
Tyler Gunnf15dc332016-06-07 16:01:41 -07001131 private InCallServiceInfo getDefaultDialerComponent() {
Hall Liu7c928322016-12-06 18:15:39 -08001132 String packageName = mDefaultDialerCache.getDefaultDialerApplication(
1133 mCallsManager.getCurrentUserHandle().getIdentifier());
Santos Cordon501b9b32016-03-07 14:40:07 -08001134 Log.d(this, "Default Dialer package: " + packageName);
1135
1136 return getInCallServiceComponent(packageName, IN_CALL_SERVICE_TYPE_DIALER_UI);
1137 }
1138
Tyler Gunnf15dc332016-06-07 16:01:41 -07001139 private InCallServiceInfo getCarModeComponent() {
1140 // Seems strange to cast a String to null, but the signatures of getInCallServiceComponent
1141 // differ in the types of the first parameter, and passing in null is inherently ambiguous.
1142 return getInCallServiceComponent((String) null, IN_CALL_SERVICE_TYPE_CAR_MODE_UI);
Santos Cordon501b9b32016-03-07 14:40:07 -08001143 }
1144
Tyler Gunnf15dc332016-06-07 16:01:41 -07001145 private InCallServiceInfo getInCallServiceComponent(ComponentName componentName, int type) {
1146 List<InCallServiceInfo> list = getInCallServiceComponents(componentName, type);
Santos Cordon501b9b32016-03-07 14:40:07 -08001147 if (list != null && !list.isEmpty()) {
1148 return list.get(0);
Brad Ebingerabe06d32016-07-14 20:17:50 -07001149 } else {
1150 // Last Resort: Try to bind to the ComponentName given directly.
1151 Log.e(this, new Exception(), "Package Manager could not find ComponentName: "
1152 + componentName +". Trying to bind anyway.");
Tyler Gunn37e782b2017-02-10 09:42:03 -08001153 return new InCallServiceInfo(componentName, false, false, type);
Santos Cordon501b9b32016-03-07 14:40:07 -08001154 }
Santos Cordon501b9b32016-03-07 14:40:07 -08001155 }
1156
Tyler Gunnf15dc332016-06-07 16:01:41 -07001157 private InCallServiceInfo getInCallServiceComponent(String packageName, int type) {
1158 List<InCallServiceInfo> list = getInCallServiceComponents(packageName, type);
1159 if (list != null && !list.isEmpty()) {
1160 return list.get(0);
1161 }
1162 return null;
1163 }
1164
1165 private List<InCallServiceInfo> getInCallServiceComponents(int type) {
1166 return getInCallServiceComponents(null, null, type);
1167 }
1168
1169 private List<InCallServiceInfo> getInCallServiceComponents(String packageName, int type) {
1170 return getInCallServiceComponents(packageName, null, type);
1171 }
1172
1173 private List<InCallServiceInfo> getInCallServiceComponents(ComponentName componentName,
1174 int type) {
1175 return getInCallServiceComponents(null, componentName, type);
1176 }
1177
1178 private List<InCallServiceInfo> getInCallServiceComponents(String packageName,
1179 ComponentName componentName, int requestedType) {
1180
1181 List<InCallServiceInfo> retval = new LinkedList<>();
Santos Cordon501b9b32016-03-07 14:40:07 -08001182
Santos Cordon8c9ec002015-06-22 15:02:31 -07001183 Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);
Santos Cordon501b9b32016-03-07 14:40:07 -08001184 if (packageName != null) {
1185 serviceIntent.setPackage(packageName);
1186 }
Tyler Gunnf15dc332016-06-07 16:01:41 -07001187 if (componentName != null) {
1188 serviceIntent.setComponent(componentName);
1189 }
Santos Cordon501b9b32016-03-07 14:40:07 -08001190
Santos Cordon501b9b32016-03-07 14:40:07 -08001191 PackageManager packageManager = mContext.getPackageManager();
Santos Cordonf0f99f32016-02-18 16:13:57 -08001192 for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
1193 serviceIntent,
1194 PackageManager.GET_META_DATA,
1195 mCallsManager.getCurrentUserHandle().getIdentifier())) {
Santos Cordon8c9ec002015-06-22 15:02:31 -07001196 ServiceInfo serviceInfo = entry.serviceInfo;
Santos Cordonf0f99f32016-02-18 16:13:57 -08001197
Santos Cordon8c9ec002015-06-22 15:02:31 -07001198 if (serviceInfo != null) {
Tyler Gunnf15dc332016-06-07 16:01:41 -07001199 boolean isExternalCallsSupported = serviceInfo.metaData != null &&
1200 serviceInfo.metaData.getBoolean(
1201 TelecomManager.METADATA_INCLUDE_EXTERNAL_CALLS, false);
Tyler Gunn37e782b2017-02-10 09:42:03 -08001202 boolean isSelfManageCallsSupported = serviceInfo.metaData != null &&
1203 serviceInfo.metaData.getBoolean(
1204 TelecomManager.METADATA_INCLUDE_SELF_MANAGED_CALLS, false);
Tyler Gunnf15dc332016-06-07 16:01:41 -07001205
Tyler Gunnca766d22017-03-08 08:51:00 -08001206 int currentType = getInCallServiceType(entry.serviceInfo, packageManager);
1207 if (requestedType == 0 || requestedType == currentType) {
1208 if (requestedType == IN_CALL_SERVICE_TYPE_NON_UI) {
1209 // We enforce the rule that self-managed calls are not supported by non-ui
1210 // InCallServices.
1211 isSelfManageCallsSupported = false;
1212 }
Tyler Gunnf15dc332016-06-07 16:01:41 -07001213 retval.add(new InCallServiceInfo(
1214 new ComponentName(serviceInfo.packageName, serviceInfo.name),
Tyler Gunn37e782b2017-02-10 09:42:03 -08001215 isExternalCallsSupported, isSelfManageCallsSupported, requestedType));
Santos Cordonf78a72f2016-01-21 22:10:32 +00001216 }
Santos Cordon049b7b62014-01-30 05:34:26 -08001217 }
1218 }
Santos Cordon8c9ec002015-06-22 15:02:31 -07001219
Santos Cordon501b9b32016-03-07 14:40:07 -08001220 return retval;
Santos Cordon049b7b62014-01-30 05:34:26 -08001221 }
1222
Santos Cordonf78a72f2016-01-21 22:10:32 +00001223 private boolean shouldUseCarModeUI() {
1224 return mSystemStateProvider.isCarMode();
1225 }
1226
1227 /**
1228 * Returns the type of InCallService described by the specified serviceInfo.
1229 */
1230 private int getInCallServiceType(ServiceInfo serviceInfo, PackageManager packageManager) {
1231 // Verify that the InCallService requires the BIND_INCALL_SERVICE permission which
1232 // enforces that only Telecom can bind to it.
1233 boolean hasServiceBindPermission = serviceInfo.permission != null &&
1234 serviceInfo.permission.equals(
1235 Manifest.permission.BIND_INCALL_SERVICE);
1236 if (!hasServiceBindPermission) {
1237 Log.w(this, "InCallService does not require BIND_INCALL_SERVICE permission: " +
1238 serviceInfo.packageName);
1239 return IN_CALL_SERVICE_TYPE_INVALID;
1240 }
1241
1242 if (mSystemInCallComponentName.getPackageName().equals(serviceInfo.packageName) &&
1243 mSystemInCallComponentName.getClassName().equals(serviceInfo.name)) {
1244 return IN_CALL_SERVICE_TYPE_SYSTEM_UI;
1245 }
1246
1247 // Check to see if the service is a car-mode UI type by checking that it has the
1248 // CONTROL_INCALL_EXPERIENCE (to verify it is a system app) and that it has the
1249 // car-mode UI metadata.
1250 boolean hasControlInCallPermission = packageManager.checkPermission(
1251 Manifest.permission.CONTROL_INCALL_EXPERIENCE,
1252 serviceInfo.packageName) == PackageManager.PERMISSION_GRANTED;
1253 boolean isCarModeUIService = serviceInfo.metaData != null &&
1254 serviceInfo.metaData.getBoolean(
1255 TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI, false) &&
1256 hasControlInCallPermission;
1257 if (isCarModeUIService) {
1258 return IN_CALL_SERVICE_TYPE_CAR_MODE_UI;
1259 }
1260
Santos Cordonf78a72f2016-01-21 22:10:32 +00001261 // Check to see that it is the default dialer package
1262 boolean isDefaultDialerPackage = Objects.equals(serviceInfo.packageName,
Hall Liu7c928322016-12-06 18:15:39 -08001263 mDefaultDialerCache.getDefaultDialerApplication(
1264 mCallsManager.getCurrentUserHandle().getIdentifier()));
Santos Cordonf78a72f2016-01-21 22:10:32 +00001265 boolean isUIService = serviceInfo.metaData != null &&
1266 serviceInfo.metaData.getBoolean(
1267 TelecomManager.METADATA_IN_CALL_SERVICE_UI, false);
1268 if (isDefaultDialerPackage && isUIService) {
1269 return IN_CALL_SERVICE_TYPE_DIALER_UI;
1270 }
1271
1272 // Also allow any in-call service that has the control-experience permission (to ensure
1273 // that it is a system app) and doesn't claim to show any UI.
1274 if (hasControlInCallPermission && !isUIService) {
1275 return IN_CALL_SERVICE_TYPE_NON_UI;
1276 }
1277
1278 // Anything else that remains, we will not bind to.
1279 Log.i(this, "Skipping binding to %s:%s, control: %b, car-mode: %b, ui: %b",
1280 serviceInfo.packageName, serviceInfo.name, hasControlInCallPermission,
1281 isCarModeUIService, isUIService);
1282 return IN_CALL_SERVICE_TYPE_INVALID;
1283 }
1284
Santos Cordone3015e82015-06-24 12:49:37 -07001285 private void adjustServiceBindingsForEmergency() {
Santos Cordon501b9b32016-03-07 14:40:07 -08001286 // The connected UI is not the system UI, so lets check if we should switch them
1287 // if there exists an emergency number.
1288 if (mCallsManager.hasEmergencyCall()) {
1289 mInCallServiceConnection.setHasEmergency(true);
Santos Cordone3015e82015-06-24 12:49:37 -07001290 }
1291 }
1292
Santos Cordon049b7b62014-01-30 05:34:26 -08001293 /**
Santos Cordone3d76ab2014-01-28 17:25:20 -08001294 * Persists the {@link IInCallService} instance and starts the communication between
Sailesh Nepale59bb192014-04-01 18:33:59 -07001295 * this class and in-call app by sending the first update to in-call app. This method is
Santos Cordone3d76ab2014-01-28 17:25:20 -08001296 * called after a successful binding connection is established.
1297 *
Tyler Gunnf15dc332016-06-07 16:01:41 -07001298 * @param info Info about the service, including its {@link ComponentName}.
Santos Cordone3d76ab2014-01-28 17:25:20 -08001299 * @param service The {@link IInCallService} implementation.
Santos Cordon501b9b32016-03-07 14:40:07 -08001300 * @return True if we successfully connected.
Santos Cordone3d76ab2014-01-28 17:25:20 -08001301 */
Tyler Gunnf15dc332016-06-07 16:01:41 -07001302 private boolean onConnected(InCallServiceInfo info, IBinder service) {
1303 Trace.beginSection("onConnected: " + info.getComponentName());
1304 Log.i(this, "onConnected to %s", info.getComponentName());
Yorke Leeaaa88dd2014-09-03 13:48:56 -07001305
Tyler Gunn61b92102014-08-19 07:42:20 -07001306 IInCallService inCallService = IInCallService.Stub.asInterface(service);
Tyler Gunnf15dc332016-06-07 16:01:41 -07001307 mInCallServices.put(info, inCallService);
Santos Cordone3d76ab2014-01-28 17:25:20 -08001308
1309 try {
Ihab Awad78a5e6b2015-02-06 10:13:05 -08001310 inCallService.setInCallAdapter(
1311 new InCallAdapter(
Ihab Awad8de76912015-02-17 12:25:52 -08001312 mCallsManager,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -07001313 mCallIdMapper,
Brad Ebinger209e4132015-12-17 15:10:14 -08001314 mLock,
Tyler Gunnf15dc332016-06-07 16:01:41 -07001315 info.getComponentName().getPackageName()));
Santos Cordone3d76ab2014-01-28 17:25:20 -08001316 } catch (RemoteException e) {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -08001317 Log.e(this, e, "Failed to set the in-call adapter.");
Yorke Leee4a9c412014-11-14 16:59:42 -08001318 Trace.endSection();
Santos Cordon501b9b32016-03-07 14:40:07 -08001319 return false;
Santos Cordone3d76ab2014-01-28 17:25:20 -08001320 }
1321
Tyler Gunn61b92102014-08-19 07:42:20 -07001322 // Upon successful connection, send the state of the world to the service.
Hall Liubd7d3792016-01-29 18:38:43 -08001323 List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
Hall Liu28b82f02016-07-26 17:38:56 -07001324 Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
1325 "calls", calls.size(), info.getComponentName());
1326 int numCallsSent = 0;
1327 for (Call call : calls) {
Hall Liud109afb2016-01-26 12:53:53 -08001328 try {
Tyler Gunn37e782b2017-02-10 09:42:03 -08001329 if ((call.isSelfManaged() && !info.isSelfManagedCallsSupported()) ||
Tyler Gunna90ba732017-01-26 07:24:08 -08001330 (call.isExternalCall() && !info.isExternalCallsSupported())) {
Hall Liu28b82f02016-07-26 17:38:56 -07001331 continue;
1332 }
1333
Hall Liudd68bc32017-01-25 17:14:23 -08001334 // Only send the RTT call if it's a UI in-call service
1335 boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
1336
Hall Liu28b82f02016-07-26 17:38:56 -07001337 // Track the call if we don't already know about it.
1338 addCall(call);
1339 numCallsSent += 1;
1340 inCallService.addCall(ParcelableCallUtils.toParcelableCall(
1341 call,
1342 true /* includeVideoProvider */,
1343 mCallsManager.getPhoneAccountRegistrar(),
Hall Liudd68bc32017-01-25 17:14:23 -08001344 info.isExternalCallsSupported(),
1345 includeRttCall));
Hall Liud109afb2016-01-26 12:53:53 -08001346 } catch (RemoteException ignored) {
1347 }
Santos Cordon049b7b62014-01-30 05:34:26 -08001348 }
Hall Liu28b82f02016-07-26 17:38:56 -07001349 try {
1350 inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
1351 inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
1352 } catch (RemoteException ignored) {
1353 }
1354 Log.i(this, "%s calls sent to InCallService.", numCallsSent);
Yorke Leee4a9c412014-11-14 16:59:42 -08001355 Trace.endSection();
Santos Cordon501b9b32016-03-07 14:40:07 -08001356 return true;
Santos Cordone3d76ab2014-01-28 17:25:20 -08001357 }
1358
1359 /**
Tyler Gunn61b92102014-08-19 07:42:20 -07001360 * Cleans up an instance of in-call app after the service has been unbound.
1361 *
xulichengd733a732017-06-16 09:09:01 +08001362 * @param disconnectedInfo The {@link InCallServiceInfo} of the service which disconnected.
Santos Cordone3d76ab2014-01-28 17:25:20 -08001363 */
xulichengd733a732017-06-16 09:09:01 +08001364 private void onDisconnected(InCallServiceInfo disconnectedInfo) {
1365 Log.i(this, "onDisconnected from %s", disconnectedInfo.getComponentName());
Santos Cordond93a4412014-09-03 17:04:03 -07001366
xulichengd733a732017-06-16 09:09:01 +08001367 mInCallServices.remove(disconnectedInfo);
Santos Cordone3d76ab2014-01-28 17:25:20 -08001368 }
Sailesh Nepal8c85dee2014-04-07 22:21:40 -07001369
Tyler Gunn12bccad2014-08-21 13:52:03 -07001370 /**
Tyler Gunn633963e2015-04-15 14:32:00 -07001371 * Informs all {@link InCallService} instances of the updated call information.
Tyler Gunn12bccad2014-08-21 13:52:03 -07001372 *
1373 * @param call The {@link Call}.
1374 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -07001375 private void updateCall(Call call) {
Hall Liudd68bc32017-01-25 17:14:23 -08001376 updateCall(call, false /* videoProviderChanged */, false);
Tyler Gunn633963e2015-04-15 14:32:00 -07001377 }
1378
1379 /**
1380 * Informs all {@link InCallService} instances of the updated call information.
1381 *
1382 * @param call The {@link Call}.
1383 * @param videoProviderChanged {@code true} if the video provider changed, {@code false}
1384 * otherwise.
Hall Liudd68bc32017-01-25 17:14:23 -08001385 * @param rttInfoChanged {@code true} if any information about the RTT session changed,
1386 * {@code false} otherwise.
Tyler Gunn633963e2015-04-15 14:32:00 -07001387 */
Hall Liudd68bc32017-01-25 17:14:23 -08001388 private void updateCall(Call call, boolean videoProviderChanged, boolean rttInfoChanged) {
Tyler Gunn61b92102014-08-19 07:42:20 -07001389 if (!mInCallServices.isEmpty()) {
Tyler Gunn1e37be52016-07-11 08:54:23 -07001390 Log.i(this, "Sending updateCall %s", call);
Santos Cordon8c9ec002015-06-22 15:02:31 -07001391 List<ComponentName> componentsUpdated = new ArrayList<>();
Tyler Gunnf15dc332016-06-07 16:01:41 -07001392 for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
1393 InCallServiceInfo info = entry.getKey();
1394 if (call.isExternalCall() && !info.isExternalCallsSupported()) {
1395 continue;
1396 }
1397
Tyler Gunn37e782b2017-02-10 09:42:03 -08001398 if (call.isSelfManaged() && !info.isSelfManagedCallsSupported()) {
1399 continue;
1400 }
1401
Tyler Gunn1e37be52016-07-11 08:54:23 -07001402 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
1403 call,
1404 videoProviderChanged /* includeVideoProvider */,
1405 mCallsManager.getPhoneAccountRegistrar(),
Hall Liudd68bc32017-01-25 17:14:23 -08001406 info.isExternalCallsSupported(),
1407 rttInfoChanged && info.equals(mInCallServiceConnection.getInfo()));
Tyler Gunnf15dc332016-06-07 16:01:41 -07001408 ComponentName componentName = info.getComponentName();
Tyler Gunn12bccad2014-08-21 13:52:03 -07001409 IInCallService inCallService = entry.getValue();
Santos Cordon8c9ec002015-06-22 15:02:31 -07001410 componentsUpdated.add(componentName);
Tyler Gunnf15dc332016-06-07 16:01:41 -07001411
Tyler Gunn61b92102014-08-19 07:42:20 -07001412 try {
1413 inCallService.updateCall(parcelableCall);
1414 } catch (RemoteException ignored) {
1415 }
Sailesh Nepal8c85dee2014-04-07 22:21:40 -07001416 }
Santos Cordon8c9ec002015-06-22 15:02:31 -07001417 Log.i(this, "Components updated: %s", componentsUpdated);
Sailesh Nepal8c85dee2014-04-07 22:21:40 -07001418 }
1419 }
1420
Tyler Gunn12bccad2014-08-21 13:52:03 -07001421 /**
Tyler Gunn61b92102014-08-19 07:42:20 -07001422 * Adds the call to the list of calls tracked by the {@link InCallController}.
1423 * @param call The call to add.
1424 */
1425 private void addCall(Call call) {
1426 if (mCallIdMapper.getCallId(call) == null) {
1427 mCallIdMapper.addCall(call);
1428 call.addListener(mCallListener);
1429 }
1430 }
Tyler Gunn9787e0e2014-10-14 14:36:12 -07001431
Tyler Gunnca766d22017-03-08 08:51:00 -08001432 /**
1433 * @return true if we are bound to the UI InCallService and it is connected.
1434 */
1435 private boolean isBoundAndConnectedToServices() {
1436 return mInCallServiceConnection != null && mInCallServiceConnection.isConnected();
Santos Cordon8c9ec002015-06-22 15:02:31 -07001437 }
1438
Tyler Gunn9787e0e2014-10-14 14:36:12 -07001439 /**
1440 * Dumps the state of the {@link InCallController}.
1441 *
1442 * @param pw The {@code IndentingPrintWriter} to write the state to.
1443 */
1444 public void dump(IndentingPrintWriter pw) {
1445 pw.println("mInCallServices (InCalls registered):");
1446 pw.increaseIndent();
Tyler Gunnf15dc332016-06-07 16:01:41 -07001447 for (InCallServiceInfo info : mInCallServices.keySet()) {
1448 pw.println(info);
Tyler Gunn9787e0e2014-10-14 14:36:12 -07001449 }
1450 pw.decreaseIndent();
1451
Santos Cordon501b9b32016-03-07 14:40:07 -08001452 pw.println("ServiceConnections (InCalls bound):");
Tyler Gunn9787e0e2014-10-14 14:36:12 -07001453 pw.increaseIndent();
Santos Cordon501b9b32016-03-07 14:40:07 -08001454 if (mInCallServiceConnection != null) {
1455 mInCallServiceConnection.dump(pw);
Tyler Gunn9787e0e2014-10-14 14:36:12 -07001456 }
1457 pw.decreaseIndent();
1458 }
Sailesh Nepalc07b8e12016-01-23 16:43:10 -08001459
Santos Cordonf0f99f32016-02-18 16:13:57 -08001460 public boolean doesConnectedDialerSupportRinging() {
1461 String ringingPackage = null;
1462 if (mInCallUIComponentName != null) {
1463 ringingPackage = mInCallUIComponentName.getPackageName().trim();
1464 }
1465
1466 if (TextUtils.isEmpty(ringingPackage)) {
1467 // The current in-call UI returned nothing, so lets use the default dialer.
1468 ringingPackage = DefaultDialerManager.getDefaultDialerApplication(
1469 mContext, UserHandle.USER_CURRENT);
1470 }
1471 if (TextUtils.isEmpty(ringingPackage)) {
Sailesh Nepalc07b8e12016-01-23 16:43:10 -08001472 return false;
1473 }
1474
1475 Intent intent = new Intent(InCallService.SERVICE_INTERFACE)
Santos Cordonf0f99f32016-02-18 16:13:57 -08001476 .setPackage(ringingPackage);
1477 List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
1478 intent, PackageManager.GET_META_DATA,
1479 mCallsManager.getCurrentUserHandle().getIdentifier());
Sailesh Nepalc07b8e12016-01-23 16:43:10 -08001480 if (entries.isEmpty()) {
1481 return false;
1482 }
1483
1484 ResolveInfo info = entries.get(0);
1485 if (info.serviceInfo == null || info.serviceInfo.metaData == null) {
1486 return false;
1487 }
1488
1489 return info.serviceInfo.metaData
1490 .getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_RINGING, false);
1491 }
Hall Liubd7d3792016-01-29 18:38:43 -08001492
1493 private List<Call> orderCallsWithChildrenFirst(Collection<Call> calls) {
1494 LinkedList<Call> parentCalls = new LinkedList<>();
1495 LinkedList<Call> childCalls = new LinkedList<>();
1496 for (Call call : calls) {
1497 if (call.getChildCalls().size() > 0) {
1498 parentCalls.add(call);
1499 } else {
1500 childCalls.add(call);
1501 }
1502 }
1503 childCalls.addAll(parentCalls);
1504 return childCalls;
1505 }
Santos Cordone3d76ab2014-01-28 17:25:20 -08001506}