blob: 04f06a2adc9134e66f7ff09ed42c50cb40b67de3 [file] [log] [blame]
Ben Gilad0407fb22014-01-09 16:18:41 -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;
Ben Gilad9f2bed32013-12-12 17:43:26 -080018
Tyler Gunn91d43cf2014-09-17 12:19:39 -070019import android.content.Context;
Tyler Gunnbbd78a72017-04-30 14:16:07 -070020import android.content.Intent;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070021import android.graphics.Bitmap;
22import android.graphics.drawable.Drawable;
Sailesh Nepalce704b92014-03-17 18:31:43 -070023import android.net.Uri;
Sanket Padaweb5de1cd2018-01-05 14:27:30 -080024import android.os.Build;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070025import android.os.Bundle;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070026import android.os.Handler;
Ihab Awade6dbc9d2015-03-26 10:33:44 -070027import android.os.Looper;
Hall Liudd68bc32017-01-25 17:14:23 -080028import android.os.ParcelFileDescriptor;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -070029import android.os.Parcelable;
Tyler Gunn86014fc2015-06-12 14:31:25 -070030import android.os.RemoteException;
Tyler Gunn8bb2b012017-08-04 09:28:59 -070031import android.os.SystemClock;
Yorke Leee4a9c412014-11-14 16:59:42 -080032import android.os.Trace;
Yorke Lee8a736a82014-08-30 17:19:16 -070033import android.provider.ContactsContract.Contacts;
Christine Hallstrom96a0be62016-11-30 16:05:13 -080034import android.telecom.CallAudioState;
Tyler Gunn9b618b82016-10-17 15:54:35 -070035import android.telecom.Conference;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -070036import android.telecom.ConnectionService;
Andrew Lee701dc002014-09-11 21:29:12 -070037import android.telecom.DisconnectCause;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070038import android.telecom.Connection;
39import android.telecom.GatewayInfo;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070040import android.telecom.Log;
41import android.telecom.Logging.EventManager;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070042import android.telecom.ParcelableConnection;
43import android.telecom.PhoneAccount;
44import android.telecom.PhoneAccountHandle;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070045import android.telecom.Response;
46import android.telecom.StatusHints;
47import android.telecom.TelecomManager;
48import android.telecom.VideoProfile;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070049import android.telephony.PhoneNumberUtils;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070050import android.text.TextUtils;
Tej Singh95daef72018-02-05 18:34:19 -080051import android.util.StatsLog;
Tony Mak578a4e62015-11-23 11:18:51 +000052import android.os.UserHandle;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080053
Ihab Awada3653902015-01-23 13:44:46 -080054import com.android.internal.annotations.VisibleForTesting;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070055import com.android.internal.telecom.IVideoProvider;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070056import com.android.internal.telephony.CallerInfo;
Ihab Awadff7493a2014-06-10 13:47:44 -070057import com.android.internal.telephony.SmsApplication;
Tyler Gunn91d43cf2014-09-17 12:19:39 -070058import com.android.internal.util.Preconditions;
Santos Cordon61d0f702014-02-19 02:52:23 -080059
Hall Liudd68bc32017-01-25 17:14:23 -080060import java.io.IOException;
Bryce Lee41045b42015-08-05 13:10:39 -070061import java.lang.String;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070062import java.text.SimpleDateFormat;
Santos Cordon12d61822014-07-29 16:02:20 -070063import java.util.ArrayList;
Ihab Awadff7493a2014-06-10 13:47:44 -070064import java.util.Collections;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070065import java.util.Date;
Santos Cordona1610702014-06-04 20:22:56 -070066import java.util.LinkedList;
67import java.util.List;
Sailesh Nepal91990782014-03-08 17:45:52 -080068import java.util.Locale;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070069import java.util.Objects;
Ben Gilad61925612014-03-11 19:06:36 -070070import java.util.Set;
Jay Shraunera82c8f72014-08-14 15:49:16 -070071import java.util.concurrent.ConcurrentHashMap;
Ben Gilad0407fb22014-01-09 16:18:41 -080072
Ben Gilad2495d572014-01-09 17:26:19 -080073/**
74 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
Tyler Gunn7cc70b42014-09-12 22:17:27 -070075 * from the time the call intent was received by Telecom (vs. the time the call was
Ben Gilad2495d572014-01-09 17:26:19 -080076 * connected etc).
77 */
Ihab Awada3653902015-01-23 13:44:46 -080078@VisibleForTesting
Pengquan Mengd7f92cf2017-11-21 17:52:42 -080079public class Call implements CreateConnectionResponse, EventManager.Loggable,
80 ConnectionServiceFocusManager.CallFocus {
Tyler Gunn8452be02015-09-17 09:57:02 -070081 public final static String CALL_ID_UNKNOWN = "-1";
Tyler Gunn7a1f8232015-12-03 19:52:59 -080082 public final static long DATA_USAGE_NOT_SET = -1;
Tyler Gunn8452be02015-09-17 09:57:02 -070083
Hall Liu32587202015-11-18 11:10:08 -080084 public static final int CALL_DIRECTION_UNDEFINED = 0;
85 public static final int CALL_DIRECTION_OUTGOING = 1;
86 public static final int CALL_DIRECTION_INCOMING = 2;
87 public static final int CALL_DIRECTION_UNKNOWN = 3;
88
Tyler Gunn961694a2016-03-21 16:01:40 -070089 /** Identifies extras changes which originated from a connection service. */
90 public static final int SOURCE_CONNECTION_SERVICE = 1;
91 /** Identifies extras changes which originated from an incall service. */
92 public static final int SOURCE_INCALL_SERVICE = 2;
93
Hall Liudd68bc32017-01-25 17:14:23 -080094 private static final int RTT_PIPE_READ_SIDE_INDEX = 0;
95 private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;
Hall Liuaeece4e2017-02-14 16:42:12 -080096
97 private static final int INVALID_RTT_REQUEST_ID = -1;
Tyler Gunnad972422017-11-13 13:36:35 -080098
99 private static final char NO_DTMF_TONE = '\0';
100
Santos Cordon766d04f2014-05-06 10:28:25 -0700101 /**
102 * Listener for events on the call.
103 */
Hall Liu5b70c1c2016-03-03 18:42:57 -0800104 @VisibleForTesting
105 public interface Listener {
Ihab Awad6fb37c82014-08-07 19:48:57 -0700106 void onSuccessfulOutgoingCall(Call call, int callState);
Andrew Lee701dc002014-09-11 21:29:12 -0700107 void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause);
Hall Liu6d4b66d2016-04-01 16:31:13 -0700108 void onSuccessfulIncomingCall(Call call);
Santos Cordon766d04f2014-05-06 10:28:25 -0700109 void onFailedIncomingCall(Call call);
Yorke Lee9250e5f2014-10-01 13:39:09 -0700110 void onSuccessfulUnknownCall(Call call, int callState);
111 void onFailedUnknownCall(Call call);
Andrew Lee5be64bc2014-09-08 18:35:15 -0700112 void onRingbackRequested(Call call, boolean ringbackRequested);
Evan Charlton352105c2014-06-03 14:10:54 -0700113 void onPostDialWait(Call call, String remaining);
Nancy Chena469f762014-12-09 18:29:20 -0800114 void onPostDialChar(Call call, char nextChar);
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800115 void onConnectionCapabilitiesChanged(Call call);
Hall Liudd68bc32017-01-25 17:14:23 -0800116 void onConnectionPropertiesChanged(Call call, boolean didRttChange);
Santos Cordona1610702014-06-04 20:22:56 -0700117 void onParentChanged(Call call);
118 void onChildrenChanged(Call call);
Ihab Awadff7493a2014-06-10 13:47:44 -0700119 void onCannedSmsResponsesLoaded(Call call);
Andrew Lee3bcf9352014-07-23 12:36:05 -0700120 void onVideoCallProviderChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -0700121 void onCallerInfoChanged(Call call);
Andrew Lee5be64bc2014-09-08 18:35:15 -0700122 void onIsVoipAudioModeChanged(Call call);
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700123 void onStatusHintsChanged(Call call);
Tyler Gunn961694a2016-03-21 16:01:40 -0700124 void onExtrasChanged(Call c, int source, Bundle extras);
125 void onExtrasRemoved(Call c, int source, List<String> keys);
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700126 void onHandleChanged(Call call);
127 void onCallerDisplayNameChanged(Call call);
Hall Liu9696c212016-06-24 16:09:02 -0700128 void onVideoStateChanged(Call call, int previousVideoState, int newVideoState);
Ihab Awadb78b2762014-07-25 15:16:23 -0700129 void onTargetPhoneAccountChanged(Call call);
130 void onConnectionManagerPhoneAccountChanged(Call call);
Santos Cordon12d61822014-07-29 16:02:20 -0700131 void onPhoneAccountChanged(Call call);
132 void onConferenceableCallsChanged(Call call);
Hall Liu63e690c2017-02-14 18:05:03 -0800133 boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout);
Tyler Gunndb821912016-02-16 14:35:25 -0800134 void onHoldToneRequested(Call call);
Tyler Gunnd45e6d92016-03-10 20:15:39 -0800135 void onConnectionEvent(Call call, String event, Bundle extras);
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700136 void onExternalCallChanged(Call call, boolean isExternalCall);
Hall Liuaeece4e2017-02-14 16:42:12 -0800137 void onRttInitiationFailure(Call call, int reason);
138 void onRemoteRttRequest(Call call, int requestId);
Tyler Gunn6f6f1c52017-04-17 18:22:04 -0700139 void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -0800140 Bundle extras, boolean isLegacy);
141 void onHandoverFailed(Call call, int error);
Tyler Gunn7c031f22018-01-18 15:00:41 -0800142 void onHandoverComplete(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -0700143 }
144
Ihab Awad8de76912015-02-17 12:25:52 -0800145 public abstract static class ListenerBase implements Listener {
Santos Cordon64c7e962014-07-02 15:15:27 -0700146 @Override
Ihab Awad6fb37c82014-08-07 19:48:57 -0700147 public void onSuccessfulOutgoingCall(Call call, int callState) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700148 @Override
Andrew Lee701dc002014-09-11 21:29:12 -0700149 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700150 @Override
Hall Liu6d4b66d2016-04-01 16:31:13 -0700151 public void onSuccessfulIncomingCall(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700152 @Override
153 public void onFailedIncomingCall(Call call) {}
154 @Override
Yorke Lee9250e5f2014-10-01 13:39:09 -0700155 public void onSuccessfulUnknownCall(Call call, int callState) {}
156 @Override
157 public void onFailedUnknownCall(Call call) {}
158 @Override
Andrew Lee5be64bc2014-09-08 18:35:15 -0700159 public void onRingbackRequested(Call call, boolean ringbackRequested) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700160 @Override
161 public void onPostDialWait(Call call, String remaining) {}
162 @Override
Nancy Chena469f762014-12-09 18:29:20 -0800163 public void onPostDialChar(Call call, char nextChar) {}
164 @Override
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800165 public void onConnectionCapabilitiesChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700166 @Override
Hall Liudd68bc32017-01-25 17:14:23 -0800167 public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {}
Tyler Gunn571d5e62016-03-15 15:55:18 -0700168 @Override
Santos Cordon64c7e962014-07-02 15:15:27 -0700169 public void onParentChanged(Call call) {}
170 @Override
171 public void onChildrenChanged(Call call) {}
172 @Override
173 public void onCannedSmsResponsesLoaded(Call call) {}
174 @Override
Andrew Lee3bcf9352014-07-23 12:36:05 -0700175 public void onVideoCallProviderChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700176 @Override
Santos Cordon64c7e962014-07-02 15:15:27 -0700177 public void onCallerInfoChanged(Call call) {}
Sailesh Nepal7e669572014-07-08 21:29:12 -0700178 @Override
Andrew Lee5be64bc2014-09-08 18:35:15 -0700179 public void onIsVoipAudioModeChanged(Call call) {}
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700180 @Override
181 public void onStatusHintsChanged(Call call) {}
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700182 @Override
Tyler Gunn961694a2016-03-21 16:01:40 -0700183 public void onExtrasChanged(Call c, int source, Bundle extras) {}
184 @Override
185 public void onExtrasRemoved(Call c, int source, List<String> keys) {}
Santos Cordonb3907b32015-05-27 17:39:59 -0700186 @Override
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700187 public void onHandleChanged(Call call) {}
188 @Override
189 public void onCallerDisplayNameChanged(Call call) {}
Andrew Lee4a796602014-07-11 17:23:03 -0700190 @Override
Hall Liu9696c212016-06-24 16:09:02 -0700191 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {}
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700192 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700193 public void onTargetPhoneAccountChanged(Call call) {}
194 @Override
195 public void onConnectionManagerPhoneAccountChanged(Call call) {}
Santos Cordon12d61822014-07-29 16:02:20 -0700196 @Override
197 public void onPhoneAccountChanged(Call call) {}
198 @Override
199 public void onConferenceableCallsChanged(Call call) {}
Santos Cordoncf5b2912014-11-05 21:57:54 -0800200 @Override
Hall Liu63e690c2017-02-14 18:05:03 -0800201 public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) {
Santos Cordoncf5b2912014-11-05 21:57:54 -0800202 return false;
203 }
Tyler Gunndb821912016-02-16 14:35:25 -0800204 @Override
205 public void onHoldToneRequested(Call call) {}
Tyler Gunnd45e6d92016-03-10 20:15:39 -0800206 @Override
207 public void onConnectionEvent(Call call, String event, Bundle extras) {}
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700208 @Override
209 public void onExternalCallChanged(Call call, boolean isExternalCall) {}
Hall Liuaeece4e2017-02-14 16:42:12 -0800210 @Override
211 public void onRttInitiationFailure(Call call, int reason) {}
212 @Override
213 public void onRemoteRttRequest(Call call, int requestId) {}
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700214 @Override
Tyler Gunn6f6f1c52017-04-17 18:22:04 -0700215 public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -0800216 Bundle extras, boolean isLegacy) {}
217 @Override
218 public void onHandoverFailed(Call call, int error) {}
Tyler Gunn7c031f22018-01-18 15:00:41 -0800219 @Override
220 public void onHandoverComplete(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700221 }
222
Hall Liu5b70c1c2016-03-03 18:42:57 -0800223 private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener =
224 new CallerInfoLookupHelper.OnQueryCompleteListener() {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700225 /** ${inheritDoc} */
226 @Override
Hall Liu5b70c1c2016-03-03 18:42:57 -0800227 public void onCallerInfoQueryComplete(Uri handle, CallerInfo callerInfo) {
Ihab Awadabcbce42015-04-07 14:04:01 -0700228 synchronized (mLock) {
Hall Liu5b70c1c2016-03-03 18:42:57 -0800229 Call.this.setCallerInfo(handle, callerInfo);
230 }
231 }
232
233 @Override
234 public void onContactPhotoQueryComplete(Uri handle, CallerInfo callerInfo) {
235 synchronized (mLock) {
236 Call.this.setCallerInfo(handle, callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700237 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700238 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700239 };
240
Hall Liu32587202015-11-18 11:10:08 -0800241 /**
242 * One of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, or CALL_DIRECTION_UNKNOWN
Yorke Lee9250e5f2014-10-01 13:39:09 -0700243 */
Hall Liu32587202015-11-18 11:10:08 -0800244 private final int mCallDirection;
Yorke Lee9250e5f2014-10-01 13:39:09 -0700245
Ben Gilad0407fb22014-01-09 16:18:41 -0800246 /**
Hall Liub8ce19c2015-10-14 15:13:51 -0700247 * The post-dial digits that were dialed after the network portion of the number
248 */
249 private final String mPostDialDigits;
250
251 /**
Brad Ebingerbaf52ba2016-03-21 16:38:32 -0700252 * The secondary line number that an incoming call has been received on if the SIM subscription
253 * has multiple associated numbers.
254 */
255 private String mViaNumber = "";
256
257 /**
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700258 * The wall clock time this call was created. Beyond logging and such, may also be used for
259 * bookkeeping and specifically for marking certain call attempts as failed attempts.
260 * Note: This timestamp should NOT be used for calculating call duration.
Ben Gilad0407fb22014-01-09 16:18:41 -0800261 */
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700262 private long mCreationTimeMillis;
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700263
Santos Cordon40f9b172014-11-17 10:49:29 -0800264 /** The time this call was made active. */
265 private long mConnectTimeMillis = 0;
266
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700267 /**
268 * The time, in millis, since boot when this call was connected. This should ONLY be used when
269 * calculating the duration of the call.
270 *
271 * The reason for this is that the {@link SystemClock#elapsedRealtime()} is based on the
272 * elapsed time since the device was booted. Changes to the system clock (e.g. due to NITZ
273 * time sync, time zone changes user initiated clock changes) would cause a duration calculated
274 * based on {@link #mConnectTimeMillis} to change based on the delta in the time.
275 * Using the {@link SystemClock#elapsedRealtime()} ensures that changes to the wall clock do
276 * not impact the call duration.
277 */
278 private long mConnectElapsedTimeMillis = 0;
279
280 /** The wall clock time this call was disconnected. */
Santos Cordon40f9b172014-11-17 10:49:29 -0800281 private long mDisconnectTimeMillis = 0;
282
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700283 /**
284 * The elapsed time since boot when this call was disconnected. Recorded as the
285 * {@link SystemClock#elapsedRealtime()}. This ensures that the call duration is not impacted
286 * by changes in the wall time clock.
287 */
288 private long mDisconnectElapsedTimeMillis = 0;
289
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700290 /** The gateway information associated with this call. This stores the original call handle
291 * that the user is attempting to connect to via the gateway, the actual handle to dial in
292 * order to connect the call via the gateway, as well as the package name of the gateway
293 * service. */
Nancy Chen0d3076c2014-07-30 14:45:44 -0700294 private GatewayInfo mGatewayInfo;
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700295
Ihab Awadb78b2762014-07-25 15:16:23 -0700296 private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
297
298 private PhoneAccountHandle mTargetPhoneAccountHandle;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700299
Tony Mak578a4e62015-11-23 11:18:51 +0000300 private UserHandle mInitiatingUser;
301
Ihab Awade6dbc9d2015-03-26 10:33:44 -0700302 private final Handler mHandler = new Handler(Looper.getMainLooper());
Santos Cordon2174fb52014-05-29 08:22:56 -0700303
Santos Cordon12d61822014-07-29 16:02:20 -0700304 private final List<Call> mConferenceableCalls = new ArrayList<>();
305
Santos Cordon61d0f702014-02-19 02:52:23 -0800306 /** The state of the call. */
Ihab Awad6fb37c82014-08-07 19:48:57 -0700307 private int mState;
Santos Cordon61d0f702014-02-19 02:52:23 -0800308
309 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700310 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800311
Nancy Chen201b4372014-09-08 14:18:24 -0700312 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700313 * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
Nancy Chen201b4372014-09-08 14:18:24 -0700314 */
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700315 private int mHandlePresentation;
316
317 /** The caller display name (CNAP) set by the connection service. */
318 private String mCallerDisplayName;
319
Nancy Chen201b4372014-09-08 14:18:24 -0700320 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700321 * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
Nancy Chen201b4372014-09-08 14:18:24 -0700322 */
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700323 private int mCallerDisplayNamePresentation;
324
Ben Gilad0407fb22014-01-09 16:18:41 -0800325 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700326 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800327 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700328 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700329
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700330 private boolean mIsEmergencyCall;
331
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700332 private boolean mSpeakerphoneOn;
333
Srikanth Chintala5bf75022017-02-03 10:10:35 +0530334 private boolean mIsDisconnectingChildCall = false;
335
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700336 /**
337 * Tracks the video states which were applicable over the duration of a call.
Ihab Awad6fb37c82014-08-07 19:48:57 -0700338 * See {@link VideoProfile} for a list of valid video states.
Tyler Gunn5fb78402015-04-29 10:09:40 -0700339 * <p>
340 * Video state history is tracked when the call is active, and when a call is rejected or
341 * missed.
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700342 */
343 private int mVideoStateHistory;
344
Tyler Gunnc4abd912014-07-08 14:22:10 -0700345 private int mVideoState;
346
Ben Gilad61925612014-03-11 19:06:36 -0700347 /**
Ihab Awad6fb37c82014-08-07 19:48:57 -0700348 * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED.
Andrew Lee701dc002014-09-11 21:29:12 -0700349 * See {@link android.telecom.DisconnectCause}.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700350 */
Andrew Lee701dc002014-09-11 21:29:12 -0700351 private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700352
Santos Cordonb3907b32015-05-27 17:39:59 -0700353 private Bundle mIntentExtras = new Bundle();
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700354
Tyler Gunnbbd78a72017-04-30 14:16:07 -0700355 /**
356 * The {@link Intent} which originally created this call. Only populated when we are putting a
357 * call into a pending state and need to pick up initiation of the call later.
358 */
359 private Intent mOriginalCallIntent = null;
360
Jay Shraunera82c8f72014-08-14 15:49:16 -0700361 /** Set of listeners on this call.
362 *
363 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
364 * load factor before resizing, 1 means we only expect a single thread to
365 * access the map so make only a single shard
366 */
367 private final Set<Listener> mListeners = Collections.newSetFromMap(
368 new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
Santos Cordon766d04f2014-05-06 10:28:25 -0700369
Sailesh Nepal664837f2014-07-14 16:31:51 -0700370 private CreateConnectionProcessor mCreateConnectionProcessor;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700371
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700372 /** Caller information retrieved from the latest contact query. */
373 private CallerInfo mCallerInfo;
374
375 /** The latest token used with a contact info query. */
376 private int mQueryToken = 0;
377
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700378 /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */
Andrew Lee5be64bc2014-09-08 18:35:15 -0700379 private boolean mRingbackRequested = false;
Ihab Awadcb387ac2014-05-28 16:49:38 -0700380
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700381 /** Whether direct-to-voicemail query is pending. */
382 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700383
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800384 private int mConnectionCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700385
Tyler Gunn571d5e62016-03-15 15:55:18 -0700386 private int mConnectionProperties;
387
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800388 private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
389
Santos Cordona1610702014-06-04 20:22:56 -0700390 private boolean mIsConference = false;
391
Hall Liu32587202015-11-18 11:10:08 -0800392 private final boolean mShouldAttachToExistingConnection;
393
Santos Cordona1610702014-06-04 20:22:56 -0700394 private Call mParentCall = null;
395
396 private List<Call> mChildCalls = new LinkedList<>();
397
Ihab Awadff7493a2014-06-10 13:47:44 -0700398 /** Set of text message responses allowed for this call, if applicable. */
399 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
400
401 /** Whether an attempt has been made to load the text message responses. */
402 private boolean mCannedSmsResponsesLoadingStarted = false;
403
Ihab Awad6fb37c82014-08-07 19:48:57 -0700404 private IVideoProvider mVideoProvider;
Tyler Gunn86014fc2015-06-12 14:31:25 -0700405 private VideoProviderProxy mVideoProviderProxy;
Nancy Chena65d41f2014-06-24 12:06:03 -0700406
Andrew Lee5be64bc2014-09-08 18:35:15 -0700407 private boolean mIsVoipAudioMode;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700408 private StatusHints mStatusHints;
Santos Cordonb3907b32015-05-27 17:39:59 -0700409 private Bundle mExtras;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700410 private final ConnectionServiceRepository mRepository;
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700411 private final Context mContext;
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800412 private final CallsManager mCallsManager;
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700413 private final ClockProxy mClockProxy;
Ihab Awade6dbc9d2015-03-26 10:33:44 -0700414 private final TelecomSystem.SyncRoot mLock;
Tyler Gunn8452be02015-09-17 09:57:02 -0700415 private final String mId;
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700416 private String mConnectionId;
Hall Liu32587202015-11-18 11:10:08 -0800417 private Analytics.CallInfo mAnalytics;
Tyler Gunnad972422017-11-13 13:36:35 -0800418 private char mPlayingDtmfTone;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700419
Santos Cordon88a4a602014-09-29 19:32:21 -0700420 private boolean mWasConferencePreviouslyMerged = false;
Kazuya Ohshiro0063a192017-09-29 14:52:17 +0900421 private boolean mWasHighDefAudio = false;
Santos Cordon88a4a602014-09-29 19:32:21 -0700422
Santos Cordon92694512015-04-23 14:47:28 -0700423 // For conferences which support merge/swap at their level, we retain a notion of an active
424 // call. This is used for BluetoothPhoneService. In order to support hold/merge, it must have
425 // the notion of the current "active" call within the conference call. This maintains the
426 // "active" call and switches every time the user hits "swap".
Santos Cordon88a4a602014-09-29 19:32:21 -0700427 private Call mConferenceLevelActiveCall = null;
428
Tyler Gunnaa479372014-10-07 10:18:42 -0700429 private boolean mIsLocallyDisconnecting = false;
430
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700431 /**
Tyler Gunn7a1f8232015-12-03 19:52:59 -0800432 * Tracks the current call data usage as reported by the video provider.
433 */
434 private long mCallDataUsage = DATA_USAGE_NOT_SET;
435
Tony Makd29d9372016-02-08 18:23:41 +0000436 private boolean mIsWorkCall;
Tony Mak3ae2dc82016-01-07 11:49:38 +0000437
Tyler Gunn5d66e1d2018-01-25 20:22:49 -0800438 /**
439 * Tracks whether this {@link Call}'s {@link #getTargetPhoneAccount()} has
440 * {@link PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set.
441 */
442 private boolean mUseCallRecordingTone;
443
Hall Liu4b1759d2016-02-02 18:17:40 -0800444 // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed.
445 private boolean mIsNewOutgoingCallIntentBroadcastDone = false;
446
Tyler Gunndb821912016-02-16 14:35:25 -0800447 /**
448 * Indicates whether the call is remotely held. A call is considered remotely held when
449 * {@link #onConnectionEvent(String)} receives the {@link Connection#EVENT_ON_HOLD_TONE_START}
450 * event.
451 */
452 private boolean mIsRemotelyHeld = false;
453
Tyler Gunn7a1f8232015-12-03 19:52:59 -0800454 /**
Tyler Gunnacb3bc82017-01-09 09:43:56 -0800455 * Indicates whether the {@link PhoneAccount} associated with this call is self-managed.
456 * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED} for more information.
457 */
458 private boolean mIsSelfManaged = false;
459
460 /**
Tyler Gunnfa1e97e2016-04-25 21:36:37 -0700461 * Indicates whether the {@link PhoneAccount} associated with this call supports video calling.
462 * {@code True} if the phone account supports video calling, {@code false} otherwise.
463 */
464 private boolean mIsVideoCallingSupported = false;
465
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700466 private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
467
Tyler Gunnfa1e97e2016-04-25 21:36:37 -0700468 /**
Tyler Gunn9b618b82016-10-17 15:54:35 -0700469 * For {@link Connection}s or {@link android.telecom.Conference}s added via a ConnectionManager
470 * using the {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
471 * Connection)} or {@link android.telecom.ConnectionService#addConference(Conference)},
472 * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
473 * originally created it.
474 *
475 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID} for more information.
476 */
477 private String mOriginalConnectionId;
478
479 /**
Hall Liudd68bc32017-01-25 17:14:23 -0800480 * Two pairs of {@link android.os.ParcelFileDescriptor}s that handle RTT text communication
481 * between the in-call app and the connection service. If both non-null, this call should be
482 * treated as an RTT call.
483 * Each array should be of size 2. First one is the read side and the second one is the write
484 * side.
485 */
486 private ParcelFileDescriptor[] mInCallToConnectionServiceStreams;
487 private ParcelFileDescriptor[] mConnectionServiceToInCallStreams;
Hall Liu825f6ad2018-01-29 17:34:54 -0800488
489 /**
Hall Liu825f6ad2018-01-29 17:34:54 -0800490 * True if we're supposed to start this call with RTT, either due to the master switch or due
491 * to an extra.
492 */
493 private boolean mDidRequestToStartWithRtt = false;
Hall Liudd68bc32017-01-25 17:14:23 -0800494 /**
495 * Integer constant from {@link android.telecom.Call.RttCall}. Describes the current RTT mode.
496 */
497 private int mRttMode;
Hall Liu3a75c622017-11-20 17:45:33 -0800498 /**
499 * True if the call was ever an RTT call.
500 */
501 private boolean mWasEverRtt = false;
Hall Liudd68bc32017-01-25 17:14:23 -0800502
503 /**
Hall Liuaeece4e2017-02-14 16:42:12 -0800504 * Integer indicating the remote RTT request ID that is pending a response from the user.
505 */
506 private int mPendingRttRequestId = INVALID_RTT_REQUEST_ID;
507
508 /**
Tyler Gunn15d096b2017-05-03 15:02:25 -0700509 * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -0800510 * int, Bundle, boolean)}, contains the call which this call is being handed over to.
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700511 */
Tyler Gunn141ef582017-05-26 13:38:13 -0700512 private Call mHandoverDestinationCall = null;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700513
514 /**
Tyler Gunn15d096b2017-05-03 15:02:25 -0700515 * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -0800516 * int, Bundle, boolean)}, contains the call which this call is being handed over from.
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700517 */
Tyler Gunn141ef582017-05-26 13:38:13 -0700518 private Call mHandoverSourceCall = null;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700519
520 /**
Tyler Gunn141ef582017-05-26 13:38:13 -0700521 * Indicates the current state of this call if it is in the process of a handover.
Tyler Gunn15d096b2017-05-03 15:02:25 -0700522 */
Tyler Gunn141ef582017-05-26 13:38:13 -0700523 private int mHandoverState = HandoverState.HANDOVER_NONE;
Tyler Gunn15d096b2017-05-03 15:02:25 -0700524
525 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800526 * Persists the specified parameters and initializes the new instance.
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700527 * @param context The context.
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700528 * @param repository The connection service repository.
Ben Gilad0407fb22014-01-09 16:18:41 -0800529 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700530 * @param gatewayInfo Gateway information to use for the call.
Ihab Awadb78b2762014-07-25 15:16:23 -0700531 * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700532* This account must be one that was registered with the
533* {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
Ihab Awadb78b2762014-07-25 15:16:23 -0700534 * @param targetPhoneAccountHandle Account information to use for the call. This account must be
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700535* one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
Hall Liu32587202015-11-18 11:10:08 -0800536 * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700537* or CALL_DIRECTION_UNKNOWN.
Hall Liu32587202015-11-18 11:10:08 -0800538 * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700539 * @param clockProxy
Ben Gilad0407fb22014-01-09 16:18:41 -0800540 */
Ihab Awad8de76912015-02-17 12:25:52 -0800541 public Call(
Tyler Gunn8452be02015-09-17 09:57:02 -0700542 String callId,
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700543 Context context,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800544 CallsManager callsManager,
Ihab Awade6dbc9d2015-03-26 10:33:44 -0700545 TelecomSystem.SyncRoot lock,
Sailesh Nepal664837f2014-07-14 16:31:51 -0700546 ConnectionServiceRepository repository,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700547 ContactsAsyncHelper contactsAsyncHelper,
Ihab Awadabcbce42015-04-07 14:04:01 -0700548 CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700549 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
Sailesh Nepal664837f2014-07-14 16:31:51 -0700550 Uri handle,
551 GatewayInfo gatewayInfo,
Ihab Awadb78b2762014-07-25 15:16:23 -0700552 PhoneAccountHandle connectionManagerPhoneAccountHandle,
553 PhoneAccountHandle targetPhoneAccountHandle,
Hall Liu32587202015-11-18 11:10:08 -0800554 int callDirection,
555 boolean shouldAttachToExistingConnection,
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700556 boolean isConference,
557 ClockProxy clockProxy) {
Tyler Gunn8452be02015-09-17 09:57:02 -0700558 mId = callId;
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700559 mConnectionId = callId;
Santos Cordona1610702014-06-04 20:22:56 -0700560 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700561 mContext = context;
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800562 mCallsManager = callsManager;
Ihab Awade6dbc9d2015-03-26 10:33:44 -0700563 mLock = lock;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700564 mRepository = repository;
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700565 mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
Nancy Chen0d3076c2014-07-30 14:45:44 -0700566 setHandle(handle);
Dianne Hackborn6df5eb42015-10-26 17:16:21 -0700567 mPostDialDigits = handle != null
Hall Liuf62630a2015-10-27 14:53:39 -0700568 ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
Yorke Lee33501632014-03-17 19:24:12 -0700569 mGatewayInfo = gatewayInfo;
Santos Cordonf193ba42014-09-12 06:37:39 -0700570 setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
571 setTargetPhoneAccount(targetPhoneAccountHandle);
Hall Liu32587202015-11-18 11:10:08 -0800572 mCallDirection = callDirection;
Santos Cordona1610702014-06-04 20:22:56 -0700573 mIsConference = isConference;
Hall Liu32587202015-11-18 11:10:08 -0800574 mShouldAttachToExistingConnection = shouldAttachToExistingConnection
575 || callDirection == CALL_DIRECTION_INCOMING;
Ihab Awadff7493a2014-06-10 13:47:44 -0700576 maybeLoadCannedSmsResponses();
Hall Liu32587202015-11-18 11:10:08 -0800577 mAnalytics = new Analytics.CallInfo();
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700578 mClockProxy = clockProxy;
579 mCreationTimeMillis = mClockProxy.currentTimeMillis();
Ben Gilad0407fb22014-01-09 16:18:41 -0800580 }
581
Tyler Gunnd92e7372015-01-09 15:59:45 -0800582 /**
583 * Persists the specified parameters and initializes the new instance.
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700584 * @param context The context.
Tyler Gunnd92e7372015-01-09 15:59:45 -0800585 * @param repository The connection service repository.
586 * @param handle The handle to dial.
587 * @param gatewayInfo Gateway information to use for the call.
588 * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700589* This account must be one that was registered with the
590* {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
Tyler Gunnd92e7372015-01-09 15:59:45 -0800591 * @param targetPhoneAccountHandle Account information to use for the call. This account must be
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700592* one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
Hall Liu32587202015-11-18 11:10:08 -0800593 * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700594* or CALL_DIRECTION_UNKNOWN
Hall Liu32587202015-11-18 11:10:08 -0800595 * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700596* connection, regardless of whether it's incoming or outgoing.
Tyler Gunnd92e7372015-01-09 15:59:45 -0800597 * @param connectTimeMillis The connection time of the call.
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700598 * @param clockProxy
Tyler Gunnd92e7372015-01-09 15:59:45 -0800599 */
600 Call(
Tyler Gunn8452be02015-09-17 09:57:02 -0700601 String callId,
Tyler Gunnd92e7372015-01-09 15:59:45 -0800602 Context context,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800603 CallsManager callsManager,
Ihab Awade6dbc9d2015-03-26 10:33:44 -0700604 TelecomSystem.SyncRoot lock,
Tyler Gunnd92e7372015-01-09 15:59:45 -0800605 ConnectionServiceRepository repository,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700606 ContactsAsyncHelper contactsAsyncHelper,
Ihab Awadabcbce42015-04-07 14:04:01 -0700607 CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700608 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
Tyler Gunnd92e7372015-01-09 15:59:45 -0800609 Uri handle,
610 GatewayInfo gatewayInfo,
611 PhoneAccountHandle connectionManagerPhoneAccountHandle,
612 PhoneAccountHandle targetPhoneAccountHandle,
Hall Liu32587202015-11-18 11:10:08 -0800613 int callDirection,
614 boolean shouldAttachToExistingConnection,
Tyler Gunnd92e7372015-01-09 15:59:45 -0800615 boolean isConference,
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700616 long connectTimeMillis,
617 long connectElapsedTimeMillis,
618 ClockProxy clockProxy) {
Tyler Gunn8452be02015-09-17 09:57:02 -0700619 this(callId, context, callsManager, lock, repository, contactsAsyncHelper,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700620 callerInfoAsyncQueryFactory, phoneNumberUtilsAdapter, handle, gatewayInfo,
Hall Liu32587202015-11-18 11:10:08 -0800621 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700622 shouldAttachToExistingConnection, isConference, clockProxy);
Tyler Gunnd92e7372015-01-09 15:59:45 -0800623
624 mConnectTimeMillis = connectTimeMillis;
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700625 mConnectElapsedTimeMillis = connectElapsedTimeMillis;
Hall Liu32587202015-11-18 11:10:08 -0800626 mAnalytics.setCallStartTime(connectTimeMillis);
Tyler Gunnd92e7372015-01-09 15:59:45 -0800627 }
628
Ihab Awad8de76912015-02-17 12:25:52 -0800629 public void addListener(Listener listener) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700630 mListeners.add(listener);
631 }
632
Ihab Awad8de76912015-02-17 12:25:52 -0800633 public void removeListener(Listener listener) {
Jay Shraunera82c8f72014-08-14 15:49:16 -0700634 if (listener != null) {
635 mListeners.remove(listener);
636 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700637 }
638
Hall Liu32587202015-11-18 11:10:08 -0800639 public void initAnalytics() {
640 int analyticsDirection;
641 switch (mCallDirection) {
642 case CALL_DIRECTION_OUTGOING:
643 analyticsDirection = Analytics.OUTGOING_DIRECTION;
644 break;
645 case CALL_DIRECTION_INCOMING:
646 analyticsDirection = Analytics.INCOMING_DIRECTION;
647 break;
648 case CALL_DIRECTION_UNKNOWN:
649 case CALL_DIRECTION_UNDEFINED:
650 default:
651 analyticsDirection = Analytics.UNKNOWN_DIRECTION;
652 }
653 mAnalytics = Analytics.initiateCallAnalytics(mId, analyticsDirection);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700654 Log.addEvent(this, LogUtils.Events.CREATED);
Hall Liu32587202015-11-18 11:10:08 -0800655 }
656
657 public Analytics.CallInfo getAnalytics() {
658 return mAnalytics;
659 }
660
Santos Cordon2685dab2015-04-17 17:12:09 -0700661 public void destroy() {
Brad Ebingera219bf92017-05-24 11:06:49 -0700662 // We should not keep these bitmaps around because the Call objects may be held for logging
663 // purposes.
664 // TODO: Make a container object that only stores the information we care about for Logging.
665 if (mCallerInfo != null) {
666 mCallerInfo.cachedPhotoIcon = null;
667 mCallerInfo.cachedPhoto = null;
668 }
Hall Liuea67a762018-02-09 16:48:57 -0800669
670 Log.addEvent(this, LogUtils.Events.DESTROYED);
671 }
672
673 private void closeRttStreams() {
674 if (mConnectionServiceToInCallStreams != null) {
675 for (ParcelFileDescriptor fd : mConnectionServiceToInCallStreams) {
676 if (fd != null) {
677 try {
678 fd.close();
679 } catch (IOException e) {
680 // ignore
681 }
Hall Liu825f6ad2018-01-29 17:34:54 -0800682 }
683 }
684 }
Hall Liuea67a762018-02-09 16:48:57 -0800685 if (mInCallToConnectionServiceStreams != null) {
686 for (ParcelFileDescriptor fd : mInCallToConnectionServiceStreams) {
687 if (fd != null) {
688 try {
689 fd.close();
690 } catch (IOException e) {
691 // ignore
692 }
693 }
694 }
695 }
Santos Cordon2685dab2015-04-17 17:12:09 -0700696 }
697
Santos Cordon61d0f702014-02-19 02:52:23 -0800698 /** {@inheritDoc} */
Santos Cordon12d61822014-07-29 16:02:20 -0700699 @Override
700 public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700701 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700702 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
703 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700704 }
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700705
Tyler Gunn571d5e62016-03-15 15:55:18 -0700706 return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), %s, %s]",
Tyler Gunn8452be02015-09-17 09:57:02 -0700707 mId,
Santos Cordon66fe8822014-10-10 16:10:58 -0700708 CallState.toString(mState),
709 component,
710 Log.piiHandle(mHandle),
Tyler Gunn959afe02015-04-07 16:34:45 -0700711 getVideoStateDescription(getVideoState()),
Santos Cordon66fe8822014-10-10 16:10:58 -0700712 getChildCalls().size(),
713 getParentCall() != null,
Tyler Gunn571d5e62016-03-15 15:55:18 -0700714 Connection.capabilitiesToString(getConnectionCapabilities()),
715 Connection.propertiesToString(getConnectionProperties()));
Santos Cordon61d0f702014-02-19 02:52:23 -0800716 }
717
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700718 @Override
719 public String getDescription() {
Tyler Gunn2b17f232017-03-08 08:51:00 -0800720 StringBuilder s = new StringBuilder();
721 if (isSelfManaged()) {
722 s.append("SelfMgd Call");
723 } else if (isExternalCall()) {
724 s.append("External Call");
725 } else {
726 s.append("Call");
727 }
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700728 s.append(getId());
729 s.append(" [");
730 s.append(SimpleDateFormat.getDateTimeInstance().format(new Date(getCreationTimeMillis())));
731 s.append("]");
732 s.append(isIncoming() ? "(MT - incoming)" : "(MO - outgoing)");
Tyler Gunn2b17f232017-03-08 08:51:00 -0800733 s.append("\n\tVia PhoneAccount: ");
734 PhoneAccountHandle targetPhoneAccountHandle = getTargetPhoneAccount();
735 if (targetPhoneAccountHandle != null) {
736 s.append(targetPhoneAccountHandle);
737 s.append(" (");
738 s.append(getTargetPhoneAccountLabel());
739 s.append(")");
740 } else {
741 s.append("not set");
742 }
743
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700744 s.append("\n\tTo address: ");
745 s.append(Log.piiHandle(getHandle()));
Tyler Gunn26d478f2017-06-20 11:16:57 -0700746 s.append(" Presentation: ");
747 switch (getHandlePresentation()) {
748 case TelecomManager.PRESENTATION_ALLOWED:
749 s.append("Allowed");
750 break;
751 case TelecomManager.PRESENTATION_PAYPHONE:
752 s.append("Payphone");
753 break;
754 case TelecomManager.PRESENTATION_RESTRICTED:
755 s.append("Restricted");
756 break;
757 case TelecomManager.PRESENTATION_UNKNOWN:
758 s.append("Unknown");
759 break;
760 default:
761 s.append("<undefined>");
762 }
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700763 s.append("\n");
764 return s.toString();
765 }
766
Tyler Gunn959afe02015-04-07 16:34:45 -0700767 /**
768 * Builds a debug-friendly description string for a video state.
769 * <p>
770 * A = audio active, T = video transmission active, R = video reception active, P = video
771 * paused.
772 *
773 * @param videoState The video state.
774 * @return A string indicating which bits are set in the video state.
775 */
776 private String getVideoStateDescription(int videoState) {
777 StringBuilder sb = new StringBuilder();
778 sb.append("A");
779
Tyler Gunn5b882492015-06-03 10:03:13 -0700780 if (VideoProfile.isTransmissionEnabled(videoState)) {
Tyler Gunn959afe02015-04-07 16:34:45 -0700781 sb.append("T");
782 }
783
Tyler Gunn5b882492015-06-03 10:03:13 -0700784 if (VideoProfile.isReceptionEnabled(videoState)) {
Tyler Gunn959afe02015-04-07 16:34:45 -0700785 sb.append("R");
786 }
787
Tyler Gunn5b882492015-06-03 10:03:13 -0700788 if (VideoProfile.isPaused(videoState)) {
Tyler Gunn959afe02015-04-07 16:34:45 -0700789 sb.append("P");
790 }
791
792 return sb.toString();
793 }
794
Pengquan Mengd7f92cf2017-11-21 17:52:42 -0800795 @Override
796 public ConnectionServiceFocusManager.ConnectionServiceFocus getConnectionServiceWrapper() {
797 return mConnectionService;
798 }
799
Brad Ebingerd931a012015-10-21 12:54:08 -0700800 @VisibleForTesting
801 public int getState() {
Santos Cordon0fbe6322014-08-14 04:04:25 -0700802 return mState;
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800803 }
804
Tyler Gunn77ff6802018-03-29 07:05:02 +0000805 /**
806 * Determines if this {@link Call} can receive call focus via the
807 * {@link ConnectionServiceFocusManager}.
808 * Only top-level calls and non-external calls are eligible.
809 * @return {@code true} if this call is focusable, {@code false} otherwise.
810 */
811 @Override
812 public boolean isFocusable() {
813 boolean isChild = getParentCall() != null;
814 return !isChild && !isExternalCall();
815 }
816
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800817 private boolean shouldContinueProcessingAfterDisconnect() {
818 // Stop processing once the call is active.
819 if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
820 return false;
821 }
822
Brad Ebinger3ff5edd2016-07-11 15:09:55 -0700823 // Only Redial a Call in the case of it being an Emergency Call.
824 if(!isEmergencyCall()) {
825 return false;
826 }
827
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800828 // Make sure that there are additional connection services to process.
829 if (mCreateConnectionProcessor == null
830 || !mCreateConnectionProcessor.isProcessingComplete()
831 || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
832 return false;
833 }
834
835 if (mDisconnectCause == null) {
836 return false;
837 }
838
839 // Continue processing if the current attempt failed or timed out.
840 return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
841 mCreateConnectionProcessor.isCallTimedOut();
842 }
843
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800844 /**
Tyler Gunn8452be02015-09-17 09:57:02 -0700845 * Returns the unique ID for this call as it exists in Telecom.
846 * @return The call ID.
847 */
848 public String getId() {
849 return mId;
850 }
851
852 /**
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700853 * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
854 * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
855 * @return The call ID with an appended attempt id.
856 */
857 public String getConnectionId() {
858 if(mCreateConnectionProcessor != null) {
859 mConnectionId = mId + "_" +
860 String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
861 return mConnectionId;
862 } else {
863 return mConnectionId;
864 }
865 }
866
867 /**
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800868 * Sets the call state. Although there exists the notion of appropriate state transitions
869 * (see {@link CallState}), in practice those expectations break down when cellular systems
870 * misbehave and they do this very often. The result is that we do not enforce state transitions
871 * and instead keep the code resilient to unexpected state changes.
872 */
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700873 public void setState(int newState, String tag) {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700874 if (mState != newState) {
875 Log.v(this, "setState %s -> %s", mState, newState);
Sailesh Nepal27679262014-12-02 17:07:00 -0800876
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800877 if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
878 Log.w(this, "continuing processing disconnected call with another service");
Sailesh Nepal27679262014-12-02 17:07:00 -0800879 mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
880 return;
881 }
882
Brad Ebingerb079fc52017-07-07 15:59:13 -0700883 updateVideoHistoryViaState(mState, newState);
884
Sailesh Nepal810735e2014-03-18 18:15:46 -0700885 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700886 maybeLoadCannedSmsResponses();
Santos Cordon672321e2014-08-21 14:38:58 -0700887
Santos Cordon40f9b172014-11-17 10:49:29 -0800888 if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
889 if (mConnectTimeMillis == 0) {
890 // We check to see if mConnectTime is already set to prevent the
891 // call from resetting active time when it goes in and out of
892 // ACTIVE/ON_HOLD
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700893 mConnectTimeMillis = mClockProxy.currentTimeMillis();
894 mConnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
Hall Liu32587202015-11-18 11:10:08 -0800895 mAnalytics.setCallStartTime(mConnectTimeMillis);
Santos Cordon40f9b172014-11-17 10:49:29 -0800896 }
897
898 // We're clearly not disconnected, so reset the disconnected time.
899 mDisconnectTimeMillis = 0;
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700900 mDisconnectElapsedTimeMillis = 0;
Santos Cordon40f9b172014-11-17 10:49:29 -0800901 } else if (mState == CallState.DISCONNECTED) {
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700902 mDisconnectTimeMillis = mClockProxy.currentTimeMillis();
903 mDisconnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
Hall Liu32587202015-11-18 11:10:08 -0800904 mAnalytics.setCallEndTime(mDisconnectTimeMillis);
Santos Cordon5dfd3802014-10-08 20:32:13 -0700905 setLocallyDisconnecting(false);
Santos Cordon672321e2014-08-21 14:38:58 -0700906 fixParentAfterDisconnect();
907 }
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700908
909 // Log the state transition event
910 String event = null;
911 Object data = null;
912 switch (newState) {
913 case CallState.ACTIVE:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700914 event = LogUtils.Events.SET_ACTIVE;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700915 break;
916 case CallState.CONNECTING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700917 event = LogUtils.Events.SET_CONNECTING;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700918 break;
919 case CallState.DIALING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700920 event = LogUtils.Events.SET_DIALING;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700921 break;
Tyler Gunn1e37be52016-07-11 08:54:23 -0700922 case CallState.PULLING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700923 event = LogUtils.Events.SET_PULLING;
Tyler Gunn1e37be52016-07-11 08:54:23 -0700924 break;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700925 case CallState.DISCONNECTED:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700926 event = LogUtils.Events.SET_DISCONNECTED;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700927 data = getDisconnectCause();
928 break;
929 case CallState.DISCONNECTING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700930 event = LogUtils.Events.SET_DISCONNECTING;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700931 break;
932 case CallState.ON_HOLD:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700933 event = LogUtils.Events.SET_HOLD;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700934 break;
935 case CallState.SELECT_PHONE_ACCOUNT:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700936 event = LogUtils.Events.SET_SELECT_PHONE_ACCOUNT;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700937 break;
938 case CallState.RINGING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700939 event = LogUtils.Events.SET_RINGING;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700940 break;
941 }
942 if (event != null) {
943 // The string data should be just the tag.
944 String stringData = tag;
945 if (data != null) {
946 // If data exists, add it to tag. If no tag, just use data.toString().
947 stringData = stringData == null ? data.toString() : stringData + "> " + data;
948 }
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700949 Log.addEvent(this, event, stringData);
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700950 }
Tej Singh95daef72018-02-05 18:34:19 -0800951 int statsdDisconnectCause = (newState == CallState.DISCONNECTED) ?
952 getDisconnectCause().getCode() : DisconnectCause.UNKNOWN;
953 StatsLog.write(StatsLog.CALL_STATE_CHANGED, newState, statsdDisconnectCause,
954 isSelfManaged(), isExternalCall());
Sailesh Nepal810735e2014-03-18 18:15:46 -0700955 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800956 }
957
Andrew Lee5be64bc2014-09-08 18:35:15 -0700958 void setRingbackRequested(boolean ringbackRequested) {
959 mRingbackRequested = ringbackRequested;
Ihab Awadcb387ac2014-05-28 16:49:38 -0700960 for (Listener l : mListeners) {
Andrew Lee5be64bc2014-09-08 18:35:15 -0700961 l.onRingbackRequested(this, mRingbackRequested);
Ihab Awadcb387ac2014-05-28 16:49:38 -0700962 }
963 }
964
Andrew Lee5be64bc2014-09-08 18:35:15 -0700965 boolean isRingbackRequested() {
966 return mRingbackRequested;
Ihab Awadcb387ac2014-05-28 16:49:38 -0700967 }
968
Hall Liuada03012015-10-26 15:44:00 -0700969 @VisibleForTesting
970 public boolean isConference() {
Santos Cordon672321e2014-08-21 14:38:58 -0700971 return mIsConference;
972 }
973
Ihab Awad8de76912015-02-17 12:25:52 -0800974 public Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800975 return mHandle;
976 }
977
Hall Liub8ce19c2015-10-14 15:13:51 -0700978 public String getPostDialDigits() {
979 return mPostDialDigits;
980 }
981
Brad Ebingerbaf52ba2016-03-21 16:38:32 -0700982 public String getViaNumber() {
983 return mViaNumber;
984 }
985
986 public void setViaNumber(String viaNumber) {
987 // If at any point the via number is not empty throughout the call, save that via number.
988 if (!TextUtils.isEmpty(viaNumber)) {
989 mViaNumber = viaNumber;
990 }
991 }
992
Tyler Gunnaee51f22018-03-09 02:33:54 +0000993 public int getHandlePresentation() {
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700994 return mHandlePresentation;
995 }
996
Nancy Chen0d3076c2014-07-30 14:45:44 -0700997
998 void setHandle(Uri handle) {
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700999 setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
Nancy Chen0d3076c2014-07-30 14:45:44 -07001000 }
1001
Ihab Awad8de76912015-02-17 12:25:52 -08001002 public void setHandle(Uri handle, int presentation) {
Sailesh Nepale8ecb982014-07-11 17:19:42 -07001003 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
Sailesh Nepale8ecb982014-07-11 17:19:42 -07001004 mHandlePresentation = presentation;
Santos Cordonecaaeac2014-11-05 20:59:04 -08001005 if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
1006 mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
1007 mHandle = null;
1008 } else {
1009 mHandle = handle;
Yorke Lee96e662d2014-11-06 13:02:49 -08001010 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
1011 && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
1012 // If the number is actually empty, set it to null, unless this is a
1013 // SCHEME_VOICEMAIL uri which always has an empty number.
Santos Cordonecaaeac2014-11-05 20:59:04 -08001014 mHandle = null;
1015 }
1016 }
1017
Roshan Pius17010e52015-08-13 13:05:35 -07001018 // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
1019 // call, it will remain so for the rest of it's lifetime.
1020 if (!mIsEmergencyCall) {
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07001021 mIsEmergencyCall = mHandle != null &&
1022 mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
1023 mHandle.getSchemeSpecificPart());
Roshan Pius17010e52015-08-13 13:05:35 -07001024 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001025 startCallerInfoLookup();
Sailesh Nepale8ecb982014-07-11 17:19:42 -07001026 for (Listener l : mListeners) {
1027 l.onHandleChanged(this);
1028 }
1029 }
1030 }
1031
Tyler Gunn2b17f232017-03-08 08:51:00 -08001032 public String getCallerDisplayName() {
Sailesh Nepale8ecb982014-07-11 17:19:42 -07001033 return mCallerDisplayName;
1034 }
1035
Tyler Gunn2b17f232017-03-08 08:51:00 -08001036 public int getCallerDisplayNamePresentation() {
Sailesh Nepale8ecb982014-07-11 17:19:42 -07001037 return mCallerDisplayNamePresentation;
1038 }
1039
1040 void setCallerDisplayName(String callerDisplayName, int presentation) {
1041 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
1042 presentation != mCallerDisplayNamePresentation) {
1043 mCallerDisplayName = callerDisplayName;
1044 mCallerDisplayNamePresentation = presentation;
1045 for (Listener l : mListeners) {
1046 l.onCallerDisplayNameChanged(this);
1047 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001048 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001049 }
1050
Ihab Awad8de76912015-02-17 12:25:52 -08001051 public String getName() {
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001052 return mCallerInfo == null ? null : mCallerInfo.name;
1053 }
1054
Bryce Lee41045b42015-08-05 13:10:39 -07001055 public String getPhoneNumber() {
1056 return mCallerInfo == null ? null : mCallerInfo.phoneNumber;
1057 }
1058
Ihab Awad8de76912015-02-17 12:25:52 -08001059 public Bitmap getPhotoIcon() {
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001060 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
1061 }
1062
Ihab Awad8de76912015-02-17 12:25:52 -08001063 public Drawable getPhoto() {
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001064 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
1065 }
1066
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001067 /**
Andrew Lee701dc002014-09-11 21:29:12 -07001068 * @param disconnectCause The reason for the disconnection, represented by
1069 * {@link android.telecom.DisconnectCause}.
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001070 */
Ihab Awad8de76912015-02-17 12:25:52 -08001071 public void setDisconnectCause(DisconnectCause disconnectCause) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001072 // TODO: Consider combining this method with a setDisconnected() method that is totally
1073 // separate from setState.
Hall Liu32587202015-11-18 11:10:08 -08001074 mAnalytics.setCallDisconnectCause(disconnectCause);
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001075 mDisconnectCause = disconnectCause;
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001076 }
1077
Ihab Awad8de76912015-02-17 12:25:52 -08001078 public DisconnectCause getDisconnectCause() {
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001079 return mDisconnectCause;
1080 }
1081
Brad Ebingerf1900072015-11-12 17:25:06 -08001082 @VisibleForTesting
1083 public boolean isEmergencyCall() {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001084 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -08001085 }
1086
Yorke Lee33501632014-03-17 19:24:12 -07001087 /**
1088 * @return The original handle this call is associated with. In-call services should use this
1089 * handle when indicating in their UI the handle that is being called.
1090 */
1091 public Uri getOriginalHandle() {
1092 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
Nancy Chen201b4372014-09-08 14:18:24 -07001093 return mGatewayInfo.getOriginalAddress();
Yorke Lee33501632014-03-17 19:24:12 -07001094 }
1095 return getHandle();
1096 }
1097
Brad Ebinger53855132015-10-30 10:58:19 -07001098 @VisibleForTesting
1099 public GatewayInfo getGatewayInfo() {
Yorke Lee33501632014-03-17 19:24:12 -07001100 return mGatewayInfo;
1101 }
1102
Nancy Chen0d3076c2014-07-30 14:45:44 -07001103 void setGatewayInfo(GatewayInfo gatewayInfo) {
1104 mGatewayInfo = gatewayInfo;
1105 }
1106
Brad Ebingerf1900072015-11-12 17:25:06 -08001107 @VisibleForTesting
1108 public PhoneAccountHandle getConnectionManagerPhoneAccount() {
Ihab Awadb78b2762014-07-25 15:16:23 -07001109 return mConnectionManagerPhoneAccountHandle;
Nancy Chen77d2d0e2014-06-24 12:06:03 -07001110 }
1111
Brad Ebingerf1900072015-11-12 17:25:06 -08001112 @VisibleForTesting
1113 public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001114 if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
1115 mConnectionManagerPhoneAccountHandle = accountHandle;
Ihab Awad69eb0f52014-07-18 11:20:37 -07001116 for (Listener l : mListeners) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001117 l.onConnectionManagerPhoneAccountChanged(this);
1118 }
1119 }
Hall Liu825f6ad2018-01-29 17:34:54 -08001120 checkIfRttCapable();
Ihab Awadb78b2762014-07-25 15:16:23 -07001121 }
1122
Hall Liuada03012015-10-26 15:44:00 -07001123 @VisibleForTesting
1124 public PhoneAccountHandle getTargetPhoneAccount() {
Ihab Awadb78b2762014-07-25 15:16:23 -07001125 return mTargetPhoneAccountHandle;
1126 }
1127
Brad Ebingerf1900072015-11-12 17:25:06 -08001128 @VisibleForTesting
1129 public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001130 if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
1131 mTargetPhoneAccountHandle = accountHandle;
1132 for (Listener l : mListeners) {
1133 l.onTargetPhoneAccountChanged(this);
Ihab Awad69eb0f52014-07-18 11:20:37 -07001134 }
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001135 configureCallAttributes();
Ihab Awad69eb0f52014-07-18 11:20:37 -07001136 }
Tyler Gunnddec5892016-09-28 10:26:13 -07001137 checkIfVideoCapable();
Hall Liu825f6ad2018-01-29 17:34:54 -08001138 checkIfRttCapable();
Nancy Chen53ceedc2014-07-08 18:56:51 -07001139 }
1140
Tyler Gunn2b17f232017-03-08 08:51:00 -08001141 public CharSequence getTargetPhoneAccountLabel() {
1142 if (getTargetPhoneAccount() == null) {
1143 return null;
1144 }
1145 PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1146 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1147
1148 if (phoneAccount == null) {
1149 return null;
1150 }
1151
1152 return phoneAccount.getLabel();
1153 }
1154
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001155 /**
1156 * Determines if this Call should be written to the call log.
1157 * @return {@code true} for managed calls or for self-managed calls which have the
1158 * {@link PhoneAccount#EXTRA_LOG_SELF_MANAGED_CALLS} extra set.
1159 */
1160 public boolean isLoggedSelfManaged() {
1161 if (!isSelfManaged()) {
1162 // Managed calls are always logged.
1163 return true;
1164 }
1165 if (getTargetPhoneAccount() == null) {
1166 return false;
1167 }
1168 PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1169 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1170
1171 if (phoneAccount == null) {
1172 return false;
1173 }
1174
Hall Liu62630f72018-05-10 17:48:59 -07001175 if (getHandle() == null) {
1176 // No point in logging a null-handle call. Some self-managed calls will have this.
1177 return false;
1178 }
1179
Tyler Gunn708a7ba2018-04-04 10:27:43 -07001180 if (!PhoneAccount.SCHEME_SIP.equals(getHandle().getScheme()) &&
1181 !PhoneAccount.SCHEME_TEL.equals(getHandle().getScheme())) {
1182 // Can't log schemes other than SIP or TEL for now.
1183 return false;
1184 }
1185
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001186 return phoneAccount.getExtras() != null && phoneAccount.getExtras().getBoolean(
1187 PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, false);
1188 }
1189
Brad Ebingerd931a012015-10-21 12:54:08 -07001190 @VisibleForTesting
1191 public boolean isIncoming() {
Hall Liu32587202015-11-18 11:10:08 -08001192 return mCallDirection == CALL_DIRECTION_INCOMING;
1193 }
1194
Tyler Gunn4fe861d2016-03-29 19:33:56 -07001195 public boolean isExternalCall() {
1196 return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
1197 Connection.PROPERTY_IS_EXTERNAL_CALL;
1198 }
1199
Tony Makd29d9372016-02-08 18:23:41 +00001200 public boolean isWorkCall() {
1201 return mIsWorkCall;
1202 }
1203
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001204 public boolean isUsingCallRecordingTone() {
1205 return mUseCallRecordingTone;
1206 }
1207
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001208 public boolean isVideoCallingSupported() {
1209 return mIsVideoCallingSupported;
1210 }
1211
Tyler Gunnacb3bc82017-01-09 09:43:56 -08001212 public boolean isSelfManaged() {
1213 return mIsSelfManaged;
1214 }
1215
1216 public void setIsSelfManaged(boolean isSelfManaged) {
1217 mIsSelfManaged = isSelfManaged;
1218
1219 // Connection properties will add/remove the PROPERTY_SELF_MANAGED.
1220 setConnectionProperties(getConnectionProperties());
1221 }
1222
Tyler Gunn141ef582017-05-26 13:38:13 -07001223 public void markFinishedHandoverStateAndCleanup(int handoverState) {
1224 if (mHandoverSourceCall != null) {
1225 mHandoverSourceCall.setHandoverState(handoverState);
1226 } else if (mHandoverDestinationCall != null) {
1227 mHandoverDestinationCall.setHandoverState(handoverState);
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001228 }
Tyler Gunn141ef582017-05-26 13:38:13 -07001229 setHandoverState(handoverState);
1230 maybeCleanupHandover();
1231 }
1232
1233 public void maybeCleanupHandover() {
1234 if (mHandoverSourceCall != null) {
1235 mHandoverSourceCall.setHandoverSourceCall(null);
1236 mHandoverSourceCall.setHandoverDestinationCall(null);
1237 mHandoverSourceCall = null;
1238 } else if (mHandoverDestinationCall != null) {
1239 mHandoverDestinationCall.setHandoverSourceCall(null);
1240 mHandoverDestinationCall.setHandoverDestinationCall(null);
1241 mHandoverDestinationCall = null;
1242 }
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001243 }
1244
1245 public boolean isHandoverInProgress() {
Tyler Gunn141ef582017-05-26 13:38:13 -07001246 return mHandoverSourceCall != null || mHandoverDestinationCall != null;
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001247 }
1248
Tyler Gunn141ef582017-05-26 13:38:13 -07001249 public Call getHandoverDestinationCall() {
1250 return mHandoverDestinationCall;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001251 }
1252
Tyler Gunn141ef582017-05-26 13:38:13 -07001253 public void setHandoverDestinationCall(Call call) {
1254 mHandoverDestinationCall = call;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001255 }
1256
Tyler Gunn141ef582017-05-26 13:38:13 -07001257 public Call getHandoverSourceCall() {
1258 return mHandoverSourceCall;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001259 }
1260
Tyler Gunn141ef582017-05-26 13:38:13 -07001261 public void setHandoverSourceCall(Call call) {
1262 mHandoverSourceCall = call;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001263 }
1264
Tyler Gunn141ef582017-05-26 13:38:13 -07001265 public void setHandoverState(int handoverState) {
1266 Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(),
1267 HandoverState.stateToString(handoverState));
1268 mHandoverState = handoverState;
1269 }
1270
1271 public int getHandoverState() {
1272 return mHandoverState;
Tyler Gunn15d096b2017-05-03 15:02:25 -07001273 }
1274
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001275 private void configureCallAttributes() {
Tony Makd29d9372016-02-08 18:23:41 +00001276 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1277 boolean isWorkCall = false;
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001278 boolean isCallRecordingToneSupported = false;
Tony Makd29d9372016-02-08 18:23:41 +00001279 PhoneAccount phoneAccount =
1280 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1281 if (phoneAccount != null) {
Brad Ebingerc1133602016-01-11 18:03:36 -08001282 final UserHandle userHandle;
1283 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
1284 userHandle = mInitiatingUser;
1285 } else {
Tony Mak3ae2dc82016-01-07 11:49:38 +00001286 userHandle = mTargetPhoneAccountHandle.getUserHandle();
1287 }
Tony Makd29d9372016-02-08 18:23:41 +00001288 if (userHandle != null) {
1289 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle);
Tony Mak3ae2dc82016-01-07 11:49:38 +00001290 }
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001291
1292 isCallRecordingToneSupported = (phoneAccount.hasCapabilities(
1293 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) && phoneAccount.getExtras() != null
1294 && phoneAccount.getExtras().getBoolean(
1295 PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false));
Tony Mak3ae2dc82016-01-07 11:49:38 +00001296 }
Tony Makd29d9372016-02-08 18:23:41 +00001297 mIsWorkCall = isWorkCall;
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001298 mUseCallRecordingTone = isCallRecordingToneSupported;
Tony Mak3ae2dc82016-01-07 11:49:38 +00001299 }
1300
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001301 /**
1302 * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount}
Tyler Gunnddec5892016-09-28 10:26:13 -07001303 * capability and ensures that the video state is updated if the phone account does not support
1304 * video calling.
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001305 */
1306 private void checkIfVideoCapable() {
1307 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
Tyler Gunnddec5892016-09-28 10:26:13 -07001308 if (mTargetPhoneAccountHandle == null) {
1309 // If no target phone account handle is specified, assume we can potentially perform a
1310 // video call; once the phone account is set, we can confirm that it is video capable.
1311 mIsVideoCallingSupported = true;
1312 Log.d(this, "checkIfVideoCapable: no phone account selected; assume video capable.");
1313 return;
1314 }
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001315 PhoneAccount phoneAccount =
1316 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1317 mIsVideoCallingSupported = phoneAccount != null && phoneAccount.hasCapabilities(
1318 PhoneAccount.CAPABILITY_VIDEO_CALLING);
Tyler Gunnddec5892016-09-28 10:26:13 -07001319
1320 if (!mIsVideoCallingSupported && VideoProfile.isVideo(getVideoState())) {
1321 // The PhoneAccount for the Call was set to one which does not support video calling,
1322 // and the current call is configured to be a video call; downgrade to audio-only.
1323 setVideoState(VideoProfile.STATE_AUDIO_ONLY);
1324 Log.d(this, "checkIfVideoCapable: selected phone account doesn't support video.");
1325 }
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001326 }
1327
Hall Liu825f6ad2018-01-29 17:34:54 -08001328 private void checkIfRttCapable() {
1329 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1330 if (mTargetPhoneAccountHandle == null) {
1331 return;
1332 }
1333
1334 // Check both the target phone account and the connection manager phone account -- if
1335 // either support RTT, just set the streams and have them set/unset the RTT property as
1336 // needed.
1337 PhoneAccount phoneAccount =
1338 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1339 PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked(
1340 mConnectionManagerPhoneAccountHandle);
1341 boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities(
1342 PhoneAccount.CAPABILITY_RTT);
1343 boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null
1344 && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT);
1345
1346 if ((isConnectionManagerRttSupported || isRttSupported)
1347 && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) {
1348 // If the phone account got set to an RTT capable one and we haven't set the streams
1349 // yet, do so now.
Hall Liuea67a762018-02-09 16:48:57 -08001350 createRttStreams();
Hall Liu825f6ad2018-01-29 17:34:54 -08001351 Log.i(this, "Setting RTT streams after target phone account selected");
Hall Liu825f6ad2018-01-29 17:34:54 -08001352 }
1353 }
1354
Hall Liu32587202015-11-18 11:10:08 -08001355 boolean shouldAttachToExistingConnection() {
1356 return mShouldAttachToExistingConnection;
Sailesh Nepal810735e2014-03-18 18:15:46 -07001357 }
1358
Ben Gilad0407fb22014-01-09 16:18:41 -08001359 /**
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001360 * Note: This method relies on {@link #mConnectElapsedTimeMillis} and
1361 * {@link #mDisconnectElapsedTimeMillis} which are independent of the wall clock (which could
1362 * change due to clock changes).
Ben Gilad0407fb22014-01-09 16:18:41 -08001363 * @return The "age" of this call object in milliseconds, which typically also represents the
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001364 * period since this call was added to the set pending outgoing calls.
Ben Gilad0407fb22014-01-09 16:18:41 -08001365 */
Hall Liuada03012015-10-26 15:44:00 -07001366 @VisibleForTesting
1367 public long getAgeMillis() {
Santos Cordon40f9b172014-11-17 10:49:29 -08001368 if (mState == CallState.DISCONNECTED &&
1369 (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
1370 mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
1371 // Rejected and missed calls have no age. They're immortal!!
1372 return 0;
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001373 } else if (mConnectElapsedTimeMillis == 0) {
Santos Cordon40f9b172014-11-17 10:49:29 -08001374 // Age is measured in the amount of time the call was active. A zero connect time
1375 // indicates that we never went active, so return 0 for the age.
1376 return 0;
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001377 } else if (mDisconnectElapsedTimeMillis == 0) {
Santos Cordon40f9b172014-11-17 10:49:29 -08001378 // We connected, but have not yet disconnected
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001379 return mClockProxy.elapsedRealtime() - mConnectElapsedTimeMillis;
Santos Cordon40f9b172014-11-17 10:49:29 -08001380 }
1381
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001382 return mDisconnectElapsedTimeMillis - mConnectElapsedTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -08001383 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -08001384
Yorke Leef98fb572014-03-05 10:56:55 -08001385 /**
1386 * @return The time when this call object was created and added to the set of pending outgoing
1387 * calls.
1388 */
Ihab Awad8de76912015-02-17 12:25:52 -08001389 public long getCreationTimeMillis() {
Sailesh Nepal8c85dee2014-04-07 22:21:40 -07001390 return mCreationTimeMillis;
1391 }
1392
Ihab Awad8de76912015-02-17 12:25:52 -08001393 public void setCreationTimeMillis(long time) {
Yorke Lee0c1e4f62014-10-14 16:56:45 -07001394 mCreationTimeMillis = time;
1395 }
1396
Sailesh Nepal8c85dee2014-04-07 22:21:40 -07001397 long getConnectTimeMillis() {
1398 return mConnectTimeMillis;
1399 }
1400
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001401 int getConnectionCapabilities() {
1402 return mConnectionCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -07001403 }
1404
Tyler Gunn571d5e62016-03-15 15:55:18 -07001405 int getConnectionProperties() {
1406 return mConnectionProperties;
1407 }
1408
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001409 void setConnectionCapabilities(int connectionCapabilities) {
1410 setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
Yorke Lee604a90f2014-10-20 12:13:32 -07001411 }
1412
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001413 void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
1414 Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
1415 connectionCapabilities));
1416 if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001417 // If the phone account does not support video calling, and the connection capabilities
1418 // passed in indicate that the call supports video, remove those video capabilities.
1419 if (!isVideoCallingSupported() && doesCallSupportVideo(connectionCapabilities)) {
1420 Log.w(this, "setConnectionCapabilities: attempt to set connection as video " +
1421 "capable when not supported by the phone account.");
1422 connectionCapabilities = removeVideoCapabilities(connectionCapabilities);
1423 }
1424
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001425 int previousCapabilities = mConnectionCapabilities;
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001426 mConnectionCapabilities = connectionCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -07001427 for (Listener l : mListeners) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001428 l.onConnectionCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -07001429 }
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001430
1431 int xorCaps = previousCapabilities ^ mConnectionCapabilities;
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001432 Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE,
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001433 "Current: [%s], Removed [%s], Added [%s]",
1434 Connection.capabilitiesToStringShort(mConnectionCapabilities),
1435 Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
1436 Connection.capabilitiesToStringShort(mConnectionCapabilities & xorCaps));
Santos Cordona1610702014-06-04 20:22:56 -07001437 }
1438 }
1439
Tyler Gunn571d5e62016-03-15 15:55:18 -07001440 void setConnectionProperties(int connectionProperties) {
1441 Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
1442 connectionProperties));
Tyler Gunnacb3bc82017-01-09 09:43:56 -08001443
1444 // Ensure the ConnectionService can't change the state of the self-managed property.
1445 if (isSelfManaged()) {
1446 connectionProperties |= Connection.PROPERTY_SELF_MANAGED;
1447 } else {
1448 connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED;
1449 }
1450
Hall Liudd68bc32017-01-25 17:14:23 -08001451 int changedProperties = mConnectionProperties ^ connectionProperties;
1452
1453 if (changedProperties != 0) {
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001454 int previousProperties = mConnectionProperties;
Tyler Gunn571d5e62016-03-15 15:55:18 -07001455 mConnectionProperties = connectionProperties;
Hall Liuea67a762018-02-09 16:48:57 -08001456 if ((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
1457 Connection.PROPERTY_IS_RTT) {
1458 createRttStreams();
Hall Liue15f6282018-05-31 14:42:01 -07001459 // Call startRtt to pass the RTT pipes down to the connection service.
1460 // They already turned on the RTT property so no request should be sent.
1461 mConnectionService.startRtt(this,
1462 getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
Hall Liu2d738542018-04-06 17:17:00 -07001463 mWasEverRtt = true;
Hall Liuccd793f2018-03-16 14:48:59 -07001464 if (isEmergencyCall()) {
1465 mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
1466 mCallsManager.mute(false);
1467 }
Hall Liuea67a762018-02-09 16:48:57 -08001468 }
Kazuya Ohshiro0063a192017-09-29 14:52:17 +09001469 mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) ==
1470 Connection.PROPERTY_HIGH_DEF_AUDIO;
Hall Liudd68bc32017-01-25 17:14:23 -08001471 boolean didRttChange =
1472 (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
Tyler Gunn571d5e62016-03-15 15:55:18 -07001473 for (Listener l : mListeners) {
Hall Liudd68bc32017-01-25 17:14:23 -08001474 l.onConnectionPropertiesChanged(this, didRttChange);
Tyler Gunn571d5e62016-03-15 15:55:18 -07001475 }
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001476
1477 boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1478 == Connection.PROPERTY_IS_EXTERNAL_CALL;
1479 boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1480 == Connection.PROPERTY_IS_EXTERNAL_CALL;
1481 if (wasExternal != isExternal) {
1482 Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
1483 isExternal);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001484 Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal);
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001485 for (Listener l : mListeners) {
1486 l.onExternalCallChanged(this, isExternal);
1487 }
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001488 }
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001489
Hall Liud7fe6862016-09-09 16:36:14 -07001490 mAnalytics.addCallProperties(mConnectionProperties);
1491
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001492 int xorProps = previousProperties ^ mConnectionProperties;
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001493 Log.addEvent(this, LogUtils.Events.PROPERTY_CHANGE,
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001494 "Current: [%s], Removed [%s], Added [%s]",
1495 Connection.propertiesToStringShort(mConnectionProperties),
1496 Connection.propertiesToStringShort(previousProperties & xorProps),
1497 Connection.propertiesToStringShort(mConnectionProperties & xorProps));
Tyler Gunn571d5e62016-03-15 15:55:18 -07001498 }
1499 }
1500
Hall Liucff959e2016-12-07 14:04:57 -08001501 public int getSupportedAudioRoutes() {
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001502 return mSupportedAudioRoutes;
1503 }
1504
1505 void setSupportedAudioRoutes(int audioRoutes) {
1506 if (mSupportedAudioRoutes != audioRoutes) {
1507 mSupportedAudioRoutes = audioRoutes;
1508 }
1509 }
1510
Brad Ebinger53855132015-10-30 10:58:19 -07001511 @VisibleForTesting
1512 public Call getParentCall() {
Santos Cordona1610702014-06-04 20:22:56 -07001513 return mParentCall;
1514 }
1515
Brad Ebinger53855132015-10-30 10:58:19 -07001516 @VisibleForTesting
1517 public List<Call> getChildCalls() {
Santos Cordona1610702014-06-04 20:22:56 -07001518 return mChildCalls;
1519 }
1520
Brad Ebinger53855132015-10-30 10:58:19 -07001521 @VisibleForTesting
1522 public boolean wasConferencePreviouslyMerged() {
Santos Cordon88a4a602014-09-29 19:32:21 -07001523 return mWasConferencePreviouslyMerged;
1524 }
1525
Srikanth Chintala5bf75022017-02-03 10:10:35 +05301526 public boolean isDisconnectingChildCall() {
1527 return mIsDisconnectingChildCall;
1528 }
1529
1530 /**
1531 * Sets whether this call is a child call.
1532 */
1533 private void maybeSetCallAsDisconnectingChild() {
1534 if (mParentCall != null) {
1535 mIsDisconnectingChildCall = true;
1536 }
1537 }
1538
Brad Ebinger53855132015-10-30 10:58:19 -07001539 @VisibleForTesting
1540 public Call getConferenceLevelActiveCall() {
Santos Cordon88a4a602014-09-29 19:32:21 -07001541 return mConferenceLevelActiveCall;
1542 }
1543
Hall Liuf62630a2015-10-27 14:53:39 -07001544 @VisibleForTesting
1545 public ConnectionServiceWrapper getConnectionService() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001546 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -08001547 }
1548
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001549 /**
1550 * Retrieves the {@link Context} for the call.
1551 *
1552 * @return The {@link Context}.
1553 */
Hall Liu55076712017-02-28 19:59:30 -08001554 public Context getContext() {
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001555 return mContext;
1556 }
1557
Brad Ebingerf1900072015-11-12 17:25:06 -08001558 @VisibleForTesting
1559 public void setConnectionService(ConnectionServiceWrapper service) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001560 Preconditions.checkNotNull(service);
1561
1562 clearConnectionService();
1563
1564 service.incrementAssociatedCallCount();
1565 mConnectionService = service;
Hall Liu32587202015-11-18 11:10:08 -08001566 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001567 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -07001568 }
1569
1570 /**
Tyler Gunn9b618b82016-10-17 15:54:35 -07001571 * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call.
1572 * Removes the call from its former {@link ConnectionServiceWrapper}, ensuring that the
1573 * ConnectionService is NOT unbound if the call count hits zero.
1574 * This is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} and
1575 * {@link Conference} additions via a ConnectionManager.
1576 * The original {@link android.telecom.ConnectionService} will directly add external calls and
1577 * conferences to Telecom as well as the ConnectionManager, which will add to Telecom. In these
1578 * cases since its first added to via the original CS, we want to change the CS responsible for
1579 * the call to the ConnectionManager rather than adding it again as another call/conference.
1580 *
1581 * @param service The new {@link ConnectionServiceWrapper}.
1582 */
1583 public void replaceConnectionService(ConnectionServiceWrapper service) {
1584 Preconditions.checkNotNull(service);
1585
1586 if (mConnectionService != null) {
1587 ConnectionServiceWrapper serviceTemp = mConnectionService;
1588 mConnectionService = null;
1589 serviceTemp.removeCall(this);
1590 serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/);
1591 }
1592
1593 service.incrementAssociatedCallCount();
1594 mConnectionService = service;
1595 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
1596 }
1597
1598 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001599 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -07001600 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001601 void clearConnectionService() {
1602 if (mConnectionService != null) {
1603 ConnectionServiceWrapper serviceTemp = mConnectionService;
1604 mConnectionService = null;
1605 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -07001606
1607 // Decrementing the count can cause the service to unbind, which itself can trigger the
1608 // service-death code. Since the service death code tries to clean up any associated
1609 // calls, we need to make sure to remove that information (e.g., removeCall()) before
1610 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001611 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
1612 // to do.
1613 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -07001614 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -08001615 }
1616
Santos Cordon766d04f2014-05-06 10:28:25 -07001617 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -07001618 * Starts the create connection sequence. Upon completion, there should exist an active
1619 * connection through a connection service (or the call will have failed).
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001620 *
1621 * @param phoneAccountRegistrar The phone account registrar.
Santos Cordon766d04f2014-05-06 10:28:25 -07001622 */
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001623 void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
Hall Liu4b1759d2016-02-02 18:17:40 -08001624 if (mCreateConnectionProcessor != null) {
1625 Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
1626 " due to a race between NewOutgoingCallIntentBroadcaster and " +
1627 "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
1628 "invocation.");
1629 return;
1630 }
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001631 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
1632 phoneAccountRegistrar, mContext);
Sailesh Nepal664837f2014-07-14 16:31:51 -07001633 mCreateConnectionProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -07001634 }
1635
Sailesh Nepal5a73b032014-06-25 15:53:21 -07001636 @Override
Ihab Awad80008452014-08-23 20:35:44 -07001637 public void handleCreateConnectionSuccess(
1638 CallIdMapper idMapper,
1639 ParcelableConnection connection) {
Santos Cordon12d61822014-07-29 16:02:20 -07001640 Log.v(this, "handleCreateConnectionSuccessful %s", connection);
Ihab Awadb78b2762014-07-25 15:16:23 -07001641 setTargetPhoneAccount(connection.getPhoneAccount());
Santos Cordon72890ce2014-07-21 01:32:04 -07001642 setHandle(connection.getHandle(), connection.getHandlePresentation());
1643 setCallerDisplayName(
1644 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001645
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001646 setConnectionCapabilities(connection.getConnectionCapabilities());
Tyler Gunn571d5e62016-03-15 15:55:18 -07001647 setConnectionProperties(connection.getConnectionProperties());
Hall Liue67e3182018-03-13 15:09:38 -07001648 setIsVoipAudioMode(connection.getIsVoipAudioMode());
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001649 setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
Ihab Awad6fb37c82014-08-07 19:48:57 -07001650 setVideoProvider(connection.getVideoProvider());
Tyler Gunn13e8a692016-08-03 18:38:00 -07001651 setVideoState(connection.getVideoState());
Andrew Lee5be64bc2014-09-08 18:35:15 -07001652 setRingbackRequested(connection.isRingbackRequested());
Sailesh Nepal604a4fe2014-08-14 18:03:43 -07001653 setStatusHints(connection.getStatusHints());
Tyler Gunn961694a2016-03-21 16:01:40 -07001654 putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras());
Sailesh Nepal664837f2014-07-14 16:31:51 -07001655
Ihab Awad80008452014-08-23 20:35:44 -07001656 mConferenceableCalls.clear();
1657 for (String id : connection.getConferenceableConnectionIds()) {
1658 mConferenceableCalls.add(idMapper.getCall(id));
1659 }
1660
Hall Liu32587202015-11-18 11:10:08 -08001661 switch (mCallDirection) {
1662 case CALL_DIRECTION_INCOMING:
Hall Liu6d4b66d2016-04-01 16:31:13 -07001663 // Listeners (just CallsManager for now) will be responsible for checking whether
1664 // the call should be blocked.
1665 for (Listener l : mListeners) {
1666 l.onSuccessfulIncomingCall(this);
1667 }
Hall Liu32587202015-11-18 11:10:08 -08001668 break;
1669 case CALL_DIRECTION_OUTGOING:
1670 for (Listener l : mListeners) {
1671 l.onSuccessfulOutgoingCall(this,
1672 getStateFromConnectionState(connection.getState()));
1673 }
1674 break;
1675 case CALL_DIRECTION_UNKNOWN:
1676 for (Listener l : mListeners) {
1677 l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
1678 .getState()));
1679 }
1680 break;
Santos Cordon766d04f2014-05-06 10:28:25 -07001681 }
1682 }
1683
Sailesh Nepal5a73b032014-06-25 15:53:21 -07001684 @Override
Andrew Lee701dc002014-09-11 21:29:12 -07001685 public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
Ihab Awadfb5560d2014-08-18 09:32:51 -07001686 clearConnectionService();
Andrew Lee701dc002014-09-11 21:29:12 -07001687 setDisconnectCause(disconnectCause);
Ihab Awad78a5e6b2015-02-06 10:13:05 -08001688 mCallsManager.markCallAsDisconnected(this, disconnectCause);
Santos Cordon682fe6b2014-05-20 08:56:39 -07001689
Hall Liu32587202015-11-18 11:10:08 -08001690 switch (mCallDirection) {
1691 case CALL_DIRECTION_INCOMING:
1692 for (Listener listener : mListeners) {
1693 listener.onFailedIncomingCall(this);
1694 }
1695 break;
1696 case CALL_DIRECTION_OUTGOING:
1697 for (Listener listener : mListeners) {
1698 listener.onFailedOutgoingCall(this, disconnectCause);
1699 }
1700 break;
1701 case CALL_DIRECTION_UNKNOWN:
1702 for (Listener listener : mListeners) {
1703 listener.onFailedUnknownCall(this);
1704 }
1705 break;
Sailesh Nepal664837f2014-07-14 16:31:51 -07001706 }
Santos Cordon766d04f2014-05-06 10:28:25 -07001707 }
1708
1709 /**
Ihab Awad74549ec2014-03-10 15:33:25 -07001710 * Plays the specified DTMF tone.
1711 */
Tyler Gunnad972422017-11-13 13:36:35 -08001712 @VisibleForTesting
1713 public void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001714 if (mConnectionService == null) {
1715 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -07001716 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001717 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
1718 mConnectionService.playDtmfTone(this, digit);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001719 Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit));
Ihab Awad74549ec2014-03-10 15:33:25 -07001720 }
Tyler Gunnad972422017-11-13 13:36:35 -08001721 mPlayingDtmfTone = digit;
Ihab Awad74549ec2014-03-10 15:33:25 -07001722 }
1723
1724 /**
1725 * Stops playing any currently playing DTMF tone.
1726 */
Tyler Gunnad972422017-11-13 13:36:35 -08001727 @VisibleForTesting
1728 public void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001729 if (mConnectionService == null) {
Jay Shraunerfd4d7b52014-08-14 14:14:43 -07001730 Log.w(this, "stopDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -07001731 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001732 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001733 Log.addEvent(this, LogUtils.Events.STOP_DTMF);
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001734 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -07001735 }
Tyler Gunnad972422017-11-13 13:36:35 -08001736 mPlayingDtmfTone = NO_DTMF_TONE;
1737 }
1738
1739 /**
1740 * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has
1741 * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise.
1742 */
1743 boolean isDtmfTonePlaying() {
1744 return mPlayingDtmfTone != NO_DTMF_TONE;
Ihab Awad74549ec2014-03-10 15:33:25 -07001745 }
1746
Bryce Lee6d962522015-11-30 10:28:37 -08001747 /**
1748 * Silences the ringer.
1749 */
1750 void silence() {
1751 if (mConnectionService == null) {
1752 Log.w(this, "silence() request on a call without a connection service.");
1753 } else {
1754 Log.i(this, "Send silence to connection service for call %s", this);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001755 Log.addEvent(this, LogUtils.Events.SILENCE);
Bryce Lee6d962522015-11-30 10:28:37 -08001756 mConnectionService.silence(this);
1757 }
1758 }
1759
Hall Liu0a6dd302015-12-16 15:06:49 -08001760 @VisibleForTesting
1761 public void disconnect() {
Hall Liu63e690c2017-02-14 18:05:03 -08001762 disconnect(0);
Santos Cordoncf5b2912014-11-05 21:57:54 -08001763 }
1764
Hall Liu220b4192015-12-11 11:33:08 -08001765 @VisibleForTesting
Tyler Gunnf4f05392018-03-26 18:57:59 +00001766 public void disconnect(String reason) {
1767 disconnect(0, reason);
Tyler Gunn587fc272018-02-07 16:07:29 -08001768 }
1769
1770 /**
1771 * Attempts to disconnect the call through the connection service.
1772 */
1773 @VisibleForTesting
Tyler Gunnf4f05392018-03-26 18:57:59 +00001774 public void disconnect(long disconnectionTimeout) {
1775 disconnect(disconnectionTimeout, "internal" /** reason */);
1776 }
1777
1778 /**
1779 * Attempts to disconnect the call through the connection service.
1780 * @param reason the reason for the disconnect; used for logging purposes only. In some cases
1781 * this can be a package name if the disconnect was initiated through an API such
1782 * as TelecomManager.
1783 */
1784 @VisibleForTesting
1785 public void disconnect(long disconnectionTimeout, String reason) {
1786 Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT, reason);
Santos Cordon2685dab2015-04-17 17:12:09 -07001787
Tyler Gunnaa479372014-10-07 10:18:42 -07001788 // Track that the call is now locally disconnecting.
1789 setLocallyDisconnecting(true);
Srikanth Chintala5bf75022017-02-03 10:10:35 +05301790 maybeSetCallAsDisconnectingChild();
Tyler Gunnaa479372014-10-07 10:18:42 -07001791
Santos Cordon92694512015-04-23 14:47:28 -07001792 if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
Jay Shraunerfd4d7b52014-08-14 14:14:43 -07001793 mState == CallState.CONNECTING) {
Santos Cordon682fe6b2014-05-20 08:56:39 -07001794 Log.v(this, "Aborting call %s", this);
Hall Liu63e690c2017-02-14 18:05:03 -08001795 abort(disconnectionTimeout);
Santos Cordon74d420b2014-05-07 14:38:47 -07001796 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Jay Shraunerfd4d7b52014-08-14 14:14:43 -07001797 if (mConnectionService == null) {
1798 Log.e(this, new Exception(), "disconnect() request on a call without a"
1799 + " connection service.");
1800 } else {
1801 Log.i(this, "Send disconnect to connection service for call: %s", this);
1802 // The call isn't officially disconnected until the connection service
1803 // confirms that the call was actually disconnected. Only then is the
1804 // association between call and connection service severed, see
1805 // {@link CallsManager#markCallAsDisconnected}.
1806 mConnectionService.disconnect(this);
1807 }
Santos Cordon049b7b62014-01-30 05:34:26 -08001808 }
1809 }
1810
Hall Liu63e690c2017-02-14 18:05:03 -08001811 void abort(long disconnectionTimeout) {
Sailesh Nepal752cacb2014-11-06 12:35:26 -08001812 if (mCreateConnectionProcessor != null &&
1813 !mCreateConnectionProcessor.isProcessingComplete()) {
Sailesh Nepal664837f2014-07-14 16:31:51 -07001814 mCreateConnectionProcessor.abort();
Santos Cordon92694512015-04-23 14:47:28 -07001815 } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT
Yorke Lee43df90f2014-08-20 10:56:09 -07001816 || mState == CallState.CONNECTING) {
Hall Liu63e690c2017-02-14 18:05:03 -08001817 if (disconnectionTimeout > 0) {
1818 // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0
1819 // milliseconds, do not destroy the call.
1820 // Instead, we announce the cancellation and CallsManager handles
Santos Cordoncf5b2912014-11-05 21:57:54 -08001821 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
1822 // then re-dial them quickly using a gateway, allowing the first call to end
1823 // causes jank. This timeout allows CallsManager to transition the first call into
1824 // the second call so that in-call only ever sees a single call...eliminating the
Hall Liu63e690c2017-02-14 18:05:03 -08001825 // jank altogether. The app will also be able to set the timeout via an extra on
1826 // the ordered broadcast.
Santos Cordoncf5b2912014-11-05 21:57:54 -08001827 for (Listener listener : mListeners) {
Hall Liu63e690c2017-02-14 18:05:03 -08001828 if (listener.onCanceledViaNewOutgoingCallBroadcast(
1829 this, disconnectionTimeout)) {
Santos Cordoncf5b2912014-11-05 21:57:54 -08001830 // The first listener to handle this wins. A return value of true means that
1831 // the listener will handle the disconnection process later and so we
1832 // should not continue it here.
1833 setLocallyDisconnecting(false);
1834 return;
1835 }
1836 }
1837 }
1838
Tyler Gunn504eb832014-10-13 14:15:27 -07001839 handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
Santos Cordon2d0b3312014-08-15 15:04:17 -07001840 } else {
Santos Cordon92694512015-04-23 14:47:28 -07001841 Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING");
Santos Cordon682fe6b2014-05-20 08:56:39 -07001842 }
1843 }
1844
Santos Cordon0b03b4b2014-01-29 18:01:59 -08001845 /**
Santos Cordon61d0f702014-02-19 02:52:23 -08001846 * Answers the call if it is ringing.
Andrew Lee38931d02014-07-16 10:17:36 -07001847 *
1848 * @param videoState The video state in which to answer the call.
Santos Cordon61d0f702014-02-19 02:52:23 -08001849 */
Hall Liu0a6dd302015-12-16 15:06:49 -08001850 @VisibleForTesting
1851 public void answer(int videoState) {
Santos Cordon61d0f702014-02-19 02:52:23 -08001852 // Check to verify that the call is still in the ringing state. A call can change states
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001853 // between the time the user hits 'answer' and Telecom receives the command.
Santos Cordon61d0f702014-02-19 02:52:23 -08001854 if (isRinging("answer")) {
Tyler Gunn13e8a692016-08-03 18:38:00 -07001855 if (!isVideoCallingSupported() && VideoProfile.isVideo(videoState)) {
1856 // Video calling is not supported, yet the InCallService is attempting to answer as
1857 // video. We will simply answer as audio-only.
1858 videoState = VideoProfile.STATE_AUDIO_ONLY;
1859 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001860 // At this point, we are asking the connection service to answer but we don't assume
1861 // that it will work. Instead, we wait until confirmation from the connectino service
Ihab Awad6fb37c82014-08-07 19:48:57 -07001862 // that the call is in a non-STATE_RINGING state before changing the UI. See
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001863 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
Tyler Gunn165ee752016-09-06 13:40:10 -07001864 if (mConnectionService != null) {
1865 mConnectionService.answer(this, videoState);
1866 } else {
1867 Log.e(this, new NullPointerException(),
1868 "answer call failed due to null CS callId=%s", getId());
1869 }
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001870 Log.addEvent(this, LogUtils.Events.REQUEST_ACCEPT);
Santos Cordon61d0f702014-02-19 02:52:23 -08001871 }
1872 }
1873
1874 /**
Pooja Jainad4ebc02017-12-28 14:23:13 +05301875 * Deflects the call if it is ringing.
1876 *
1877 * @param address address to be deflected to.
1878 */
1879 @VisibleForTesting
1880 public void deflect(Uri address) {
1881 // Check to verify that the call is still in the ringing state. A call can change states
1882 // between the time the user hits 'deflect' and Telecomm receives the command.
1883 if (isRinging("deflect")) {
1884 // At this point, we are asking the connection service to deflect but we don't assume
1885 // that it will work. Instead, we wait until confirmation from the connection service
1886 // that the call is in a non-STATE_RINGING state before changing the UI. See
1887 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
1888 mVideoStateHistory |= mVideoState;
1889 if (mConnectionService != null) {
1890 mConnectionService.deflect(this, address);
1891 } else {
1892 Log.e(this, new NullPointerException(),
1893 "deflect call failed due to null CS callId=%s", getId());
1894 }
1895 Log.addEvent(this, LogUtils.Events.REQUEST_DEFLECT, Log.pii(address));
1896 }
1897 }
1898
1899 /**
Santos Cordon61d0f702014-02-19 02:52:23 -08001900 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -07001901 *
1902 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
1903 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -08001904 */
Hall Liu0a6dd302015-12-16 15:06:49 -08001905 @VisibleForTesting
1906 public void reject(boolean rejectWithMessage, String textMessage) {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001907 reject(rejectWithMessage, textMessage, "internal" /** reason */);
Tyler Gunn587fc272018-02-07 16:07:29 -08001908 }
1909
1910 /**
1911 * Rejects the call if it is ringing.
1912 *
1913 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
1914 * @param textMessage An optional text message to send as part of the rejection.
Tyler Gunnf4f05392018-03-26 18:57:59 +00001915 * @param reason The reason for the reject; used for logging purposes. May be a package name
1916 * if the reject is initiated from an API such as TelecomManager.
Tyler Gunn587fc272018-02-07 16:07:29 -08001917 */
1918 @VisibleForTesting
Tyler Gunnf4f05392018-03-26 18:57:59 +00001919 public void reject(boolean rejectWithMessage, String textMessage, String reason) {
Santos Cordon61d0f702014-02-19 02:52:23 -08001920 // Check to verify that the call is still in the ringing state. A call can change states
1921 // between the time the user hits 'reject' and Telecomm receives the command.
1922 if (isRinging("reject")) {
Tyler Gunn5fb78402015-04-29 10:09:40 -07001923 // Ensure video state history tracks video state at time of rejection.
1924 mVideoStateHistory |= mVideoState;
1925
Tyler Gunn165ee752016-09-06 13:40:10 -07001926 if (mConnectionService != null) {
1927 mConnectionService.reject(this, rejectWithMessage, textMessage);
1928 } else {
1929 Log.e(this, new NullPointerException(),
1930 "reject call failed due to null CS callId=%s", getId());
1931 }
Tyler Gunnf4f05392018-03-26 18:57:59 +00001932 Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
Santos Cordon61d0f702014-02-19 02:52:23 -08001933 }
1934 }
1935
1936 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001937 * Puts the call on hold if it is currently active.
1938 */
Pengquan Meng4832f202017-12-20 16:13:04 -08001939 @VisibleForTesting
1940 public void hold() {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001941 hold(null /* reason */);
1942 }
1943
1944 public void hold(String reason) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001945 if (mState == CallState.ACTIVE) {
Tyler Gunn165ee752016-09-06 13:40:10 -07001946 if (mConnectionService != null) {
1947 mConnectionService.hold(this);
1948 } else {
1949 Log.e(this, new NullPointerException(),
1950 "hold call failed due to null CS callId=%s", getId());
1951 }
Tyler Gunnf4f05392018-03-26 18:57:59 +00001952 Log.addEvent(this, LogUtils.Events.REQUEST_HOLD, reason);
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001953 }
1954 }
1955
1956 /**
1957 * Releases the call from hold if it is currently active.
1958 */
Pengquan Meng4832f202017-12-20 16:13:04 -08001959 @VisibleForTesting
1960 public void unhold() {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001961 unhold(null /* reason */);
1962 }
1963
1964 public void unhold(String reason) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001965 if (mState == CallState.ON_HOLD) {
Tyler Gunn165ee752016-09-06 13:40:10 -07001966 if (mConnectionService != null) {
1967 mConnectionService.unhold(this);
1968 } else {
1969 Log.e(this, new NullPointerException(),
1970 "unhold call failed due to null CS callId=%s", getId());
1971 }
Tyler Gunnf4f05392018-03-26 18:57:59 +00001972 Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD, reason);
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001973 }
1974 }
1975
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001976 /** Checks if this is a live call or not. */
Hall Liuf62630a2015-10-27 14:53:39 -07001977 @VisibleForTesting
1978 public boolean isAlive() {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001979 switch (mState) {
Ihab Awad6fb37c82014-08-07 19:48:57 -07001980 case CallState.NEW:
1981 case CallState.RINGING:
1982 case CallState.DISCONNECTED:
1983 case CallState.ABORTED:
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001984 return false;
1985 default:
1986 return true;
1987 }
1988 }
1989
Santos Cordon40f78c22014-04-07 02:11:42 -07001990 boolean isActive() {
Ihab Awad84bfe472014-07-13 17:11:57 -07001991 return mState == CallState.ACTIVE;
Santos Cordon40f78c22014-04-07 02:11:42 -07001992 }
1993
Sailesh Nepal84fa5f82014-04-02 11:01:11 -07001994 Bundle getExtras() {
1995 return mExtras;
1996 }
1997
Tyler Gunn961694a2016-03-21 16:01:40 -07001998 /**
1999 * Adds extras to the extras bundle associated with this {@link Call}.
2000 *
2001 * Note: this method needs to know the source of the extras change (see
2002 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which
2003 * originate from a connection service will only be notified to incall services. Likewise,
2004 * changes originating from the incall services will only notify the connection service of the
2005 * change.
2006 *
2007 * @param source The source of the extras addition.
2008 * @param extras The extras.
2009 */
2010 void putExtras(int source, Bundle extras) {
2011 if (extras == null) {
2012 return;
2013 }
2014 if (mExtras == null) {
2015 mExtras = new Bundle();
2016 }
2017 mExtras.putAll(extras);
2018
Santos Cordonb3907b32015-05-27 17:39:59 -07002019 for (Listener l : mListeners) {
Tyler Gunn961694a2016-03-21 16:01:40 -07002020 l.onExtrasChanged(this, source, extras);
2021 }
2022
2023 // If the change originated from an InCallService, notify the connection service.
2024 if (source == SOURCE_INCALL_SERVICE) {
Tyler Gunn165ee752016-09-06 13:40:10 -07002025 if (mConnectionService != null) {
2026 mConnectionService.onExtrasChanged(this, mExtras);
2027 } else {
2028 Log.e(this, new NullPointerException(),
2029 "putExtras failed due to null CS callId=%s", getId());
2030 }
Tyler Gunn961694a2016-03-21 16:01:40 -07002031 }
2032 }
2033
2034 /**
2035 * Removes extras from the extras bundle associated with this {@link Call}.
2036 *
2037 * Note: this method needs to know the source of the extras change (see
2038 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which
2039 * originate from a connection service will only be notified to incall services. Likewise,
2040 * changes originating from the incall services will only notify the connection service of the
2041 * change.
2042 *
2043 * @param source The source of the extras removal.
2044 * @param keys The extra keys to remove.
2045 */
2046 void removeExtras(int source, List<String> keys) {
2047 if (mExtras == null) {
2048 return;
2049 }
2050 for (String key : keys) {
2051 mExtras.remove(key);
2052 }
2053
2054 for (Listener l : mListeners) {
2055 l.onExtrasRemoved(this, source, keys);
2056 }
2057
2058 // If the change originated from an InCallService, notify the connection service.
2059 if (source == SOURCE_INCALL_SERVICE) {
Tyler Gunn165ee752016-09-06 13:40:10 -07002060 if (mConnectionService != null) {
2061 mConnectionService.onExtrasChanged(this, mExtras);
2062 } else {
2063 Log.e(this, new NullPointerException(),
2064 "removeExtras failed due to null CS callId=%s", getId());
2065 }
Santos Cordonb3907b32015-05-27 17:39:59 -07002066 }
2067 }
2068
Santos Cordonf0f99f32016-02-18 16:13:57 -08002069 @VisibleForTesting
2070 public Bundle getIntentExtras() {
Santos Cordonb3907b32015-05-27 17:39:59 -07002071 return mIntentExtras;
2072 }
2073
2074 void setIntentExtras(Bundle extras) {
2075 mIntentExtras = extras;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -07002076 }
2077
Tyler Gunnbbd78a72017-04-30 14:16:07 -07002078 public Intent getOriginalCallIntent() {
2079 return mOriginalCallIntent;
2080 }
2081
2082 public void setOriginalCallIntent(Intent intent) {
2083 mOriginalCallIntent = intent;
2084 }
2085
Yorke Lee8a736a82014-08-30 17:19:16 -07002086 /**
2087 * @return the uri of the contact associated with this call.
2088 */
Brad Ebingerd931a012015-10-21 12:54:08 -07002089 @VisibleForTesting
2090 public Uri getContactUri() {
Yorke Lee8a736a82014-08-30 17:19:16 -07002091 if (mCallerInfo == null || !mCallerInfo.contactExists) {
Yorke Leea7fa7432014-09-22 13:34:29 -07002092 return getHandle();
Yorke Lee8a736a82014-08-30 17:19:16 -07002093 }
2094 return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey);
2095 }
2096
Santos Cordon5ba7f272014-05-28 13:59:49 -07002097 Uri getRingtone() {
2098 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
2099 }
2100
Evan Charlton352105c2014-06-03 14:10:54 -07002101 void onPostDialWait(String remaining) {
2102 for (Listener l : mListeners) {
2103 l.onPostDialWait(this, remaining);
2104 }
2105 }
2106
Nancy Chena469f762014-12-09 18:29:20 -08002107 void onPostDialChar(char nextChar) {
2108 for (Listener l : mListeners) {
2109 l.onPostDialChar(this, nextChar);
2110 }
2111 }
2112
Evan Charlton352105c2014-06-03 14:10:54 -07002113 void postDialContinue(boolean proceed) {
Tyler Gunn165ee752016-09-06 13:40:10 -07002114 if (mConnectionService != null) {
2115 mConnectionService.onPostDialContinue(this, proceed);
2116 } else {
2117 Log.e(this, new NullPointerException(),
2118 "postDialContinue failed due to null CS callId=%s", getId());
2119 }
Evan Charlton352105c2014-06-03 14:10:54 -07002120 }
2121
Santos Cordon0fbe6322014-08-14 04:04:25 -07002122 void conferenceWith(Call otherCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07002123 if (mConnectionService == null) {
2124 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -07002125 } else {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002126 Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall);
Santos Cordon0fbe6322014-08-14 04:04:25 -07002127 mConnectionService.conference(this, otherCall);
Santos Cordona1610702014-06-04 20:22:56 -07002128 }
2129 }
2130
2131 void splitFromConference() {
Santos Cordon672321e2014-08-21 14:38:58 -07002132 if (mConnectionService == null) {
2133 Log.w(this, "splitting from conference call without a connection service");
2134 } else {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002135 Log.addEvent(this, LogUtils.Events.SPLIT_FROM_CONFERENCE);
Santos Cordon672321e2014-08-21 14:38:58 -07002136 mConnectionService.splitFromConference(this);
2137 }
Santos Cordona1610702014-06-04 20:22:56 -07002138 }
2139
Brad Ebinger53855132015-10-30 10:58:19 -07002140 @VisibleForTesting
2141 public void mergeConference() {
Santos Cordon68059232014-09-04 20:09:42 -07002142 if (mConnectionService == null) {
2143 Log.w(this, "merging conference calls without a connection service.");
Ihab Awad07bc5ee2014-11-12 13:42:52 -08002144 } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002145 Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH);
Santos Cordon68059232014-09-04 20:09:42 -07002146 mConnectionService.mergeConference(this);
Santos Cordon88a4a602014-09-29 19:32:21 -07002147 mWasConferencePreviouslyMerged = true;
Santos Cordon68059232014-09-04 20:09:42 -07002148 }
2149 }
2150
Brad Ebinger53855132015-10-30 10:58:19 -07002151 @VisibleForTesting
2152 public void swapConference() {
Santos Cordon68059232014-09-04 20:09:42 -07002153 if (mConnectionService == null) {
2154 Log.w(this, "swapping conference calls without a connection service.");
Ihab Awad07bc5ee2014-11-12 13:42:52 -08002155 } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002156 Log.addEvent(this, LogUtils.Events.SWAP);
Santos Cordon68059232014-09-04 20:09:42 -07002157 mConnectionService.swapConference(this);
Santos Cordon88a4a602014-09-29 19:32:21 -07002158 switch (mChildCalls.size()) {
2159 case 1:
2160 mConferenceLevelActiveCall = mChildCalls.get(0);
2161 break;
2162 case 2:
2163 // swap
2164 mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
2165 mChildCalls.get(1) : mChildCalls.get(0);
2166 break;
2167 default:
2168 // For anything else 0, or 3+, set it to null since it is impossible to tell.
2169 mConferenceLevelActiveCall = null;
2170 break;
2171 }
Santos Cordon68059232014-09-04 20:09:42 -07002172 }
2173 }
2174
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002175 /**
2176 * Initiates a request to the connection service to pull this call.
2177 * <p>
2178 * This method can only be used for calls that have the
Tyler Gunn571d5e62016-03-15 15:55:18 -07002179 * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and
2180 * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002181 * <p>
2182 * An external call is a representation of a call which is taking place on another device
Santos Cordon0d7a00a2016-07-26 15:33:00 -07002183 * associated with a PhoneAccount on this device. Issuing a request to pull the external call
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002184 * tells the {@link android.telecom.ConnectionService} that it should move the call from the
2185 * other device to this one. An example of this is the IMS multi-endpoint functionality. A
2186 * user may have two phones with the same phone number. If the user is engaged in an active
2187 * call on their first device, the network will inform the second device of that ongoing call in
2188 * the form of an external call. The user may wish to continue their conversation on the second
2189 * device, so will issue a request to pull the call to the second device.
2190 * <p>
2191 * Requests to pull a call which is not external, or a call which is not pullable are ignored.
2192 */
2193 public void pullExternalCall() {
2194 if (mConnectionService == null) {
2195 Log.w(this, "pulling a call without a connection service.");
2196 }
2197
Tyler Gunn571d5e62016-03-15 15:55:18 -07002198 if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) {
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002199 Log.w(this, "pullExternalCall - call %s is not an external call.", mId);
2200 return;
2201 }
2202
2203 if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) {
2204 Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId);
2205 return;
2206 }
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002207 Log.addEvent(this, LogUtils.Events.REQUEST_PULL);
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002208 mConnectionService.pullExternalCall(this);
2209 }
2210
2211 /**
Sanket Padaweb5de1cd2018-01-05 14:27:30 -08002212 * Sends a call event to the {@link ConnectionService} for this call. This function is
2213 * called for event other than {@link Call#EVENT_REQUEST_HANDOVER}
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002214 *
2215 * @param event The call event.
2216 * @param extras Associated extras.
2217 */
2218 public void sendCallEvent(String event, Bundle extras) {
Sanket Padaweb5de1cd2018-01-05 14:27:30 -08002219 sendCallEvent(event, 0/*For Event != EVENT_REQUEST_HANDOVER*/, extras);
2220 }
2221
2222 /**
2223 * Sends a call event to the {@link ConnectionService} for this call.
2224 *
2225 * See {@link Call#sendCallEvent(String, Bundle)}.
2226 *
2227 * @param event The call event.
2228 * @param targetSdkVer SDK version of the app calling this api
2229 * @param extras Associated extras.
2230 */
2231 public void sendCallEvent(String event, int targetSdkVer, Bundle extras) {
Tyler Gunn165ee752016-09-06 13:40:10 -07002232 if (mConnectionService != null) {
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002233 if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) {
Sanket Padaweb5de1cd2018-01-05 14:27:30 -08002234 if (targetSdkVer > Build.VERSION_CODES.O_MR1) {
2235 Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" +
2236 " for API > 27(O-MR1)");
2237 // TODO: Add "return" after DUO team adds new API support for handover
2238 }
2239
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002240 // Handover requests are targeted at Telecom, not the ConnectionService.
2241 if (extras == null) {
2242 Log.w(this, "sendCallEvent: %s event received with null extras.",
2243 android.telecom.Call.EVENT_REQUEST_HANDOVER);
2244 mConnectionService.sendCallEvent(this,
2245 android.telecom.Call.EVENT_HANDOVER_FAILED, null);
2246 return;
2247 }
2248 Parcelable parcelable = extras.getParcelable(
2249 android.telecom.Call.EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE);
2250 if (!(parcelable instanceof PhoneAccountHandle) || parcelable == null) {
2251 Log.w(this, "sendCallEvent: %s event received with invalid handover acct.",
2252 android.telecom.Call.EVENT_REQUEST_HANDOVER);
2253 mConnectionService.sendCallEvent(this,
2254 android.telecom.Call.EVENT_HANDOVER_FAILED, null);
2255 return;
2256 }
2257 PhoneAccountHandle phoneAccountHandle = (PhoneAccountHandle) parcelable;
2258 int videoState = extras.getInt(android.telecom.Call.EXTRA_HANDOVER_VIDEO_STATE,
2259 VideoProfile.STATE_AUDIO_ONLY);
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07002260 Parcelable handoverExtras = extras.getParcelable(
2261 android.telecom.Call.EXTRA_HANDOVER_EXTRAS);
2262 Bundle handoverExtrasBundle = null;
2263 if (handoverExtras instanceof Bundle) {
2264 handoverExtrasBundle = (Bundle) handoverExtras;
2265 }
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08002266 requestHandover(phoneAccountHandle, videoState, handoverExtrasBundle, true);
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002267 } else {
2268 Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
2269 mConnectionService.sendCallEvent(this, event, extras);
2270 }
Tyler Gunn165ee752016-09-06 13:40:10 -07002271 } else {
2272 Log.e(this, new NullPointerException(),
2273 "sendCallEvent failed due to null CS callId=%s", getId());
2274 }
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002275 }
2276
Tyler Gunn76581712017-05-09 14:39:42 -07002277 /**
Sanket Padaweccdf3642017-11-10 14:49:05 -08002278 * Initiates a handover of this Call to the {@link ConnectionService} identified
2279 * by destAcct.
2280 * @param destAcct ConnectionService to which the call should be handed over.
2281 * @param videoState The video state desired after the handover.
2282 * @param extras Extra information to be passed to ConnectionService
2283 */
2284 public void handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras) {
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08002285 requestHandover(destAcct, videoState, extras, false);
Sanket Padaweccdf3642017-11-10 14:49:05 -08002286 }
2287
2288 /**
Tyler Gunn76581712017-05-09 14:39:42 -07002289 * Sets this {@link Call} to has the specified {@code parentCall}. Also sets the parent to
2290 * have this call as a child.
2291 * @param parentCall
2292 */
2293 void setParentAndChildCall(Call parentCall) {
Tyler Gunn988ccc82017-06-01 11:22:10 -07002294 boolean isParentChanging = (mParentCall != parentCall);
Tyler Gunn76581712017-05-09 14:39:42 -07002295 setParentCall(parentCall);
2296 setChildOf(parentCall);
Tyler Gunn988ccc82017-06-01 11:22:10 -07002297 if (isParentChanging) {
2298 notifyParentChanged(parentCall);
2299 }
2300 }
2301
2302 /**
2303 * Notifies listeners when the parent call changes.
2304 * Used by {@link #setParentAndChildCall(Call)}, and in {@link CallsManager}.
2305 * @param parentCall The new parent call for this call.
2306 */
2307 void notifyParentChanged(Call parentCall) {
2308 Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall);
2309 for (Listener l : mListeners) {
2310 l.onParentChanged(this);
2311 }
Tyler Gunn76581712017-05-09 14:39:42 -07002312 }
2313
2314 /**
2315 * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set
2316 * the child.
2317 * TODO: This is only required when adding existing connections as a workaround so that we
2318 * can avoid sending the "onParentChanged" callback until later.
2319 * @param parentCall The new parent call.
2320 */
Santos Cordona1610702014-06-04 20:22:56 -07002321 void setParentCall(Call parentCall) {
2322 if (parentCall == this) {
2323 Log.e(this, new Exception(), "setting the parent to self");
2324 return;
2325 }
Santos Cordon0fbe6322014-08-14 04:04:25 -07002326 if (parentCall == mParentCall) {
2327 // nothing to do
2328 return;
2329 }
Santos Cordona1610702014-06-04 20:22:56 -07002330 if (mParentCall != null) {
2331 mParentCall.removeChildCall(this);
2332 }
2333 mParentCall = parentCall;
Tyler Gunn76581712017-05-09 14:39:42 -07002334 }
2335
2336 /**
2337 * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding
2338 * this call as a child of another call.
2339 * <p>
Tyler Gunn988ccc82017-06-01 11:22:10 -07002340 * Note: if using this method alone, the caller must call {@link #notifyParentChanged(Call)} to
2341 * ensure the InCall UI is updated with the change in parent.
Tyler Gunn76581712017-05-09 14:39:42 -07002342 * @param parentCall The new parent for this call.
2343 */
2344 void setChildOf(Call parentCall) {
Tyler Gunn988ccc82017-06-01 11:22:10 -07002345 if (parentCall != null && !parentCall.getChildCalls().contains(this)) {
Tyler Gunn76581712017-05-09 14:39:42 -07002346 parentCall.addChildCall(this);
Santos Cordona1610702014-06-04 20:22:56 -07002347 }
2348 }
2349
Santos Cordon12d61822014-07-29 16:02:20 -07002350 void setConferenceableCalls(List<Call> conferenceableCalls) {
2351 mConferenceableCalls.clear();
2352 mConferenceableCalls.addAll(conferenceableCalls);
Tyler Gunn703d7cf2014-10-02 15:22:30 -07002353
2354 for (Listener l : mListeners) {
2355 l.onConferenceableCallsChanged(this);
2356 }
Santos Cordon12d61822014-07-29 16:02:20 -07002357 }
2358
Brad Ebinger53855132015-10-30 10:58:19 -07002359 @VisibleForTesting
2360 public List<Call> getConferenceableCalls() {
Santos Cordon12d61822014-07-29 16:02:20 -07002361 return mConferenceableCalls;
2362 }
2363
Brad Ebinger53855132015-10-30 10:58:19 -07002364 @VisibleForTesting
2365 public boolean can(int capability) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -08002366 return (mConnectionCapabilities & capability) == capability;
Santos Cordon68059232014-09-04 20:09:42 -07002367 }
2368
Tyler Gunn571d5e62016-03-15 15:55:18 -07002369 @VisibleForTesting
2370 public boolean hasProperty(int property) {
2371 return (mConnectionProperties & property) == property;
2372 }
2373
Santos Cordona1610702014-06-04 20:22:56 -07002374 private void addChildCall(Call call) {
2375 if (!mChildCalls.contains(call)) {
Santos Cordon88a4a602014-09-29 19:32:21 -07002376 // Set the pseudo-active call to the latest child added to the conference.
2377 // See definition of mConferenceLevelActiveCall for more detail.
2378 mConferenceLevelActiveCall = call;
Santos Cordona1610702014-06-04 20:22:56 -07002379 mChildCalls.add(call);
2380
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002381 Log.addEvent(this, LogUtils.Events.ADD_CHILD, call);
Santos Cordon165c1ce2015-07-10 16:07:59 -07002382
Santos Cordona1610702014-06-04 20:22:56 -07002383 for (Listener l : mListeners) {
2384 l.onChildrenChanged(this);
2385 }
2386 }
2387 }
2388
2389 private void removeChildCall(Call call) {
2390 if (mChildCalls.remove(call)) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002391 Log.addEvent(this, LogUtils.Events.REMOVE_CHILD, call);
Santos Cordona1610702014-06-04 20:22:56 -07002392 for (Listener l : mListeners) {
2393 l.onChildrenChanged(this);
2394 }
2395 }
2396 }
2397
Santos Cordon0b03b4b2014-01-29 18:01:59 -08002398 /**
Ihab Awadff7493a2014-06-10 13:47:44 -07002399 * Return whether the user can respond to this {@code Call} via an SMS message.
2400 *
2401 * @return true if the "Respond via SMS" feature should be enabled
2402 * for this incoming call.
2403 *
2404 * The general rule is that we *do* allow "Respond via SMS" except for
2405 * the few (relatively rare) cases where we know for sure it won't
2406 * work, namely:
2407 * - a bogus or blank incoming number
2408 * - a call from a SIP address
2409 * - a "call presentation" that doesn't allow the number to be revealed
2410 *
2411 * In all other cases, we allow the user to respond via SMS.
2412 *
2413 * Note that this behavior isn't perfect; for example we have no way
2414 * to detect whether the incoming call is from a landline (with most
2415 * networks at least), so we still enable this feature even though
2416 * SMSes to that number will silently fail.
2417 */
2418 boolean isRespondViaSmsCapable() {
2419 if (mState != CallState.RINGING) {
2420 return false;
2421 }
2422
2423 if (getHandle() == null) {
2424 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
2425 // other words, the user should not be able to see the incoming phone number.
2426 return false;
2427 }
2428
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07002429 if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
Ihab Awadff7493a2014-06-10 13:47:44 -07002430 // The incoming number is actually a URI (i.e. a SIP address),
2431 // not a regular PSTN phone number, and we can't send SMSes to
2432 // SIP addresses.
2433 // (TODO: That might still be possible eventually, though. Is
2434 // there some SIP-specific equivalent to sending a text message?)
2435 return false;
2436 }
2437
2438 // Is there a valid SMS application on the phone?
Tyler Gunn91d43cf2014-09-17 12:19:39 -07002439 if (SmsApplication.getDefaultRespondViaMessageApplication(mContext,
Ihab Awadff7493a2014-06-10 13:47:44 -07002440 true /*updateIfNeeded*/) == null) {
2441 return false;
2442 }
2443
2444 // TODO: with some carriers (in certain countries) you *can* actually
2445 // tell whether a given number is a mobile phone or not. So in that
2446 // case we could potentially return false here if the incoming call is
2447 // from a land line.
2448
2449 // If none of the above special cases apply, it's OK to enable the
2450 // "Respond via SMS" feature.
2451 return true;
2452 }
2453
2454 List<String> getCannedSmsResponses() {
2455 return mCannedSmsResponses;
2456 }
2457
2458 /**
Santos Cordon672321e2014-08-21 14:38:58 -07002459 * We need to make sure that before we move a call to the disconnected state, it no
2460 * longer has any parent/child relationships. We want to do this to ensure that the InCall
Tyler Gunn7cc70b42014-09-12 22:17:27 -07002461 * Service always has the right data in the right order. We also want to do it in telecom so
Santos Cordon672321e2014-08-21 14:38:58 -07002462 * that the insurance policy lives in the framework side of things.
2463 */
2464 private void fixParentAfterDisconnect() {
Tyler Gunn76581712017-05-09 14:39:42 -07002465 setParentAndChildCall(null);
Santos Cordon672321e2014-08-21 14:38:58 -07002466 }
2467
2468 /**
Santos Cordon61d0f702014-02-19 02:52:23 -08002469 * @return True if the call is ringing, else logs the action name.
2470 */
2471 private boolean isRinging(String actionName) {
2472 if (mState == CallState.RINGING) {
2473 return true;
2474 }
2475
Sailesh Nepalf1c191d2014-03-07 18:17:39 -08002476 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -08002477 return false;
2478 }
2479
Ben Gilad8e55d1d2014-02-26 16:25:56 -08002480 @SuppressWarnings("rawtypes")
2481 private void decrementAssociatedCallCount(ServiceBinder binder) {
2482 if (binder != null) {
2483 binder.decrementAssociatedCallCount();
2484 }
2485 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002486
2487 /**
2488 * Looks up contact information based on the current handle.
2489 */
2490 private void startCallerInfoLookup() {
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002491 mCallerInfo = null;
Hall Liu5b70c1c2016-03-03 18:42:57 -08002492 mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener);
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002493 }
2494
2495 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -07002496 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002497 * that was made.
2498 *
2499 * @param callerInfo The new caller information to set.
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002500 */
Hall Liu5b70c1c2016-03-03 18:42:57 -08002501 private void setCallerInfo(Uri handle, CallerInfo callerInfo) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002502 Trace.beginSection("setCallerInfo");
Shigeru Muraif2373e12016-09-12 10:26:23 +09002503 if (callerInfo == null) {
2504 Log.i(this, "CallerInfo lookup returned null, skipping update");
2505 return;
2506 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -07002507
Srikanth Chintala3bf22282017-09-06 10:24:26 +05302508 if ((handle != null) && !handle.equals(mHandle)) {
Hall Liu5b70c1c2016-03-03 18:42:57 -08002509 Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring.");
2510 return;
Santos Cordon99c8a6f2014-05-28 18:28:47 -07002511 }
Hall Liu5b70c1c2016-03-03 18:42:57 -08002512
2513 mCallerInfo = callerInfo;
2514 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
2515
2516 if (mCallerInfo.contactDisplayPhotoUri == null ||
2517 mCallerInfo.cachedPhotoIcon != null || mCallerInfo.cachedPhoto != null) {
2518 for (Listener l : mListeners) {
2519 l.onCallerInfoChanged(this);
2520 }
2521 }
2522
Yorke Leee4a9c412014-11-14 16:59:42 -08002523 Trace.endSection();
Santos Cordon99c8a6f2014-05-28 18:28:47 -07002524 }
2525
Victor Chang9e7f98a2016-01-04 16:03:27 +00002526 public CallerInfo getCallerInfo() {
Yorke Lee6f3f7af2014-07-11 10:59:46 -07002527 return mCallerInfo;
2528 }
2529
Ihab Awadff7493a2014-06-10 13:47:44 -07002530 private void maybeLoadCannedSmsResponses() {
Hall Liu32587202015-11-18 11:10:08 -08002531 if (mCallDirection == CALL_DIRECTION_INCOMING
2532 && isRespondViaSmsCapable()
2533 && !mCannedSmsResponsesLoadingStarted) {
Ihab Awadff7493a2014-06-10 13:47:44 -07002534 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
2535 mCannedSmsResponsesLoadingStarted = true;
Ihab Awad8de76912015-02-17 12:25:52 -08002536 mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
Ihab Awadff7493a2014-06-10 13:47:44 -07002537 new Response<Void, List<String>>() {
2538 @Override
2539 public void onResult(Void request, List<String>... result) {
2540 if (result.length > 0) {
2541 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
2542 mCannedSmsResponses = result[0];
2543 for (Listener l : mListeners) {
2544 l.onCannedSmsResponsesLoaded(Call.this);
2545 }
2546 }
2547 }
2548
2549 @Override
2550 public void onError(Void request, int code, String msg) {
2551 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
2552 msg);
2553 }
Tyler Gunn91d43cf2014-09-17 12:19:39 -07002554 },
2555 mContext
Ihab Awadff7493a2014-06-10 13:47:44 -07002556 );
2557 } else {
2558 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
2559 }
2560 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07002561
2562 /**
2563 * Sets speakerphone option on when call begins.
2564 */
2565 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
2566 mSpeakerphoneOn = startWithSpeakerphone;
2567 }
2568
2569 /**
2570 * Returns speakerphone option.
2571 *
2572 * @return Whether or not speakerphone should be set automatically when call begins.
2573 */
2574 public boolean getStartWithSpeakerphoneOn() {
2575 return mSpeakerphoneOn;
2576 }
Andrew Leee9a77652014-06-26 13:07:57 -07002577
Hall Liu825f6ad2018-01-29 17:34:54 -08002578 public void setRequestedToStartWithRtt() {
2579 mDidRequestToStartWithRtt = true;
2580 }
2581
Hall Liuaeece4e2017-02-14 16:42:12 -08002582 public void stopRtt() {
2583 if (mConnectionService != null) {
2584 mConnectionService.stopRtt(this);
2585 } else {
2586 // If this gets called by the in-call app before the connection service is set, we'll
2587 // just ignore it since it's really not supposed to happen.
2588 Log.w(this, "stopRtt() called before connection service is set.");
2589 }
2590 }
2591
2592 public void sendRttRequest() {
Hall Liuea67a762018-02-09 16:48:57 -08002593 createRttStreams();
Hall Liuaeece4e2017-02-14 16:42:12 -08002594 mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2595 }
2596
Hall Liu825f6ad2018-01-29 17:34:54 -08002597 private boolean areRttStreamsInitialized() {
2598 return mInCallToConnectionServiceStreams != null
Hall Liudd68bc32017-01-25 17:14:23 -08002599 && mConnectionServiceToInCallStreams != null;
Hall Liu825f6ad2018-01-29 17:34:54 -08002600 }
2601
Hall Liuea67a762018-02-09 16:48:57 -08002602 public void createRttStreams() {
2603 if (!areRttStreamsInitialized()) {
2604 Log.i(this, "Initializing RTT streams");
Hall Liudd68bc32017-01-25 17:14:23 -08002605 try {
2606 mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe();
2607 mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe();
2608 } catch (IOException e) {
2609 Log.e(this, e, "Failed to create pipes for RTT call.");
2610 }
Hall Liudd68bc32017-01-25 17:14:23 -08002611 }
2612 }
2613
Hall Liuaeece4e2017-02-14 16:42:12 -08002614 public void onRttConnectionFailure(int reason) {
Hall Liu825f6ad2018-01-29 17:34:54 -08002615 Log.i(this, "Got RTT initiation failure with reason %d", reason);
Hall Liuaeece4e2017-02-14 16:42:12 -08002616 for (Listener l : mListeners) {
2617 l.onRttInitiationFailure(this, reason);
2618 }
2619 }
2620
2621 public void onRemoteRttRequest() {
2622 if (isRttCall()) {
2623 Log.w(this, "Remote RTT request on a call that's already RTT");
2624 return;
2625 }
2626
2627 mPendingRttRequestId = mCallsManager.getNextRttRequestId();
2628 for (Listener l : mListeners) {
2629 l.onRemoteRttRequest(this, mPendingRttRequestId);
2630 }
2631 }
2632
2633 public void handleRttRequestResponse(int id, boolean accept) {
2634 if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) {
2635 Log.w(this, "Response received to a nonexistent RTT request: %d", id);
2636 return;
2637 }
2638 if (id != mPendingRttRequestId) {
2639 Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId);
2640 return;
2641 }
Hall Liuaeece4e2017-02-14 16:42:12 -08002642 if (accept) {
Hall Liuea67a762018-02-09 16:48:57 -08002643 createRttStreams();
Hall Liuaeece4e2017-02-14 16:42:12 -08002644 Log.i(this, "RTT request %d accepted.", id);
2645 mConnectionService.respondToRttRequest(
2646 this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2647 } else {
2648 Log.i(this, "RTT request %d rejected.", id);
2649 mConnectionService.respondToRttRequest(this, null, null);
2650 }
2651 }
2652
Hall Liudd68bc32017-01-25 17:14:23 -08002653 public boolean isRttCall() {
2654 return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
2655 }
2656
Hall Liu3a75c622017-11-20 17:45:33 -08002657 public boolean wasEverRttCall() {
2658 return mWasEverRtt;
2659 }
2660
Hall Liudd68bc32017-01-25 17:14:23 -08002661 public ParcelFileDescriptor getCsToInCallRttPipeForCs() {
2662 return mConnectionServiceToInCallStreams == null ? null
2663 : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX];
2664 }
2665
2666 public ParcelFileDescriptor getInCallToCsRttPipeForCs() {
2667 return mInCallToConnectionServiceStreams == null ? null
2668 : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX];
2669 }
2670
2671 public ParcelFileDescriptor getCsToInCallRttPipeForInCall() {
2672 return mConnectionServiceToInCallStreams == null ? null
2673 : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX];
2674 }
2675
2676 public ParcelFileDescriptor getInCallToCsRttPipeForInCall() {
2677 return mInCallToConnectionServiceStreams == null ? null
2678 : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX];
2679 }
2680
2681 public int getRttMode() {
2682 return mRttMode;
2683 }
2684
Andrew Leee9a77652014-06-26 13:07:57 -07002685 /**
Andrew Lee3bcf9352014-07-23 12:36:05 -07002686 * Sets a video call provider for the call.
Andrew Leee9a77652014-06-26 13:07:57 -07002687 */
Ihab Awad6fb37c82014-08-07 19:48:57 -07002688 public void setVideoProvider(IVideoProvider videoProvider) {
Tyler Gunn86014fc2015-06-12 14:31:25 -07002689 Log.v(this, "setVideoProvider");
2690
Ravi Palurib4088ee2016-12-20 20:00:00 +05302691 if (mVideoProviderProxy != null) {
2692 mVideoProviderProxy.clearVideoCallback();
2693 mVideoProviderProxy = null;
2694 }
2695
Tyler Gunn86014fc2015-06-12 14:31:25 -07002696 if (videoProvider != null ) {
2697 try {
Tyler Gunn1bf0e6b2016-11-30 15:19:13 -08002698 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this,
2699 mCallsManager);
Tyler Gunn86014fc2015-06-12 14:31:25 -07002700 } catch (RemoteException ignored) {
2701 // Ignore RemoteException.
2702 }
Tyler Gunn86014fc2015-06-12 14:31:25 -07002703 }
2704
Ihab Awad6fb37c82014-08-07 19:48:57 -07002705 mVideoProvider = videoProvider;
Tyler Gunn86014fc2015-06-12 14:31:25 -07002706
Nancy Chena65d41f2014-06-24 12:06:03 -07002707 for (Listener l : mListeners) {
Andrew Lee3bcf9352014-07-23 12:36:05 -07002708 l.onVideoCallProviderChanged(Call.this);
Nancy Chena65d41f2014-06-24 12:06:03 -07002709 }
2710 }
2711
2712 /**
Tyler Gunn86014fc2015-06-12 14:31:25 -07002713 * @return The {@link Connection.VideoProvider} binder.
Nancy Chena65d41f2014-06-24 12:06:03 -07002714 */
Ihab Awad6fb37c82014-08-07 19:48:57 -07002715 public IVideoProvider getVideoProvider() {
Tyler Gunn86014fc2015-06-12 14:31:25 -07002716 if (mVideoProviderProxy == null) {
2717 return null;
2718 }
2719
2720 return mVideoProviderProxy.getInterface();
2721 }
2722
2723 /**
2724 * @return The {@link VideoProviderProxy} for this call.
2725 */
2726 public VideoProviderProxy getVideoProviderProxy() {
2727 return mVideoProviderProxy;
Andrew Leee9a77652014-06-26 13:07:57 -07002728 }
Tyler Gunne19cc002014-07-01 11:32:53 -07002729
2730 /**
Tyler Gunnc4abd912014-07-08 14:22:10 -07002731 * The current video state for the call.
Tyler Gunn5b882492015-06-03 10:03:13 -07002732 * See {@link VideoProfile} for a list of valid video states.
Tyler Gunnc4abd912014-07-08 14:22:10 -07002733 */
2734 public int getVideoState() {
2735 return mVideoState;
2736 }
2737
2738 /**
Tyler Gunn0a388fc2014-07-17 12:21:17 -07002739 * Returns the video states which were applicable over the duration of a call.
Ihab Awad6fb37c82014-08-07 19:48:57 -07002740 * See {@link VideoProfile} for a list of valid video states.
Tyler Gunn0a388fc2014-07-17 12:21:17 -07002741 *
2742 * @return The video states applicable over the duration of the call.
2743 */
2744 public int getVideoStateHistory() {
2745 return mVideoStateHistory;
2746 }
2747
2748 /**
2749 * Determines the current video state for the call.
2750 * For an outgoing call determines the desired video state for the call.
Tyler Gunn5b882492015-06-03 10:03:13 -07002751 * Valid values: see {@link VideoProfile}
Tyler Gunnc4abd912014-07-08 14:22:10 -07002752 *
Tyler Gunn0a388fc2014-07-17 12:21:17 -07002753 * @param videoState The video state for the call.
Tyler Gunnc4abd912014-07-08 14:22:10 -07002754 */
2755 public void setVideoState(int videoState) {
Tyler Gunn13e8a692016-08-03 18:38:00 -07002756 // If the phone account associated with this call does not support video calling, then we
2757 // will automatically set the video state to audio-only.
2758 if (!isVideoCallingSupported()) {
Tyler Gunnddec5892016-09-28 10:26:13 -07002759 Log.d(this, "setVideoState: videoState=%s defaulted to audio (video not supported)",
2760 VideoProfile.videoStateToString(videoState));
Tyler Gunn13e8a692016-08-03 18:38:00 -07002761 videoState = VideoProfile.STATE_AUDIO_ONLY;
2762 }
2763
Brad Ebingerb079fc52017-07-07 15:59:13 -07002764 // Track Video State history during the duration of the call.
2765 // Only update the history when the call is active or disconnected. This ensures we do
2766 // not include the video state history when:
Tyler Gunneb39aa22015-04-03 11:47:28 -07002767 // - Call is incoming (but not answered).
2768 // - Call it outgoing (but not answered).
2769 // We include the video state when disconnected to ensure that rejected calls reflect the
2770 // appropriate video state.
Brad Ebingerb079fc52017-07-07 15:59:13 -07002771 // For all other times we add to the video state history, see #setState.
Tyler Gunneb39aa22015-04-03 11:47:28 -07002772 if (isActive() || getState() == CallState.DISCONNECTED) {
2773 mVideoStateHistory = mVideoStateHistory | videoState;
2774 }
Tyler Gunn0a388fc2014-07-17 12:21:17 -07002775
Hall Liu9696c212016-06-24 16:09:02 -07002776 int previousVideoState = mVideoState;
Tyler Gunnc4abd912014-07-08 14:22:10 -07002777 mVideoState = videoState;
Hall Liu9696c212016-06-24 16:09:02 -07002778 if (mVideoState != previousVideoState) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002779 Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED,
Hall Liu9696c212016-06-24 16:09:02 -07002780 VideoProfile.videoStateToString(videoState));
2781 for (Listener l : mListeners) {
2782 l.onVideoStateChanged(this, previousVideoState, mVideoState);
2783 }
Andrew Lee4a796602014-07-11 17:23:03 -07002784 }
Hall Liu4640c4f2016-06-22 17:13:20 -07002785
2786 if (VideoProfile.isVideo(videoState)) {
2787 mAnalytics.setCallIsVideo(true);
2788 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07002789 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07002790
Andrew Lee5be64bc2014-09-08 18:35:15 -07002791 public boolean getIsVoipAudioMode() {
2792 return mIsVoipAudioMode;
Sailesh Nepal7e669572014-07-08 21:29:12 -07002793 }
2794
Andrew Lee5be64bc2014-09-08 18:35:15 -07002795 public void setIsVoipAudioMode(boolean audioModeIsVoip) {
2796 mIsVoipAudioMode = audioModeIsVoip;
Sailesh Nepal7e669572014-07-08 21:29:12 -07002797 for (Listener l : mListeners) {
Andrew Lee5be64bc2014-09-08 18:35:15 -07002798 l.onIsVoipAudioModeChanged(this);
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07002799 }
2800 }
2801
2802 public StatusHints getStatusHints() {
2803 return mStatusHints;
2804 }
2805
2806 public void setStatusHints(StatusHints statusHints) {
2807 mStatusHints = statusHints;
2808 for (Listener l : mListeners) {
2809 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07002810 }
2811 }
Sailesh Nepal9d58de52014-07-18 14:53:19 -07002812
Yorke Lee9250e5f2014-10-01 13:39:09 -07002813 public boolean isUnknown() {
Hall Liu32587202015-11-18 11:10:08 -08002814 return mCallDirection == CALL_DIRECTION_UNKNOWN;
Yorke Lee9250e5f2014-10-01 13:39:09 -07002815 }
2816
Tyler Gunnaa479372014-10-07 10:18:42 -07002817 /**
2818 * Determines if this call is in a disconnecting state.
2819 *
2820 * @return {@code true} if this call is locally disconnecting.
2821 */
2822 public boolean isLocallyDisconnecting() {
2823 return mIsLocallyDisconnecting;
2824 }
2825
2826 /**
2827 * Sets whether this call is in a disconnecting state.
2828 *
2829 * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
2830 */
2831 private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
2832 mIsLocallyDisconnecting = isLocallyDisconnecting;
2833 }
2834
Tony Mak578a4e62015-11-23 11:18:51 +00002835 /**
2836 * @return user handle of user initiating the outgoing call.
2837 */
2838 public UserHandle getInitiatingUser() {
2839 return mInitiatingUser;
2840 }
2841
2842 /**
2843 * Set the user handle of user initiating the outgoing call.
2844 * @param initiatingUser
2845 */
2846 public void setInitiatingUser(UserHandle initiatingUser) {
2847 Preconditions.checkNotNull(initiatingUser);
2848 mInitiatingUser = initiatingUser;
2849 }
2850
Santos Cordon0fbe6322014-08-14 04:04:25 -07002851 static int getStateFromConnectionState(int state) {
Santos Cordon72890ce2014-07-21 01:32:04 -07002852 switch (state) {
Ihab Awada02bef52014-08-13 18:18:42 -07002853 case Connection.STATE_INITIALIZING:
2854 return CallState.CONNECTING;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002855 case Connection.STATE_ACTIVE:
Santos Cordon72890ce2014-07-21 01:32:04 -07002856 return CallState.ACTIVE;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002857 case Connection.STATE_DIALING:
Santos Cordon72890ce2014-07-21 01:32:04 -07002858 return CallState.DIALING;
Tyler Gunn1e37be52016-07-11 08:54:23 -07002859 case Connection.STATE_PULLING_CALL:
2860 return CallState.PULLING;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002861 case Connection.STATE_DISCONNECTED:
Santos Cordon72890ce2014-07-21 01:32:04 -07002862 return CallState.DISCONNECTED;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002863 case Connection.STATE_HOLDING:
Santos Cordon72890ce2014-07-21 01:32:04 -07002864 return CallState.ON_HOLD;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002865 case Connection.STATE_NEW:
Santos Cordon72890ce2014-07-21 01:32:04 -07002866 return CallState.NEW;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002867 case Connection.STATE_RINGING:
Santos Cordon72890ce2014-07-21 01:32:04 -07002868 return CallState.RINGING;
2869 }
2870 return CallState.DISCONNECTED;
2871 }
Roshan Pius669f90b2015-09-02 16:21:39 -07002872
2873 /**
2874 * Determines if this call is in disconnected state and waiting to be destroyed.
2875 *
2876 * @return {@code true} if this call is disconected.
2877 */
2878 public boolean isDisconnected() {
2879 return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
2880 }
Tyler Gunn7a1f8232015-12-03 19:52:59 -08002881
2882 /**
Brad Ebinger3da57642016-04-04 17:32:19 -07002883 * Determines if this call has just been created and has not been configured properly yet.
2884 *
2885 * @return {@code true} if this call is new.
2886 */
2887 public boolean isNew() {
2888 return getState() == CallState.NEW;
2889 }
2890
2891 /**
Tyler Gunn7a1f8232015-12-03 19:52:59 -08002892 * Sets the call data usage for the call.
2893 *
2894 * @param callDataUsage The new call data usage (in bytes).
2895 */
2896 public void setCallDataUsage(long callDataUsage) {
2897 mCallDataUsage = callDataUsage;
2898 }
2899
2900 /**
2901 * Returns the call data usage for the call.
2902 *
2903 * @return The call data usage (in bytes).
2904 */
2905 public long getCallDataUsage() {
2906 return mCallDataUsage;
2907 }
Tony Mak3ae2dc82016-01-07 11:49:38 +00002908
Hall Liudd68bc32017-01-25 17:14:23 -08002909 public void setRttMode(int mode) {
2910 mRttMode = mode;
2911 // TODO: hook this up to CallAudioManager
2912 }
2913
Hall Liu4b1759d2016-02-02 18:17:40 -08002914 /**
2915 * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent
2916 * has come back to telecom and was processed.
2917 */
2918 public boolean isNewOutgoingCallIntentBroadcastDone() {
2919 return mIsNewOutgoingCallIntentBroadcastDone;
2920 }
2921
2922 public void setNewOutgoingCallIntentBroadcastIsDone() {
2923 mIsNewOutgoingCallIntentBroadcastDone = true;
2924 }
Tyler Gunndb821912016-02-16 14:35:25 -08002925
2926 /**
2927 * Determines if the call has been held by the remote party.
2928 *
2929 * @return {@code true} if the call is remotely held, {@code false} otherwise.
2930 */
2931 public boolean isRemotelyHeld() {
2932 return mIsRemotelyHeld;
2933 }
2934
2935 /**
2936 * Handles Connection events received from a {@link ConnectionService}.
2937 *
2938 * @param event The event.
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002939 * @param extras The extras.
Tyler Gunndb821912016-02-16 14:35:25 -08002940 */
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002941 public void onConnectionEvent(String event, Bundle extras) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002942 Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event);
Tyler Gunndb821912016-02-16 14:35:25 -08002943 if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
2944 mIsRemotelyHeld = true;
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002945 Log.addEvent(this, LogUtils.Events.REMOTELY_HELD);
Tyler Gunndb821912016-02-16 14:35:25 -08002946 // Inform listeners of the fact that a call hold tone was received. This will trigger
2947 // the CallAudioManager to play a tone via the InCallTonePlayer.
2948 for (Listener l : mListeners) {
2949 l.onHoldToneRequested(this);
2950 }
2951 } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
2952 mIsRemotelyHeld = false;
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002953 Log.addEvent(this, LogUtils.Events.REMOTELY_UNHELD);
Tyler Gunndb821912016-02-16 14:35:25 -08002954 for (Listener l : mListeners) {
2955 l.onHoldToneRequested(this);
2956 }
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002957 } else {
2958 for (Listener l : mListeners) {
2959 l.onConnectionEvent(this, event, extras);
2960 }
Tyler Gunndb821912016-02-16 14:35:25 -08002961 }
2962 }
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07002963
Tyler Gunn7c031f22018-01-18 15:00:41 -08002964 /**
2965 * Notifies interested parties that the handover has completed.
2966 * Notifies:
2967 * 1. {@link InCallController} which communicates this to the
2968 * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}.
2969 * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of
2970 * the successful handover.
2971 */
2972 public void onHandoverComplete() {
2973 Log.i(this, "onHandoverComplete; callId=%s", getId());
2974 if (mConnectionService != null) {
2975 mConnectionService.handoverComplete(this);
2976 }
2977 for (Listener l : mListeners) {
2978 l.onHandoverComplete(this);
2979 }
2980 }
2981
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08002982 public void onHandoverFailed(int handoverError) {
Tyler Gunn7c031f22018-01-18 15:00:41 -08002983 Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError);
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08002984 for (Listener l : mListeners) {
2985 l.onHandoverFailed(this, handoverError);
2986 }
2987 }
2988
Tyler Gunn9b618b82016-10-17 15:54:35 -07002989 public void setOriginalConnectionId(String originalConnectionId) {
2990 mOriginalConnectionId = originalConnectionId;
2991 }
2992
2993 /**
2994 * For calls added via a ConnectionManager using the
2995 * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
2996 * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS,
2997 * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
2998 * originally created it.
2999 *
3000 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
3001 * @return The original connection ID.
3002 */
3003 public String getOriginalConnectionId() {
3004 return mOriginalConnectionId;
3005 }
3006
Tyler Gunnb652dad2018-03-27 18:49:49 +00003007 public ConnectionServiceFocusManager getConnectionServiceFocusManager() {
Pengquan Meng4832f202017-12-20 16:13:04 -08003008 return mCallsManager.getConnectionServiceFocusManager();
3009 }
3010
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07003011 /**
3012 * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
3013 * remotely or locally.
3014 *
3015 * @param capabilities The {@link Connection} capabilities for the call.
3016 * @return {@code true} if video is supported, {@code false} otherwise.
3017 */
3018 private boolean doesCallSupportVideo(int capabilities) {
3019 return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 ||
3020 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
3021 }
3022
3023 /**
3024 * Remove any video capabilities set on a {@link Connection} capabilities bitmask.
3025 *
3026 * @param capabilities The capabilities.
3027 * @return The bitmask with video capabilities removed.
3028 */
3029 private int removeVideoCapabilities(int capabilities) {
3030 return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL |
3031 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
3032 }
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003033
3034 /**
3035 * Initiates a handover of this {@link Call} to another {@link PhoneAccount}.
3036 * @param handoverToHandle The {@link PhoneAccountHandle} to handover to.
3037 * @param videoState The video state of the call when handed over.
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07003038 * @param extras Optional extras {@link Bundle} provided by the initiating
3039 * {@link android.telecom.InCallService}.
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003040 */
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07003041 private void requestHandover(PhoneAccountHandle handoverToHandle, int videoState,
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003042 Bundle extras, boolean isLegacy) {
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003043 for (Listener l : mListeners) {
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003044 l.onHandoverRequested(this, handoverToHandle, videoState, extras, isLegacy);
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003045 }
3046 }
Brad Ebingerb079fc52017-07-07 15:59:13 -07003047
3048 /**
3049 * Sets the video history based on the state and state transitions of the call. Always add the
3050 * current video state to the video state history during a call transition except for the
3051 * transitions DIALING->ACTIVE and RINGING->ACTIVE. In these cases, clear the history. If a
3052 * call starts dialing/ringing as a VT call and gets downgraded to audio, we need to record
3053 * the history as an audio call.
3054 */
3055 private void updateVideoHistoryViaState(int oldState, int newState) {
3056 if ((oldState == CallState.DIALING || oldState == CallState.RINGING)
3057 && newState == CallState.ACTIVE) {
3058 mVideoStateHistory = mVideoState;
3059 }
3060
3061 mVideoStateHistory |= mVideoState;
3062 }
3063
Kazuya Ohshiro0063a192017-09-29 14:52:17 +09003064 /**
3065 * Returns whether or not high definition audio was used.
3066 *
3067 * @return true if high definition audio was used during this call.
3068 */
3069 boolean wasHighDefAudio() {
3070 return mWasHighDefAudio;
3071 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08003072}