blob: ae15e0eec3a8f67a657ff1685029817348b185ed [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
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800805 private boolean shouldContinueProcessingAfterDisconnect() {
806 // Stop processing once the call is active.
807 if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
808 return false;
809 }
810
Brad Ebinger3ff5edd2016-07-11 15:09:55 -0700811 // Only Redial a Call in the case of it being an Emergency Call.
812 if(!isEmergencyCall()) {
813 return false;
814 }
815
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800816 // Make sure that there are additional connection services to process.
817 if (mCreateConnectionProcessor == null
818 || !mCreateConnectionProcessor.isProcessingComplete()
819 || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
820 return false;
821 }
822
823 if (mDisconnectCause == null) {
824 return false;
825 }
826
827 // Continue processing if the current attempt failed or timed out.
828 return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
829 mCreateConnectionProcessor.isCallTimedOut();
830 }
831
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800832 /**
Tyler Gunn8452be02015-09-17 09:57:02 -0700833 * Returns the unique ID for this call as it exists in Telecom.
834 * @return The call ID.
835 */
836 public String getId() {
837 return mId;
838 }
839
840 /**
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700841 * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
842 * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
843 * @return The call ID with an appended attempt id.
844 */
845 public String getConnectionId() {
846 if(mCreateConnectionProcessor != null) {
847 mConnectionId = mId + "_" +
848 String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
849 return mConnectionId;
850 } else {
851 return mConnectionId;
852 }
853 }
854
855 /**
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800856 * Sets the call state. Although there exists the notion of appropriate state transitions
857 * (see {@link CallState}), in practice those expectations break down when cellular systems
858 * misbehave and they do this very often. The result is that we do not enforce state transitions
859 * and instead keep the code resilient to unexpected state changes.
860 */
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700861 public void setState(int newState, String tag) {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700862 if (mState != newState) {
863 Log.v(this, "setState %s -> %s", mState, newState);
Sailesh Nepal27679262014-12-02 17:07:00 -0800864
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800865 if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
866 Log.w(this, "continuing processing disconnected call with another service");
Sailesh Nepal27679262014-12-02 17:07:00 -0800867 mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
868 return;
869 }
870
Brad Ebingerb079fc52017-07-07 15:59:13 -0700871 updateVideoHistoryViaState(mState, newState);
872
Sailesh Nepal810735e2014-03-18 18:15:46 -0700873 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700874 maybeLoadCannedSmsResponses();
Santos Cordon672321e2014-08-21 14:38:58 -0700875
Santos Cordon40f9b172014-11-17 10:49:29 -0800876 if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
877 if (mConnectTimeMillis == 0) {
878 // We check to see if mConnectTime is already set to prevent the
879 // call from resetting active time when it goes in and out of
880 // ACTIVE/ON_HOLD
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700881 mConnectTimeMillis = mClockProxy.currentTimeMillis();
882 mConnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
Hall Liu32587202015-11-18 11:10:08 -0800883 mAnalytics.setCallStartTime(mConnectTimeMillis);
Santos Cordon40f9b172014-11-17 10:49:29 -0800884 }
885
886 // We're clearly not disconnected, so reset the disconnected time.
887 mDisconnectTimeMillis = 0;
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700888 mDisconnectElapsedTimeMillis = 0;
Santos Cordon40f9b172014-11-17 10:49:29 -0800889 } else if (mState == CallState.DISCONNECTED) {
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700890 mDisconnectTimeMillis = mClockProxy.currentTimeMillis();
891 mDisconnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
Hall Liu32587202015-11-18 11:10:08 -0800892 mAnalytics.setCallEndTime(mDisconnectTimeMillis);
Santos Cordon5dfd3802014-10-08 20:32:13 -0700893 setLocallyDisconnecting(false);
Santos Cordon672321e2014-08-21 14:38:58 -0700894 fixParentAfterDisconnect();
895 }
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700896
897 // Log the state transition event
898 String event = null;
899 Object data = null;
900 switch (newState) {
901 case CallState.ACTIVE:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700902 event = LogUtils.Events.SET_ACTIVE;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700903 break;
904 case CallState.CONNECTING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700905 event = LogUtils.Events.SET_CONNECTING;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700906 break;
907 case CallState.DIALING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700908 event = LogUtils.Events.SET_DIALING;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700909 break;
Tyler Gunn1e37be52016-07-11 08:54:23 -0700910 case CallState.PULLING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700911 event = LogUtils.Events.SET_PULLING;
Tyler Gunn1e37be52016-07-11 08:54:23 -0700912 break;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700913 case CallState.DISCONNECTED:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700914 event = LogUtils.Events.SET_DISCONNECTED;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700915 data = getDisconnectCause();
916 break;
917 case CallState.DISCONNECTING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700918 event = LogUtils.Events.SET_DISCONNECTING;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700919 break;
920 case CallState.ON_HOLD:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700921 event = LogUtils.Events.SET_HOLD;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700922 break;
923 case CallState.SELECT_PHONE_ACCOUNT:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700924 event = LogUtils.Events.SET_SELECT_PHONE_ACCOUNT;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700925 break;
926 case CallState.RINGING:
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700927 event = LogUtils.Events.SET_RINGING;
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700928 break;
929 }
930 if (event != null) {
931 // The string data should be just the tag.
932 String stringData = tag;
933 if (data != null) {
934 // If data exists, add it to tag. If no tag, just use data.toString().
935 stringData = stringData == null ? data.toString() : stringData + "> " + data;
936 }
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700937 Log.addEvent(this, event, stringData);
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700938 }
Tej Singh95daef72018-02-05 18:34:19 -0800939 int statsdDisconnectCause = (newState == CallState.DISCONNECTED) ?
940 getDisconnectCause().getCode() : DisconnectCause.UNKNOWN;
941 StatsLog.write(StatsLog.CALL_STATE_CHANGED, newState, statsdDisconnectCause,
942 isSelfManaged(), isExternalCall());
Sailesh Nepal810735e2014-03-18 18:15:46 -0700943 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800944 }
945
Andrew Lee5be64bc2014-09-08 18:35:15 -0700946 void setRingbackRequested(boolean ringbackRequested) {
947 mRingbackRequested = ringbackRequested;
Ihab Awadcb387ac2014-05-28 16:49:38 -0700948 for (Listener l : mListeners) {
Andrew Lee5be64bc2014-09-08 18:35:15 -0700949 l.onRingbackRequested(this, mRingbackRequested);
Ihab Awadcb387ac2014-05-28 16:49:38 -0700950 }
951 }
952
Andrew Lee5be64bc2014-09-08 18:35:15 -0700953 boolean isRingbackRequested() {
954 return mRingbackRequested;
Ihab Awadcb387ac2014-05-28 16:49:38 -0700955 }
956
Hall Liuada03012015-10-26 15:44:00 -0700957 @VisibleForTesting
958 public boolean isConference() {
Santos Cordon672321e2014-08-21 14:38:58 -0700959 return mIsConference;
960 }
961
Ihab Awad8de76912015-02-17 12:25:52 -0800962 public Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800963 return mHandle;
964 }
965
Hall Liub8ce19c2015-10-14 15:13:51 -0700966 public String getPostDialDigits() {
967 return mPostDialDigits;
968 }
969
Brad Ebingerbaf52ba2016-03-21 16:38:32 -0700970 public String getViaNumber() {
971 return mViaNumber;
972 }
973
974 public void setViaNumber(String viaNumber) {
975 // If at any point the via number is not empty throughout the call, save that via number.
976 if (!TextUtils.isEmpty(viaNumber)) {
977 mViaNumber = viaNumber;
978 }
979 }
980
Tyler Gunnaee51f22018-03-09 02:33:54 +0000981 public int getHandlePresentation() {
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700982 return mHandlePresentation;
983 }
984
Nancy Chen0d3076c2014-07-30 14:45:44 -0700985
986 void setHandle(Uri handle) {
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700987 setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
Nancy Chen0d3076c2014-07-30 14:45:44 -0700988 }
989
Ihab Awad8de76912015-02-17 12:25:52 -0800990 public void setHandle(Uri handle, int presentation) {
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700991 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700992 mHandlePresentation = presentation;
Santos Cordonecaaeac2014-11-05 20:59:04 -0800993 if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
994 mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
995 mHandle = null;
996 } else {
997 mHandle = handle;
Yorke Lee96e662d2014-11-06 13:02:49 -0800998 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
999 && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
1000 // If the number is actually empty, set it to null, unless this is a
1001 // SCHEME_VOICEMAIL uri which always has an empty number.
Santos Cordonecaaeac2014-11-05 20:59:04 -08001002 mHandle = null;
1003 }
1004 }
1005
Roshan Pius17010e52015-08-13 13:05:35 -07001006 // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
1007 // call, it will remain so for the rest of it's lifetime.
1008 if (!mIsEmergencyCall) {
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07001009 mIsEmergencyCall = mHandle != null &&
1010 mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
1011 mHandle.getSchemeSpecificPart());
Roshan Pius17010e52015-08-13 13:05:35 -07001012 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001013 startCallerInfoLookup();
Sailesh Nepale8ecb982014-07-11 17:19:42 -07001014 for (Listener l : mListeners) {
1015 l.onHandleChanged(this);
1016 }
1017 }
1018 }
1019
Tyler Gunn2b17f232017-03-08 08:51:00 -08001020 public String getCallerDisplayName() {
Sailesh Nepale8ecb982014-07-11 17:19:42 -07001021 return mCallerDisplayName;
1022 }
1023
Tyler Gunn2b17f232017-03-08 08:51:00 -08001024 public int getCallerDisplayNamePresentation() {
Sailesh Nepale8ecb982014-07-11 17:19:42 -07001025 return mCallerDisplayNamePresentation;
1026 }
1027
1028 void setCallerDisplayName(String callerDisplayName, int presentation) {
1029 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
1030 presentation != mCallerDisplayNamePresentation) {
1031 mCallerDisplayName = callerDisplayName;
1032 mCallerDisplayNamePresentation = presentation;
1033 for (Listener l : mListeners) {
1034 l.onCallerDisplayNameChanged(this);
1035 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001036 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001037 }
1038
Ihab Awad8de76912015-02-17 12:25:52 -08001039 public String getName() {
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001040 return mCallerInfo == null ? null : mCallerInfo.name;
1041 }
1042
Bryce Lee41045b42015-08-05 13:10:39 -07001043 public String getPhoneNumber() {
1044 return mCallerInfo == null ? null : mCallerInfo.phoneNumber;
1045 }
1046
Ihab Awad8de76912015-02-17 12:25:52 -08001047 public Bitmap getPhotoIcon() {
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001048 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
1049 }
1050
Ihab Awad8de76912015-02-17 12:25:52 -08001051 public Drawable getPhoto() {
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001052 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
1053 }
1054
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001055 /**
Andrew Lee701dc002014-09-11 21:29:12 -07001056 * @param disconnectCause The reason for the disconnection, represented by
1057 * {@link android.telecom.DisconnectCause}.
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001058 */
Ihab Awad8de76912015-02-17 12:25:52 -08001059 public void setDisconnectCause(DisconnectCause disconnectCause) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001060 // TODO: Consider combining this method with a setDisconnected() method that is totally
1061 // separate from setState.
Hall Liu32587202015-11-18 11:10:08 -08001062 mAnalytics.setCallDisconnectCause(disconnectCause);
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001063 mDisconnectCause = disconnectCause;
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001064 }
1065
Ihab Awad8de76912015-02-17 12:25:52 -08001066 public DisconnectCause getDisconnectCause() {
Santos Cordon79ff2bc2014-03-27 15:31:27 -07001067 return mDisconnectCause;
1068 }
1069
Brad Ebingerf1900072015-11-12 17:25:06 -08001070 @VisibleForTesting
1071 public boolean isEmergencyCall() {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001072 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -08001073 }
1074
Yorke Lee33501632014-03-17 19:24:12 -07001075 /**
1076 * @return The original handle this call is associated with. In-call services should use this
1077 * handle when indicating in their UI the handle that is being called.
1078 */
1079 public Uri getOriginalHandle() {
1080 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
Nancy Chen201b4372014-09-08 14:18:24 -07001081 return mGatewayInfo.getOriginalAddress();
Yorke Lee33501632014-03-17 19:24:12 -07001082 }
1083 return getHandle();
1084 }
1085
Brad Ebinger53855132015-10-30 10:58:19 -07001086 @VisibleForTesting
1087 public GatewayInfo getGatewayInfo() {
Yorke Lee33501632014-03-17 19:24:12 -07001088 return mGatewayInfo;
1089 }
1090
Nancy Chen0d3076c2014-07-30 14:45:44 -07001091 void setGatewayInfo(GatewayInfo gatewayInfo) {
1092 mGatewayInfo = gatewayInfo;
1093 }
1094
Brad Ebingerf1900072015-11-12 17:25:06 -08001095 @VisibleForTesting
1096 public PhoneAccountHandle getConnectionManagerPhoneAccount() {
Ihab Awadb78b2762014-07-25 15:16:23 -07001097 return mConnectionManagerPhoneAccountHandle;
Nancy Chen77d2d0e2014-06-24 12:06:03 -07001098 }
1099
Brad Ebingerf1900072015-11-12 17:25:06 -08001100 @VisibleForTesting
1101 public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001102 if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
1103 mConnectionManagerPhoneAccountHandle = accountHandle;
Ihab Awad69eb0f52014-07-18 11:20:37 -07001104 for (Listener l : mListeners) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001105 l.onConnectionManagerPhoneAccountChanged(this);
1106 }
1107 }
Hall Liu825f6ad2018-01-29 17:34:54 -08001108 checkIfRttCapable();
Ihab Awadb78b2762014-07-25 15:16:23 -07001109 }
1110
Hall Liuada03012015-10-26 15:44:00 -07001111 @VisibleForTesting
1112 public PhoneAccountHandle getTargetPhoneAccount() {
Ihab Awadb78b2762014-07-25 15:16:23 -07001113 return mTargetPhoneAccountHandle;
1114 }
1115
Brad Ebingerf1900072015-11-12 17:25:06 -08001116 @VisibleForTesting
1117 public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001118 if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
1119 mTargetPhoneAccountHandle = accountHandle;
1120 for (Listener l : mListeners) {
1121 l.onTargetPhoneAccountChanged(this);
Ihab Awad69eb0f52014-07-18 11:20:37 -07001122 }
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001123 configureCallAttributes();
Ihab Awad69eb0f52014-07-18 11:20:37 -07001124 }
Tyler Gunnddec5892016-09-28 10:26:13 -07001125 checkIfVideoCapable();
Hall Liu825f6ad2018-01-29 17:34:54 -08001126 checkIfRttCapable();
Nancy Chen53ceedc2014-07-08 18:56:51 -07001127 }
1128
Tyler Gunn2b17f232017-03-08 08:51:00 -08001129 public CharSequence getTargetPhoneAccountLabel() {
1130 if (getTargetPhoneAccount() == null) {
1131 return null;
1132 }
1133 PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1134 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1135
1136 if (phoneAccount == null) {
1137 return null;
1138 }
1139
1140 return phoneAccount.getLabel();
1141 }
1142
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001143 /**
1144 * Determines if this Call should be written to the call log.
1145 * @return {@code true} for managed calls or for self-managed calls which have the
1146 * {@link PhoneAccount#EXTRA_LOG_SELF_MANAGED_CALLS} extra set.
1147 */
1148 public boolean isLoggedSelfManaged() {
1149 if (!isSelfManaged()) {
1150 // Managed calls are always logged.
1151 return true;
1152 }
1153 if (getTargetPhoneAccount() == null) {
1154 return false;
1155 }
1156 PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1157 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1158
1159 if (phoneAccount == null) {
1160 return false;
1161 }
1162
1163 return phoneAccount.getExtras() != null && phoneAccount.getExtras().getBoolean(
1164 PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, false);
1165 }
1166
Brad Ebingerd931a012015-10-21 12:54:08 -07001167 @VisibleForTesting
1168 public boolean isIncoming() {
Hall Liu32587202015-11-18 11:10:08 -08001169 return mCallDirection == CALL_DIRECTION_INCOMING;
1170 }
1171
Tyler Gunn4fe861d2016-03-29 19:33:56 -07001172 public boolean isExternalCall() {
1173 return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
1174 Connection.PROPERTY_IS_EXTERNAL_CALL;
1175 }
1176
Tony Makd29d9372016-02-08 18:23:41 +00001177 public boolean isWorkCall() {
1178 return mIsWorkCall;
1179 }
1180
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001181 public boolean isUsingCallRecordingTone() {
1182 return mUseCallRecordingTone;
1183 }
1184
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001185 public boolean isVideoCallingSupported() {
1186 return mIsVideoCallingSupported;
1187 }
1188
Tyler Gunnacb3bc82017-01-09 09:43:56 -08001189 public boolean isSelfManaged() {
1190 return mIsSelfManaged;
1191 }
1192
1193 public void setIsSelfManaged(boolean isSelfManaged) {
1194 mIsSelfManaged = isSelfManaged;
1195
1196 // Connection properties will add/remove the PROPERTY_SELF_MANAGED.
1197 setConnectionProperties(getConnectionProperties());
1198 }
1199
Tyler Gunn141ef582017-05-26 13:38:13 -07001200 public void markFinishedHandoverStateAndCleanup(int handoverState) {
1201 if (mHandoverSourceCall != null) {
1202 mHandoverSourceCall.setHandoverState(handoverState);
1203 } else if (mHandoverDestinationCall != null) {
1204 mHandoverDestinationCall.setHandoverState(handoverState);
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001205 }
Tyler Gunn141ef582017-05-26 13:38:13 -07001206 setHandoverState(handoverState);
1207 maybeCleanupHandover();
1208 }
1209
1210 public void maybeCleanupHandover() {
1211 if (mHandoverSourceCall != null) {
1212 mHandoverSourceCall.setHandoverSourceCall(null);
1213 mHandoverSourceCall.setHandoverDestinationCall(null);
1214 mHandoverSourceCall = null;
1215 } else if (mHandoverDestinationCall != null) {
1216 mHandoverDestinationCall.setHandoverSourceCall(null);
1217 mHandoverDestinationCall.setHandoverDestinationCall(null);
1218 mHandoverDestinationCall = null;
1219 }
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001220 }
1221
1222 public boolean isHandoverInProgress() {
Tyler Gunn141ef582017-05-26 13:38:13 -07001223 return mHandoverSourceCall != null || mHandoverDestinationCall != null;
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001224 }
1225
Tyler Gunn141ef582017-05-26 13:38:13 -07001226 public Call getHandoverDestinationCall() {
1227 return mHandoverDestinationCall;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001228 }
1229
Tyler Gunn141ef582017-05-26 13:38:13 -07001230 public void setHandoverDestinationCall(Call call) {
1231 mHandoverDestinationCall = call;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001232 }
1233
Tyler Gunn141ef582017-05-26 13:38:13 -07001234 public Call getHandoverSourceCall() {
1235 return mHandoverSourceCall;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001236 }
1237
Tyler Gunn141ef582017-05-26 13:38:13 -07001238 public void setHandoverSourceCall(Call call) {
1239 mHandoverSourceCall = call;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001240 }
1241
Tyler Gunn141ef582017-05-26 13:38:13 -07001242 public void setHandoverState(int handoverState) {
1243 Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(),
1244 HandoverState.stateToString(handoverState));
1245 mHandoverState = handoverState;
1246 }
1247
1248 public int getHandoverState() {
1249 return mHandoverState;
Tyler Gunn15d096b2017-05-03 15:02:25 -07001250 }
1251
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001252 private void configureCallAttributes() {
Tony Makd29d9372016-02-08 18:23:41 +00001253 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1254 boolean isWorkCall = false;
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001255 boolean isCallRecordingToneSupported = false;
Tony Makd29d9372016-02-08 18:23:41 +00001256 PhoneAccount phoneAccount =
1257 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1258 if (phoneAccount != null) {
Brad Ebingerc1133602016-01-11 18:03:36 -08001259 final UserHandle userHandle;
1260 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
1261 userHandle = mInitiatingUser;
1262 } else {
Tony Mak3ae2dc82016-01-07 11:49:38 +00001263 userHandle = mTargetPhoneAccountHandle.getUserHandle();
1264 }
Tony Makd29d9372016-02-08 18:23:41 +00001265 if (userHandle != null) {
1266 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle);
Tony Mak3ae2dc82016-01-07 11:49:38 +00001267 }
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001268
1269 isCallRecordingToneSupported = (phoneAccount.hasCapabilities(
1270 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) && phoneAccount.getExtras() != null
1271 && phoneAccount.getExtras().getBoolean(
1272 PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false));
Tony Mak3ae2dc82016-01-07 11:49:38 +00001273 }
Tony Makd29d9372016-02-08 18:23:41 +00001274 mIsWorkCall = isWorkCall;
Tyler Gunn5d66e1d2018-01-25 20:22:49 -08001275 mUseCallRecordingTone = isCallRecordingToneSupported;
Tony Mak3ae2dc82016-01-07 11:49:38 +00001276 }
1277
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001278 /**
1279 * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount}
Tyler Gunnddec5892016-09-28 10:26:13 -07001280 * capability and ensures that the video state is updated if the phone account does not support
1281 * video calling.
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001282 */
1283 private void checkIfVideoCapable() {
1284 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
Tyler Gunnddec5892016-09-28 10:26:13 -07001285 if (mTargetPhoneAccountHandle == null) {
1286 // If no target phone account handle is specified, assume we can potentially perform a
1287 // video call; once the phone account is set, we can confirm that it is video capable.
1288 mIsVideoCallingSupported = true;
1289 Log.d(this, "checkIfVideoCapable: no phone account selected; assume video capable.");
1290 return;
1291 }
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001292 PhoneAccount phoneAccount =
1293 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1294 mIsVideoCallingSupported = phoneAccount != null && phoneAccount.hasCapabilities(
1295 PhoneAccount.CAPABILITY_VIDEO_CALLING);
Tyler Gunnddec5892016-09-28 10:26:13 -07001296
1297 if (!mIsVideoCallingSupported && VideoProfile.isVideo(getVideoState())) {
1298 // The PhoneAccount for the Call was set to one which does not support video calling,
1299 // and the current call is configured to be a video call; downgrade to audio-only.
1300 setVideoState(VideoProfile.STATE_AUDIO_ONLY);
1301 Log.d(this, "checkIfVideoCapable: selected phone account doesn't support video.");
1302 }
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001303 }
1304
Hall Liu825f6ad2018-01-29 17:34:54 -08001305 private void checkIfRttCapable() {
1306 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1307 if (mTargetPhoneAccountHandle == null) {
1308 return;
1309 }
1310
1311 // Check both the target phone account and the connection manager phone account -- if
1312 // either support RTT, just set the streams and have them set/unset the RTT property as
1313 // needed.
1314 PhoneAccount phoneAccount =
1315 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1316 PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked(
1317 mConnectionManagerPhoneAccountHandle);
1318 boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities(
1319 PhoneAccount.CAPABILITY_RTT);
1320 boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null
1321 && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT);
1322
1323 if ((isConnectionManagerRttSupported || isRttSupported)
1324 && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) {
1325 // If the phone account got set to an RTT capable one and we haven't set the streams
1326 // yet, do so now.
Hall Liuea67a762018-02-09 16:48:57 -08001327 createRttStreams();
Hall Liu825f6ad2018-01-29 17:34:54 -08001328 Log.i(this, "Setting RTT streams after target phone account selected");
Hall Liu825f6ad2018-01-29 17:34:54 -08001329 }
1330 }
1331
Hall Liu32587202015-11-18 11:10:08 -08001332 boolean shouldAttachToExistingConnection() {
1333 return mShouldAttachToExistingConnection;
Sailesh Nepal810735e2014-03-18 18:15:46 -07001334 }
1335
Ben Gilad0407fb22014-01-09 16:18:41 -08001336 /**
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001337 * Note: This method relies on {@link #mConnectElapsedTimeMillis} and
1338 * {@link #mDisconnectElapsedTimeMillis} which are independent of the wall clock (which could
1339 * change due to clock changes).
Ben Gilad0407fb22014-01-09 16:18:41 -08001340 * @return The "age" of this call object in milliseconds, which typically also represents the
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001341 * period since this call was added to the set pending outgoing calls.
Ben Gilad0407fb22014-01-09 16:18:41 -08001342 */
Hall Liuada03012015-10-26 15:44:00 -07001343 @VisibleForTesting
1344 public long getAgeMillis() {
Santos Cordon40f9b172014-11-17 10:49:29 -08001345 if (mState == CallState.DISCONNECTED &&
1346 (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
1347 mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
1348 // Rejected and missed calls have no age. They're immortal!!
1349 return 0;
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001350 } else if (mConnectElapsedTimeMillis == 0) {
Santos Cordon40f9b172014-11-17 10:49:29 -08001351 // Age is measured in the amount of time the call was active. A zero connect time
1352 // indicates that we never went active, so return 0 for the age.
1353 return 0;
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001354 } else if (mDisconnectElapsedTimeMillis == 0) {
Santos Cordon40f9b172014-11-17 10:49:29 -08001355 // We connected, but have not yet disconnected
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001356 return mClockProxy.elapsedRealtime() - mConnectElapsedTimeMillis;
Santos Cordon40f9b172014-11-17 10:49:29 -08001357 }
1358
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001359 return mDisconnectElapsedTimeMillis - mConnectElapsedTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -08001360 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -08001361
Yorke Leef98fb572014-03-05 10:56:55 -08001362 /**
1363 * @return The time when this call object was created and added to the set of pending outgoing
1364 * calls.
1365 */
Ihab Awad8de76912015-02-17 12:25:52 -08001366 public long getCreationTimeMillis() {
Sailesh Nepal8c85dee2014-04-07 22:21:40 -07001367 return mCreationTimeMillis;
1368 }
1369
Ihab Awad8de76912015-02-17 12:25:52 -08001370 public void setCreationTimeMillis(long time) {
Yorke Lee0c1e4f62014-10-14 16:56:45 -07001371 mCreationTimeMillis = time;
1372 }
1373
Sailesh Nepal8c85dee2014-04-07 22:21:40 -07001374 long getConnectTimeMillis() {
1375 return mConnectTimeMillis;
1376 }
1377
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001378 int getConnectionCapabilities() {
1379 return mConnectionCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -07001380 }
1381
Tyler Gunn571d5e62016-03-15 15:55:18 -07001382 int getConnectionProperties() {
1383 return mConnectionProperties;
1384 }
1385
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001386 void setConnectionCapabilities(int connectionCapabilities) {
1387 setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
Yorke Lee604a90f2014-10-20 12:13:32 -07001388 }
1389
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001390 void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
1391 Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
1392 connectionCapabilities));
1393 if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001394 // If the phone account does not support video calling, and the connection capabilities
1395 // passed in indicate that the call supports video, remove those video capabilities.
1396 if (!isVideoCallingSupported() && doesCallSupportVideo(connectionCapabilities)) {
1397 Log.w(this, "setConnectionCapabilities: attempt to set connection as video " +
1398 "capable when not supported by the phone account.");
1399 connectionCapabilities = removeVideoCapabilities(connectionCapabilities);
1400 }
1401
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001402 int previousCapabilities = mConnectionCapabilities;
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001403 mConnectionCapabilities = connectionCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -07001404 for (Listener l : mListeners) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001405 l.onConnectionCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -07001406 }
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001407
1408 int xorCaps = previousCapabilities ^ mConnectionCapabilities;
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001409 Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE,
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001410 "Current: [%s], Removed [%s], Added [%s]",
1411 Connection.capabilitiesToStringShort(mConnectionCapabilities),
1412 Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
1413 Connection.capabilitiesToStringShort(mConnectionCapabilities & xorCaps));
Santos Cordona1610702014-06-04 20:22:56 -07001414 }
1415 }
1416
Tyler Gunn571d5e62016-03-15 15:55:18 -07001417 void setConnectionProperties(int connectionProperties) {
1418 Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
1419 connectionProperties));
Tyler Gunnacb3bc82017-01-09 09:43:56 -08001420
1421 // Ensure the ConnectionService can't change the state of the self-managed property.
1422 if (isSelfManaged()) {
1423 connectionProperties |= Connection.PROPERTY_SELF_MANAGED;
1424 } else {
1425 connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED;
1426 }
1427
Hall Liudd68bc32017-01-25 17:14:23 -08001428 int changedProperties = mConnectionProperties ^ connectionProperties;
1429
1430 if (changedProperties != 0) {
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001431 int previousProperties = mConnectionProperties;
Tyler Gunn571d5e62016-03-15 15:55:18 -07001432 mConnectionProperties = connectionProperties;
Hall Liuea67a762018-02-09 16:48:57 -08001433 if ((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
1434 Connection.PROPERTY_IS_RTT) {
1435 createRttStreams();
1436 }
Kazuya Ohshiro0063a192017-09-29 14:52:17 +09001437 mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) ==
1438 Connection.PROPERTY_HIGH_DEF_AUDIO;
Hall Liudd68bc32017-01-25 17:14:23 -08001439 boolean didRttChange =
1440 (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
Tyler Gunn571d5e62016-03-15 15:55:18 -07001441 for (Listener l : mListeners) {
Hall Liudd68bc32017-01-25 17:14:23 -08001442 l.onConnectionPropertiesChanged(this, didRttChange);
Tyler Gunn571d5e62016-03-15 15:55:18 -07001443 }
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001444
1445 boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1446 == Connection.PROPERTY_IS_EXTERNAL_CALL;
1447 boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1448 == Connection.PROPERTY_IS_EXTERNAL_CALL;
1449 if (wasExternal != isExternal) {
1450 Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
1451 isExternal);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001452 Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal);
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001453 for (Listener l : mListeners) {
1454 l.onExternalCallChanged(this, isExternal);
1455 }
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001456 }
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001457
Hall Liud7fe6862016-09-09 16:36:14 -07001458 mAnalytics.addCallProperties(mConnectionProperties);
1459
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001460 int xorProps = previousProperties ^ mConnectionProperties;
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001461 Log.addEvent(this, LogUtils.Events.PROPERTY_CHANGE,
Santos Cordon0d7a00a2016-07-26 15:33:00 -07001462 "Current: [%s], Removed [%s], Added [%s]",
1463 Connection.propertiesToStringShort(mConnectionProperties),
1464 Connection.propertiesToStringShort(previousProperties & xorProps),
1465 Connection.propertiesToStringShort(mConnectionProperties & xorProps));
Tyler Gunn571d5e62016-03-15 15:55:18 -07001466 }
1467 }
1468
Hall Liucff959e2016-12-07 14:04:57 -08001469 public int getSupportedAudioRoutes() {
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001470 return mSupportedAudioRoutes;
1471 }
1472
1473 void setSupportedAudioRoutes(int audioRoutes) {
1474 if (mSupportedAudioRoutes != audioRoutes) {
1475 mSupportedAudioRoutes = audioRoutes;
1476 }
1477 }
1478
Brad Ebinger53855132015-10-30 10:58:19 -07001479 @VisibleForTesting
1480 public Call getParentCall() {
Santos Cordona1610702014-06-04 20:22:56 -07001481 return mParentCall;
1482 }
1483
Brad Ebinger53855132015-10-30 10:58:19 -07001484 @VisibleForTesting
1485 public List<Call> getChildCalls() {
Santos Cordona1610702014-06-04 20:22:56 -07001486 return mChildCalls;
1487 }
1488
Brad Ebinger53855132015-10-30 10:58:19 -07001489 @VisibleForTesting
1490 public boolean wasConferencePreviouslyMerged() {
Santos Cordon88a4a602014-09-29 19:32:21 -07001491 return mWasConferencePreviouslyMerged;
1492 }
1493
Srikanth Chintala5bf75022017-02-03 10:10:35 +05301494 public boolean isDisconnectingChildCall() {
1495 return mIsDisconnectingChildCall;
1496 }
1497
1498 /**
1499 * Sets whether this call is a child call.
1500 */
1501 private void maybeSetCallAsDisconnectingChild() {
1502 if (mParentCall != null) {
1503 mIsDisconnectingChildCall = true;
1504 }
1505 }
1506
Brad Ebinger53855132015-10-30 10:58:19 -07001507 @VisibleForTesting
1508 public Call getConferenceLevelActiveCall() {
Santos Cordon88a4a602014-09-29 19:32:21 -07001509 return mConferenceLevelActiveCall;
1510 }
1511
Hall Liuf62630a2015-10-27 14:53:39 -07001512 @VisibleForTesting
1513 public ConnectionServiceWrapper getConnectionService() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001514 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -08001515 }
1516
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001517 /**
1518 * Retrieves the {@link Context} for the call.
1519 *
1520 * @return The {@link Context}.
1521 */
Hall Liu55076712017-02-28 19:59:30 -08001522 public Context getContext() {
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001523 return mContext;
1524 }
1525
Brad Ebingerf1900072015-11-12 17:25:06 -08001526 @VisibleForTesting
1527 public void setConnectionService(ConnectionServiceWrapper service) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001528 Preconditions.checkNotNull(service);
1529
1530 clearConnectionService();
1531
1532 service.incrementAssociatedCallCount();
1533 mConnectionService = service;
Hall Liu32587202015-11-18 11:10:08 -08001534 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001535 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -07001536 }
1537
1538 /**
Tyler Gunn9b618b82016-10-17 15:54:35 -07001539 * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call.
1540 * Removes the call from its former {@link ConnectionServiceWrapper}, ensuring that the
1541 * ConnectionService is NOT unbound if the call count hits zero.
1542 * This is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} and
1543 * {@link Conference} additions via a ConnectionManager.
1544 * The original {@link android.telecom.ConnectionService} will directly add external calls and
1545 * conferences to Telecom as well as the ConnectionManager, which will add to Telecom. In these
1546 * cases since its first added to via the original CS, we want to change the CS responsible for
1547 * the call to the ConnectionManager rather than adding it again as another call/conference.
1548 *
1549 * @param service The new {@link ConnectionServiceWrapper}.
1550 */
1551 public void replaceConnectionService(ConnectionServiceWrapper service) {
1552 Preconditions.checkNotNull(service);
1553
1554 if (mConnectionService != null) {
1555 ConnectionServiceWrapper serviceTemp = mConnectionService;
1556 mConnectionService = null;
1557 serviceTemp.removeCall(this);
1558 serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/);
1559 }
1560
1561 service.incrementAssociatedCallCount();
1562 mConnectionService = service;
1563 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
1564 }
1565
1566 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001567 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -07001568 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001569 void clearConnectionService() {
1570 if (mConnectionService != null) {
1571 ConnectionServiceWrapper serviceTemp = mConnectionService;
1572 mConnectionService = null;
1573 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -07001574
1575 // Decrementing the count can cause the service to unbind, which itself can trigger the
1576 // service-death code. Since the service death code tries to clean up any associated
1577 // calls, we need to make sure to remove that information (e.g., removeCall()) before
1578 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001579 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
1580 // to do.
1581 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -07001582 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -08001583 }
1584
Santos Cordon766d04f2014-05-06 10:28:25 -07001585 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -07001586 * Starts the create connection sequence. Upon completion, there should exist an active
1587 * connection through a connection service (or the call will have failed).
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001588 *
1589 * @param phoneAccountRegistrar The phone account registrar.
Santos Cordon766d04f2014-05-06 10:28:25 -07001590 */
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001591 void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
Hall Liu4b1759d2016-02-02 18:17:40 -08001592 if (mCreateConnectionProcessor != null) {
1593 Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
1594 " due to a race between NewOutgoingCallIntentBroadcaster and " +
1595 "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
1596 "invocation.");
1597 return;
1598 }
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001599 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
1600 phoneAccountRegistrar, mContext);
Sailesh Nepal664837f2014-07-14 16:31:51 -07001601 mCreateConnectionProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -07001602 }
1603
Sailesh Nepal5a73b032014-06-25 15:53:21 -07001604 @Override
Ihab Awad80008452014-08-23 20:35:44 -07001605 public void handleCreateConnectionSuccess(
1606 CallIdMapper idMapper,
1607 ParcelableConnection connection) {
Santos Cordon12d61822014-07-29 16:02:20 -07001608 Log.v(this, "handleCreateConnectionSuccessful %s", connection);
Ihab Awadb78b2762014-07-25 15:16:23 -07001609 setTargetPhoneAccount(connection.getPhoneAccount());
Santos Cordon72890ce2014-07-21 01:32:04 -07001610 setHandle(connection.getHandle(), connection.getHandlePresentation());
1611 setCallerDisplayName(
1612 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07001613
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001614 setConnectionCapabilities(connection.getConnectionCapabilities());
Tyler Gunn571d5e62016-03-15 15:55:18 -07001615 setConnectionProperties(connection.getConnectionProperties());
Hall Liue67e3182018-03-13 15:09:38 -07001616 setIsVoipAudioMode(connection.getIsVoipAudioMode());
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001617 setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
Ihab Awad6fb37c82014-08-07 19:48:57 -07001618 setVideoProvider(connection.getVideoProvider());
Tyler Gunn13e8a692016-08-03 18:38:00 -07001619 setVideoState(connection.getVideoState());
Andrew Lee5be64bc2014-09-08 18:35:15 -07001620 setRingbackRequested(connection.isRingbackRequested());
Sailesh Nepal604a4fe2014-08-14 18:03:43 -07001621 setStatusHints(connection.getStatusHints());
Tyler Gunn961694a2016-03-21 16:01:40 -07001622 putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras());
Sailesh Nepal664837f2014-07-14 16:31:51 -07001623
Ihab Awad80008452014-08-23 20:35:44 -07001624 mConferenceableCalls.clear();
1625 for (String id : connection.getConferenceableConnectionIds()) {
1626 mConferenceableCalls.add(idMapper.getCall(id));
1627 }
1628
Hall Liu32587202015-11-18 11:10:08 -08001629 switch (mCallDirection) {
1630 case CALL_DIRECTION_INCOMING:
Hall Liu6d4b66d2016-04-01 16:31:13 -07001631 // Listeners (just CallsManager for now) will be responsible for checking whether
1632 // the call should be blocked.
1633 for (Listener l : mListeners) {
1634 l.onSuccessfulIncomingCall(this);
1635 }
Hall Liu32587202015-11-18 11:10:08 -08001636 break;
1637 case CALL_DIRECTION_OUTGOING:
1638 for (Listener l : mListeners) {
1639 l.onSuccessfulOutgoingCall(this,
1640 getStateFromConnectionState(connection.getState()));
1641 }
1642 break;
1643 case CALL_DIRECTION_UNKNOWN:
1644 for (Listener l : mListeners) {
1645 l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
1646 .getState()));
1647 }
1648 break;
Santos Cordon766d04f2014-05-06 10:28:25 -07001649 }
1650 }
1651
Sailesh Nepal5a73b032014-06-25 15:53:21 -07001652 @Override
Andrew Lee701dc002014-09-11 21:29:12 -07001653 public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
Ihab Awadfb5560d2014-08-18 09:32:51 -07001654 clearConnectionService();
Andrew Lee701dc002014-09-11 21:29:12 -07001655 setDisconnectCause(disconnectCause);
Ihab Awad78a5e6b2015-02-06 10:13:05 -08001656 mCallsManager.markCallAsDisconnected(this, disconnectCause);
Santos Cordon682fe6b2014-05-20 08:56:39 -07001657
Hall Liu32587202015-11-18 11:10:08 -08001658 switch (mCallDirection) {
1659 case CALL_DIRECTION_INCOMING:
1660 for (Listener listener : mListeners) {
1661 listener.onFailedIncomingCall(this);
1662 }
1663 break;
1664 case CALL_DIRECTION_OUTGOING:
1665 for (Listener listener : mListeners) {
1666 listener.onFailedOutgoingCall(this, disconnectCause);
1667 }
1668 break;
1669 case CALL_DIRECTION_UNKNOWN:
1670 for (Listener listener : mListeners) {
1671 listener.onFailedUnknownCall(this);
1672 }
1673 break;
Sailesh Nepal664837f2014-07-14 16:31:51 -07001674 }
Santos Cordon766d04f2014-05-06 10:28:25 -07001675 }
1676
1677 /**
Ihab Awad74549ec2014-03-10 15:33:25 -07001678 * Plays the specified DTMF tone.
1679 */
Tyler Gunnad972422017-11-13 13:36:35 -08001680 @VisibleForTesting
1681 public void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001682 if (mConnectionService == null) {
1683 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -07001684 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001685 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
1686 mConnectionService.playDtmfTone(this, digit);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001687 Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit));
Ihab Awad74549ec2014-03-10 15:33:25 -07001688 }
Tyler Gunnad972422017-11-13 13:36:35 -08001689 mPlayingDtmfTone = digit;
Ihab Awad74549ec2014-03-10 15:33:25 -07001690 }
1691
1692 /**
1693 * Stops playing any currently playing DTMF tone.
1694 */
Tyler Gunnad972422017-11-13 13:36:35 -08001695 @VisibleForTesting
1696 public void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001697 if (mConnectionService == null) {
Jay Shraunerfd4d7b52014-08-14 14:14:43 -07001698 Log.w(this, "stopDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -07001699 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001700 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001701 Log.addEvent(this, LogUtils.Events.STOP_DTMF);
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001702 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -07001703 }
Tyler Gunnad972422017-11-13 13:36:35 -08001704 mPlayingDtmfTone = NO_DTMF_TONE;
1705 }
1706
1707 /**
1708 * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has
1709 * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise.
1710 */
1711 boolean isDtmfTonePlaying() {
1712 return mPlayingDtmfTone != NO_DTMF_TONE;
Ihab Awad74549ec2014-03-10 15:33:25 -07001713 }
1714
Bryce Lee6d962522015-11-30 10:28:37 -08001715 /**
1716 * Silences the ringer.
1717 */
1718 void silence() {
1719 if (mConnectionService == null) {
1720 Log.w(this, "silence() request on a call without a connection service.");
1721 } else {
1722 Log.i(this, "Send silence to connection service for call %s", this);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001723 Log.addEvent(this, LogUtils.Events.SILENCE);
Bryce Lee6d962522015-11-30 10:28:37 -08001724 mConnectionService.silence(this);
1725 }
1726 }
1727
Hall Liu0a6dd302015-12-16 15:06:49 -08001728 @VisibleForTesting
1729 public void disconnect() {
Hall Liu63e690c2017-02-14 18:05:03 -08001730 disconnect(0);
Santos Cordoncf5b2912014-11-05 21:57:54 -08001731 }
1732
Hall Liu220b4192015-12-11 11:33:08 -08001733 @VisibleForTesting
Tyler Gunnf4f05392018-03-26 18:57:59 +00001734 public void disconnect(String reason) {
1735 disconnect(0, reason);
Tyler Gunn587fc272018-02-07 16:07:29 -08001736 }
1737
1738 /**
1739 * Attempts to disconnect the call through the connection service.
1740 */
1741 @VisibleForTesting
Tyler Gunnf4f05392018-03-26 18:57:59 +00001742 public void disconnect(long disconnectionTimeout) {
1743 disconnect(disconnectionTimeout, "internal" /** reason */);
1744 }
1745
1746 /**
1747 * Attempts to disconnect the call through the connection service.
1748 * @param reason the reason for the disconnect; used for logging purposes only. In some cases
1749 * this can be a package name if the disconnect was initiated through an API such
1750 * as TelecomManager.
1751 */
1752 @VisibleForTesting
1753 public void disconnect(long disconnectionTimeout, String reason) {
1754 Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT, reason);
Santos Cordon2685dab2015-04-17 17:12:09 -07001755
Tyler Gunnaa479372014-10-07 10:18:42 -07001756 // Track that the call is now locally disconnecting.
1757 setLocallyDisconnecting(true);
Srikanth Chintala5bf75022017-02-03 10:10:35 +05301758 maybeSetCallAsDisconnectingChild();
Tyler Gunnaa479372014-10-07 10:18:42 -07001759
Santos Cordon92694512015-04-23 14:47:28 -07001760 if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
Jay Shraunerfd4d7b52014-08-14 14:14:43 -07001761 mState == CallState.CONNECTING) {
Santos Cordon682fe6b2014-05-20 08:56:39 -07001762 Log.v(this, "Aborting call %s", this);
Hall Liu63e690c2017-02-14 18:05:03 -08001763 abort(disconnectionTimeout);
Santos Cordon74d420b2014-05-07 14:38:47 -07001764 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Jay Shraunerfd4d7b52014-08-14 14:14:43 -07001765 if (mConnectionService == null) {
1766 Log.e(this, new Exception(), "disconnect() request on a call without a"
1767 + " connection service.");
1768 } else {
1769 Log.i(this, "Send disconnect to connection service for call: %s", this);
1770 // The call isn't officially disconnected until the connection service
1771 // confirms that the call was actually disconnected. Only then is the
1772 // association between call and connection service severed, see
1773 // {@link CallsManager#markCallAsDisconnected}.
1774 mConnectionService.disconnect(this);
1775 }
Santos Cordon049b7b62014-01-30 05:34:26 -08001776 }
1777 }
1778
Hall Liu63e690c2017-02-14 18:05:03 -08001779 void abort(long disconnectionTimeout) {
Sailesh Nepal752cacb2014-11-06 12:35:26 -08001780 if (mCreateConnectionProcessor != null &&
1781 !mCreateConnectionProcessor.isProcessingComplete()) {
Sailesh Nepal664837f2014-07-14 16:31:51 -07001782 mCreateConnectionProcessor.abort();
Santos Cordon92694512015-04-23 14:47:28 -07001783 } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT
Yorke Lee43df90f2014-08-20 10:56:09 -07001784 || mState == CallState.CONNECTING) {
Hall Liu63e690c2017-02-14 18:05:03 -08001785 if (disconnectionTimeout > 0) {
1786 // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0
1787 // milliseconds, do not destroy the call.
1788 // Instead, we announce the cancellation and CallsManager handles
Santos Cordoncf5b2912014-11-05 21:57:54 -08001789 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
1790 // then re-dial them quickly using a gateway, allowing the first call to end
1791 // causes jank. This timeout allows CallsManager to transition the first call into
1792 // the second call so that in-call only ever sees a single call...eliminating the
Hall Liu63e690c2017-02-14 18:05:03 -08001793 // jank altogether. The app will also be able to set the timeout via an extra on
1794 // the ordered broadcast.
Santos Cordoncf5b2912014-11-05 21:57:54 -08001795 for (Listener listener : mListeners) {
Hall Liu63e690c2017-02-14 18:05:03 -08001796 if (listener.onCanceledViaNewOutgoingCallBroadcast(
1797 this, disconnectionTimeout)) {
Santos Cordoncf5b2912014-11-05 21:57:54 -08001798 // The first listener to handle this wins. A return value of true means that
1799 // the listener will handle the disconnection process later and so we
1800 // should not continue it here.
1801 setLocallyDisconnecting(false);
1802 return;
1803 }
1804 }
1805 }
1806
Tyler Gunn504eb832014-10-13 14:15:27 -07001807 handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
Santos Cordon2d0b3312014-08-15 15:04:17 -07001808 } else {
Santos Cordon92694512015-04-23 14:47:28 -07001809 Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING");
Santos Cordon682fe6b2014-05-20 08:56:39 -07001810 }
1811 }
1812
Santos Cordon0b03b4b2014-01-29 18:01:59 -08001813 /**
Santos Cordon61d0f702014-02-19 02:52:23 -08001814 * Answers the call if it is ringing.
Andrew Lee38931d02014-07-16 10:17:36 -07001815 *
1816 * @param videoState The video state in which to answer the call.
Santos Cordon61d0f702014-02-19 02:52:23 -08001817 */
Hall Liu0a6dd302015-12-16 15:06:49 -08001818 @VisibleForTesting
1819 public void answer(int videoState) {
Santos Cordon61d0f702014-02-19 02:52:23 -08001820 // Check to verify that the call is still in the ringing state. A call can change states
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001821 // between the time the user hits 'answer' and Telecom receives the command.
Santos Cordon61d0f702014-02-19 02:52:23 -08001822 if (isRinging("answer")) {
Tyler Gunn13e8a692016-08-03 18:38:00 -07001823 if (!isVideoCallingSupported() && VideoProfile.isVideo(videoState)) {
1824 // Video calling is not supported, yet the InCallService is attempting to answer as
1825 // video. We will simply answer as audio-only.
1826 videoState = VideoProfile.STATE_AUDIO_ONLY;
1827 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001828 // At this point, we are asking the connection service to answer but we don't assume
1829 // that it will work. Instead, we wait until confirmation from the connectino service
Ihab Awad6fb37c82014-08-07 19:48:57 -07001830 // that the call is in a non-STATE_RINGING state before changing the UI. See
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001831 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
Tyler Gunn165ee752016-09-06 13:40:10 -07001832 if (mConnectionService != null) {
1833 mConnectionService.answer(this, videoState);
1834 } else {
1835 Log.e(this, new NullPointerException(),
1836 "answer call failed due to null CS callId=%s", getId());
1837 }
Brad Ebingera3eccfe2016-10-05 15:45:22 -07001838 Log.addEvent(this, LogUtils.Events.REQUEST_ACCEPT);
Santos Cordon61d0f702014-02-19 02:52:23 -08001839 }
1840 }
1841
1842 /**
Pooja Jainad4ebc02017-12-28 14:23:13 +05301843 * Deflects the call if it is ringing.
1844 *
1845 * @param address address to be deflected to.
1846 */
1847 @VisibleForTesting
1848 public void deflect(Uri address) {
1849 // Check to verify that the call is still in the ringing state. A call can change states
1850 // between the time the user hits 'deflect' and Telecomm receives the command.
1851 if (isRinging("deflect")) {
1852 // At this point, we are asking the connection service to deflect but we don't assume
1853 // that it will work. Instead, we wait until confirmation from the connection service
1854 // that the call is in a non-STATE_RINGING state before changing the UI. See
1855 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
1856 mVideoStateHistory |= mVideoState;
1857 if (mConnectionService != null) {
1858 mConnectionService.deflect(this, address);
1859 } else {
1860 Log.e(this, new NullPointerException(),
1861 "deflect call failed due to null CS callId=%s", getId());
1862 }
1863 Log.addEvent(this, LogUtils.Events.REQUEST_DEFLECT, Log.pii(address));
1864 }
1865 }
1866
1867 /**
Santos Cordon61d0f702014-02-19 02:52:23 -08001868 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -07001869 *
1870 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
1871 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -08001872 */
Hall Liu0a6dd302015-12-16 15:06:49 -08001873 @VisibleForTesting
1874 public void reject(boolean rejectWithMessage, String textMessage) {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001875 reject(rejectWithMessage, textMessage, "internal" /** reason */);
Tyler Gunn587fc272018-02-07 16:07:29 -08001876 }
1877
1878 /**
1879 * Rejects the call if it is ringing.
1880 *
1881 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
1882 * @param textMessage An optional text message to send as part of the rejection.
Tyler Gunnf4f05392018-03-26 18:57:59 +00001883 * @param reason The reason for the reject; used for logging purposes. May be a package name
1884 * if the reject is initiated from an API such as TelecomManager.
Tyler Gunn587fc272018-02-07 16:07:29 -08001885 */
1886 @VisibleForTesting
Tyler Gunnf4f05392018-03-26 18:57:59 +00001887 public void reject(boolean rejectWithMessage, String textMessage, String reason) {
Santos Cordon61d0f702014-02-19 02:52:23 -08001888 // Check to verify that the call is still in the ringing state. A call can change states
1889 // between the time the user hits 'reject' and Telecomm receives the command.
1890 if (isRinging("reject")) {
Tyler Gunn5fb78402015-04-29 10:09:40 -07001891 // Ensure video state history tracks video state at time of rejection.
1892 mVideoStateHistory |= mVideoState;
1893
Tyler Gunn165ee752016-09-06 13:40:10 -07001894 if (mConnectionService != null) {
1895 mConnectionService.reject(this, rejectWithMessage, textMessage);
1896 } else {
1897 Log.e(this, new NullPointerException(),
1898 "reject call failed due to null CS callId=%s", getId());
1899 }
Tyler Gunnf4f05392018-03-26 18:57:59 +00001900 Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
Santos Cordon61d0f702014-02-19 02:52:23 -08001901 }
1902 }
1903
1904 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001905 * Puts the call on hold if it is currently active.
1906 */
Pengquan Meng4832f202017-12-20 16:13:04 -08001907 @VisibleForTesting
1908 public void hold() {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001909 hold(null /* reason */);
1910 }
1911
1912 public void hold(String reason) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001913 if (mState == CallState.ACTIVE) {
Tyler Gunn165ee752016-09-06 13:40:10 -07001914 if (mConnectionService != null) {
1915 mConnectionService.hold(this);
1916 } else {
1917 Log.e(this, new NullPointerException(),
1918 "hold call failed due to null CS callId=%s", getId());
1919 }
Tyler Gunnf4f05392018-03-26 18:57:59 +00001920 Log.addEvent(this, LogUtils.Events.REQUEST_HOLD, reason);
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001921 }
1922 }
1923
1924 /**
1925 * Releases the call from hold if it is currently active.
1926 */
Pengquan Meng4832f202017-12-20 16:13:04 -08001927 @VisibleForTesting
1928 public void unhold() {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001929 unhold(null /* reason */);
1930 }
1931
1932 public void unhold(String reason) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001933 if (mState == CallState.ON_HOLD) {
Tyler Gunn165ee752016-09-06 13:40:10 -07001934 if (mConnectionService != null) {
1935 mConnectionService.unhold(this);
1936 } else {
1937 Log.e(this, new NullPointerException(),
1938 "unhold call failed due to null CS callId=%s", getId());
1939 }
Tyler Gunnf4f05392018-03-26 18:57:59 +00001940 Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD, reason);
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001941 }
1942 }
1943
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001944 /** Checks if this is a live call or not. */
Hall Liuf62630a2015-10-27 14:53:39 -07001945 @VisibleForTesting
1946 public boolean isAlive() {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001947 switch (mState) {
Ihab Awad6fb37c82014-08-07 19:48:57 -07001948 case CallState.NEW:
1949 case CallState.RINGING:
1950 case CallState.DISCONNECTED:
1951 case CallState.ABORTED:
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001952 return false;
1953 default:
1954 return true;
1955 }
1956 }
1957
Santos Cordon40f78c22014-04-07 02:11:42 -07001958 boolean isActive() {
Ihab Awad84bfe472014-07-13 17:11:57 -07001959 return mState == CallState.ACTIVE;
Santos Cordon40f78c22014-04-07 02:11:42 -07001960 }
1961
Sailesh Nepal84fa5f82014-04-02 11:01:11 -07001962 Bundle getExtras() {
1963 return mExtras;
1964 }
1965
Tyler Gunn961694a2016-03-21 16:01:40 -07001966 /**
1967 * Adds extras to the extras bundle associated with this {@link Call}.
1968 *
1969 * Note: this method needs to know the source of the extras change (see
1970 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which
1971 * originate from a connection service will only be notified to incall services. Likewise,
1972 * changes originating from the incall services will only notify the connection service of the
1973 * change.
1974 *
1975 * @param source The source of the extras addition.
1976 * @param extras The extras.
1977 */
1978 void putExtras(int source, Bundle extras) {
1979 if (extras == null) {
1980 return;
1981 }
1982 if (mExtras == null) {
1983 mExtras = new Bundle();
1984 }
1985 mExtras.putAll(extras);
1986
Santos Cordonb3907b32015-05-27 17:39:59 -07001987 for (Listener l : mListeners) {
Tyler Gunn961694a2016-03-21 16:01:40 -07001988 l.onExtrasChanged(this, source, extras);
1989 }
1990
1991 // If the change originated from an InCallService, notify the connection service.
1992 if (source == SOURCE_INCALL_SERVICE) {
Tyler Gunn165ee752016-09-06 13:40:10 -07001993 if (mConnectionService != null) {
1994 mConnectionService.onExtrasChanged(this, mExtras);
1995 } else {
1996 Log.e(this, new NullPointerException(),
1997 "putExtras failed due to null CS callId=%s", getId());
1998 }
Tyler Gunn961694a2016-03-21 16:01:40 -07001999 }
2000 }
2001
2002 /**
2003 * Removes extras from the extras bundle associated with this {@link Call}.
2004 *
2005 * Note: this method needs to know the source of the extras change (see
2006 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which
2007 * originate from a connection service will only be notified to incall services. Likewise,
2008 * changes originating from the incall services will only notify the connection service of the
2009 * change.
2010 *
2011 * @param source The source of the extras removal.
2012 * @param keys The extra keys to remove.
2013 */
2014 void removeExtras(int source, List<String> keys) {
2015 if (mExtras == null) {
2016 return;
2017 }
2018 for (String key : keys) {
2019 mExtras.remove(key);
2020 }
2021
2022 for (Listener l : mListeners) {
2023 l.onExtrasRemoved(this, source, keys);
2024 }
2025
2026 // If the change originated from an InCallService, notify the connection service.
2027 if (source == SOURCE_INCALL_SERVICE) {
Tyler Gunn165ee752016-09-06 13:40:10 -07002028 if (mConnectionService != null) {
2029 mConnectionService.onExtrasChanged(this, mExtras);
2030 } else {
2031 Log.e(this, new NullPointerException(),
2032 "removeExtras failed due to null CS callId=%s", getId());
2033 }
Santos Cordonb3907b32015-05-27 17:39:59 -07002034 }
2035 }
2036
Santos Cordonf0f99f32016-02-18 16:13:57 -08002037 @VisibleForTesting
2038 public Bundle getIntentExtras() {
Santos Cordonb3907b32015-05-27 17:39:59 -07002039 return mIntentExtras;
2040 }
2041
2042 void setIntentExtras(Bundle extras) {
2043 mIntentExtras = extras;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -07002044 }
2045
Tyler Gunnbbd78a72017-04-30 14:16:07 -07002046 public Intent getOriginalCallIntent() {
2047 return mOriginalCallIntent;
2048 }
2049
2050 public void setOriginalCallIntent(Intent intent) {
2051 mOriginalCallIntent = intent;
2052 }
2053
Yorke Lee8a736a82014-08-30 17:19:16 -07002054 /**
2055 * @return the uri of the contact associated with this call.
2056 */
Brad Ebingerd931a012015-10-21 12:54:08 -07002057 @VisibleForTesting
2058 public Uri getContactUri() {
Yorke Lee8a736a82014-08-30 17:19:16 -07002059 if (mCallerInfo == null || !mCallerInfo.contactExists) {
Yorke Leea7fa7432014-09-22 13:34:29 -07002060 return getHandle();
Yorke Lee8a736a82014-08-30 17:19:16 -07002061 }
2062 return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey);
2063 }
2064
Santos Cordon5ba7f272014-05-28 13:59:49 -07002065 Uri getRingtone() {
2066 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
2067 }
2068
Evan Charlton352105c2014-06-03 14:10:54 -07002069 void onPostDialWait(String remaining) {
2070 for (Listener l : mListeners) {
2071 l.onPostDialWait(this, remaining);
2072 }
2073 }
2074
Nancy Chena469f762014-12-09 18:29:20 -08002075 void onPostDialChar(char nextChar) {
2076 for (Listener l : mListeners) {
2077 l.onPostDialChar(this, nextChar);
2078 }
2079 }
2080
Evan Charlton352105c2014-06-03 14:10:54 -07002081 void postDialContinue(boolean proceed) {
Tyler Gunn165ee752016-09-06 13:40:10 -07002082 if (mConnectionService != null) {
2083 mConnectionService.onPostDialContinue(this, proceed);
2084 } else {
2085 Log.e(this, new NullPointerException(),
2086 "postDialContinue failed due to null CS callId=%s", getId());
2087 }
Evan Charlton352105c2014-06-03 14:10:54 -07002088 }
2089
Santos Cordon0fbe6322014-08-14 04:04:25 -07002090 void conferenceWith(Call otherCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -07002091 if (mConnectionService == null) {
2092 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -07002093 } else {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002094 Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall);
Santos Cordon0fbe6322014-08-14 04:04:25 -07002095 mConnectionService.conference(this, otherCall);
Santos Cordona1610702014-06-04 20:22:56 -07002096 }
2097 }
2098
2099 void splitFromConference() {
Santos Cordon672321e2014-08-21 14:38:58 -07002100 if (mConnectionService == null) {
2101 Log.w(this, "splitting from conference call without a connection service");
2102 } else {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002103 Log.addEvent(this, LogUtils.Events.SPLIT_FROM_CONFERENCE);
Santos Cordon672321e2014-08-21 14:38:58 -07002104 mConnectionService.splitFromConference(this);
2105 }
Santos Cordona1610702014-06-04 20:22:56 -07002106 }
2107
Brad Ebinger53855132015-10-30 10:58:19 -07002108 @VisibleForTesting
2109 public void mergeConference() {
Santos Cordon68059232014-09-04 20:09:42 -07002110 if (mConnectionService == null) {
2111 Log.w(this, "merging conference calls without a connection service.");
Ihab Awad07bc5ee2014-11-12 13:42:52 -08002112 } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002113 Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH);
Santos Cordon68059232014-09-04 20:09:42 -07002114 mConnectionService.mergeConference(this);
Santos Cordon88a4a602014-09-29 19:32:21 -07002115 mWasConferencePreviouslyMerged = true;
Santos Cordon68059232014-09-04 20:09:42 -07002116 }
2117 }
2118
Brad Ebinger53855132015-10-30 10:58:19 -07002119 @VisibleForTesting
2120 public void swapConference() {
Santos Cordon68059232014-09-04 20:09:42 -07002121 if (mConnectionService == null) {
2122 Log.w(this, "swapping conference calls without a connection service.");
Ihab Awad07bc5ee2014-11-12 13:42:52 -08002123 } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002124 Log.addEvent(this, LogUtils.Events.SWAP);
Santos Cordon68059232014-09-04 20:09:42 -07002125 mConnectionService.swapConference(this);
Santos Cordon88a4a602014-09-29 19:32:21 -07002126 switch (mChildCalls.size()) {
2127 case 1:
2128 mConferenceLevelActiveCall = mChildCalls.get(0);
2129 break;
2130 case 2:
2131 // swap
2132 mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
2133 mChildCalls.get(1) : mChildCalls.get(0);
2134 break;
2135 default:
2136 // For anything else 0, or 3+, set it to null since it is impossible to tell.
2137 mConferenceLevelActiveCall = null;
2138 break;
2139 }
Santos Cordon68059232014-09-04 20:09:42 -07002140 }
2141 }
2142
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002143 /**
2144 * Initiates a request to the connection service to pull this call.
2145 * <p>
2146 * This method can only be used for calls that have the
Tyler Gunn571d5e62016-03-15 15:55:18 -07002147 * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and
2148 * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002149 * <p>
2150 * An external call is a representation of a call which is taking place on another device
Santos Cordon0d7a00a2016-07-26 15:33:00 -07002151 * associated with a PhoneAccount on this device. Issuing a request to pull the external call
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002152 * tells the {@link android.telecom.ConnectionService} that it should move the call from the
2153 * other device to this one. An example of this is the IMS multi-endpoint functionality. A
2154 * user may have two phones with the same phone number. If the user is engaged in an active
2155 * call on their first device, the network will inform the second device of that ongoing call in
2156 * the form of an external call. The user may wish to continue their conversation on the second
2157 * device, so will issue a request to pull the call to the second device.
2158 * <p>
2159 * Requests to pull a call which is not external, or a call which is not pullable are ignored.
2160 */
2161 public void pullExternalCall() {
2162 if (mConnectionService == null) {
2163 Log.w(this, "pulling a call without a connection service.");
2164 }
2165
Tyler Gunn571d5e62016-03-15 15:55:18 -07002166 if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) {
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002167 Log.w(this, "pullExternalCall - call %s is not an external call.", mId);
2168 return;
2169 }
2170
2171 if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) {
2172 Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId);
2173 return;
2174 }
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002175 Log.addEvent(this, LogUtils.Events.REQUEST_PULL);
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002176 mConnectionService.pullExternalCall(this);
2177 }
2178
2179 /**
Sanket Padaweb5de1cd2018-01-05 14:27:30 -08002180 * Sends a call event to the {@link ConnectionService} for this call. This function is
2181 * called for event other than {@link Call#EVENT_REQUEST_HANDOVER}
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002182 *
2183 * @param event The call event.
2184 * @param extras Associated extras.
2185 */
2186 public void sendCallEvent(String event, Bundle extras) {
Sanket Padaweb5de1cd2018-01-05 14:27:30 -08002187 sendCallEvent(event, 0/*For Event != EVENT_REQUEST_HANDOVER*/, extras);
2188 }
2189
2190 /**
2191 * Sends a call event to the {@link ConnectionService} for this call.
2192 *
2193 * See {@link Call#sendCallEvent(String, Bundle)}.
2194 *
2195 * @param event The call event.
2196 * @param targetSdkVer SDK version of the app calling this api
2197 * @param extras Associated extras.
2198 */
2199 public void sendCallEvent(String event, int targetSdkVer, Bundle extras) {
Tyler Gunn165ee752016-09-06 13:40:10 -07002200 if (mConnectionService != null) {
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002201 if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) {
Sanket Padaweb5de1cd2018-01-05 14:27:30 -08002202 if (targetSdkVer > Build.VERSION_CODES.O_MR1) {
2203 Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" +
2204 " for API > 27(O-MR1)");
2205 // TODO: Add "return" after DUO team adds new API support for handover
2206 }
2207
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002208 // Handover requests are targeted at Telecom, not the ConnectionService.
2209 if (extras == null) {
2210 Log.w(this, "sendCallEvent: %s event received with null extras.",
2211 android.telecom.Call.EVENT_REQUEST_HANDOVER);
2212 mConnectionService.sendCallEvent(this,
2213 android.telecom.Call.EVENT_HANDOVER_FAILED, null);
2214 return;
2215 }
2216 Parcelable parcelable = extras.getParcelable(
2217 android.telecom.Call.EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE);
2218 if (!(parcelable instanceof PhoneAccountHandle) || parcelable == null) {
2219 Log.w(this, "sendCallEvent: %s event received with invalid handover acct.",
2220 android.telecom.Call.EVENT_REQUEST_HANDOVER);
2221 mConnectionService.sendCallEvent(this,
2222 android.telecom.Call.EVENT_HANDOVER_FAILED, null);
2223 return;
2224 }
2225 PhoneAccountHandle phoneAccountHandle = (PhoneAccountHandle) parcelable;
2226 int videoState = extras.getInt(android.telecom.Call.EXTRA_HANDOVER_VIDEO_STATE,
2227 VideoProfile.STATE_AUDIO_ONLY);
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07002228 Parcelable handoverExtras = extras.getParcelable(
2229 android.telecom.Call.EXTRA_HANDOVER_EXTRAS);
2230 Bundle handoverExtrasBundle = null;
2231 if (handoverExtras instanceof Bundle) {
2232 handoverExtrasBundle = (Bundle) handoverExtras;
2233 }
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08002234 requestHandover(phoneAccountHandle, videoState, handoverExtrasBundle, true);
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002235 } else {
2236 Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
2237 mConnectionService.sendCallEvent(this, event, extras);
2238 }
Tyler Gunn165ee752016-09-06 13:40:10 -07002239 } else {
2240 Log.e(this, new NullPointerException(),
2241 "sendCallEvent failed due to null CS callId=%s", getId());
2242 }
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002243 }
2244
Tyler Gunn76581712017-05-09 14:39:42 -07002245 /**
Sanket Padaweccdf3642017-11-10 14:49:05 -08002246 * Initiates a handover of this Call to the {@link ConnectionService} identified
2247 * by destAcct.
2248 * @param destAcct ConnectionService to which the call should be handed over.
2249 * @param videoState The video state desired after the handover.
2250 * @param extras Extra information to be passed to ConnectionService
2251 */
2252 public void handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras) {
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08002253 requestHandover(destAcct, videoState, extras, false);
Sanket Padaweccdf3642017-11-10 14:49:05 -08002254 }
2255
2256 /**
Tyler Gunn76581712017-05-09 14:39:42 -07002257 * Sets this {@link Call} to has the specified {@code parentCall}. Also sets the parent to
2258 * have this call as a child.
2259 * @param parentCall
2260 */
2261 void setParentAndChildCall(Call parentCall) {
Tyler Gunn988ccc82017-06-01 11:22:10 -07002262 boolean isParentChanging = (mParentCall != parentCall);
Tyler Gunn76581712017-05-09 14:39:42 -07002263 setParentCall(parentCall);
2264 setChildOf(parentCall);
Tyler Gunn988ccc82017-06-01 11:22:10 -07002265 if (isParentChanging) {
2266 notifyParentChanged(parentCall);
2267 }
2268 }
2269
2270 /**
2271 * Notifies listeners when the parent call changes.
2272 * Used by {@link #setParentAndChildCall(Call)}, and in {@link CallsManager}.
2273 * @param parentCall The new parent call for this call.
2274 */
2275 void notifyParentChanged(Call parentCall) {
2276 Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall);
2277 for (Listener l : mListeners) {
2278 l.onParentChanged(this);
2279 }
Tyler Gunn76581712017-05-09 14:39:42 -07002280 }
2281
2282 /**
2283 * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set
2284 * the child.
2285 * TODO: This is only required when adding existing connections as a workaround so that we
2286 * can avoid sending the "onParentChanged" callback until later.
2287 * @param parentCall The new parent call.
2288 */
Santos Cordona1610702014-06-04 20:22:56 -07002289 void setParentCall(Call parentCall) {
2290 if (parentCall == this) {
2291 Log.e(this, new Exception(), "setting the parent to self");
2292 return;
2293 }
Santos Cordon0fbe6322014-08-14 04:04:25 -07002294 if (parentCall == mParentCall) {
2295 // nothing to do
2296 return;
2297 }
Santos Cordona1610702014-06-04 20:22:56 -07002298 if (mParentCall != null) {
2299 mParentCall.removeChildCall(this);
2300 }
2301 mParentCall = parentCall;
Tyler Gunn76581712017-05-09 14:39:42 -07002302 }
2303
2304 /**
2305 * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding
2306 * this call as a child of another call.
2307 * <p>
Tyler Gunn988ccc82017-06-01 11:22:10 -07002308 * Note: if using this method alone, the caller must call {@link #notifyParentChanged(Call)} to
2309 * ensure the InCall UI is updated with the change in parent.
Tyler Gunn76581712017-05-09 14:39:42 -07002310 * @param parentCall The new parent for this call.
2311 */
2312 void setChildOf(Call parentCall) {
Tyler Gunn988ccc82017-06-01 11:22:10 -07002313 if (parentCall != null && !parentCall.getChildCalls().contains(this)) {
Tyler Gunn76581712017-05-09 14:39:42 -07002314 parentCall.addChildCall(this);
Santos Cordona1610702014-06-04 20:22:56 -07002315 }
2316 }
2317
Santos Cordon12d61822014-07-29 16:02:20 -07002318 void setConferenceableCalls(List<Call> conferenceableCalls) {
2319 mConferenceableCalls.clear();
2320 mConferenceableCalls.addAll(conferenceableCalls);
Tyler Gunn703d7cf2014-10-02 15:22:30 -07002321
2322 for (Listener l : mListeners) {
2323 l.onConferenceableCallsChanged(this);
2324 }
Santos Cordon12d61822014-07-29 16:02:20 -07002325 }
2326
Brad Ebinger53855132015-10-30 10:58:19 -07002327 @VisibleForTesting
2328 public List<Call> getConferenceableCalls() {
Santos Cordon12d61822014-07-29 16:02:20 -07002329 return mConferenceableCalls;
2330 }
2331
Brad Ebinger53855132015-10-30 10:58:19 -07002332 @VisibleForTesting
2333 public boolean can(int capability) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -08002334 return (mConnectionCapabilities & capability) == capability;
Santos Cordon68059232014-09-04 20:09:42 -07002335 }
2336
Tyler Gunn571d5e62016-03-15 15:55:18 -07002337 @VisibleForTesting
2338 public boolean hasProperty(int property) {
2339 return (mConnectionProperties & property) == property;
2340 }
2341
Santos Cordona1610702014-06-04 20:22:56 -07002342 private void addChildCall(Call call) {
2343 if (!mChildCalls.contains(call)) {
Santos Cordon88a4a602014-09-29 19:32:21 -07002344 // Set the pseudo-active call to the latest child added to the conference.
2345 // See definition of mConferenceLevelActiveCall for more detail.
2346 mConferenceLevelActiveCall = call;
Santos Cordona1610702014-06-04 20:22:56 -07002347 mChildCalls.add(call);
2348
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002349 Log.addEvent(this, LogUtils.Events.ADD_CHILD, call);
Santos Cordon165c1ce2015-07-10 16:07:59 -07002350
Santos Cordona1610702014-06-04 20:22:56 -07002351 for (Listener l : mListeners) {
2352 l.onChildrenChanged(this);
2353 }
2354 }
2355 }
2356
2357 private void removeChildCall(Call call) {
2358 if (mChildCalls.remove(call)) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002359 Log.addEvent(this, LogUtils.Events.REMOVE_CHILD, call);
Santos Cordona1610702014-06-04 20:22:56 -07002360 for (Listener l : mListeners) {
2361 l.onChildrenChanged(this);
2362 }
2363 }
2364 }
2365
Santos Cordon0b03b4b2014-01-29 18:01:59 -08002366 /**
Ihab Awadff7493a2014-06-10 13:47:44 -07002367 * Return whether the user can respond to this {@code Call} via an SMS message.
2368 *
2369 * @return true if the "Respond via SMS" feature should be enabled
2370 * for this incoming call.
2371 *
2372 * The general rule is that we *do* allow "Respond via SMS" except for
2373 * the few (relatively rare) cases where we know for sure it won't
2374 * work, namely:
2375 * - a bogus or blank incoming number
2376 * - a call from a SIP address
2377 * - a "call presentation" that doesn't allow the number to be revealed
2378 *
2379 * In all other cases, we allow the user to respond via SMS.
2380 *
2381 * Note that this behavior isn't perfect; for example we have no way
2382 * to detect whether the incoming call is from a landline (with most
2383 * networks at least), so we still enable this feature even though
2384 * SMSes to that number will silently fail.
2385 */
2386 boolean isRespondViaSmsCapable() {
2387 if (mState != CallState.RINGING) {
2388 return false;
2389 }
2390
2391 if (getHandle() == null) {
2392 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
2393 // other words, the user should not be able to see the incoming phone number.
2394 return false;
2395 }
2396
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07002397 if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
Ihab Awadff7493a2014-06-10 13:47:44 -07002398 // The incoming number is actually a URI (i.e. a SIP address),
2399 // not a regular PSTN phone number, and we can't send SMSes to
2400 // SIP addresses.
2401 // (TODO: That might still be possible eventually, though. Is
2402 // there some SIP-specific equivalent to sending a text message?)
2403 return false;
2404 }
2405
2406 // Is there a valid SMS application on the phone?
Tyler Gunn91d43cf2014-09-17 12:19:39 -07002407 if (SmsApplication.getDefaultRespondViaMessageApplication(mContext,
Ihab Awadff7493a2014-06-10 13:47:44 -07002408 true /*updateIfNeeded*/) == null) {
2409 return false;
2410 }
2411
2412 // TODO: with some carriers (in certain countries) you *can* actually
2413 // tell whether a given number is a mobile phone or not. So in that
2414 // case we could potentially return false here if the incoming call is
2415 // from a land line.
2416
2417 // If none of the above special cases apply, it's OK to enable the
2418 // "Respond via SMS" feature.
2419 return true;
2420 }
2421
2422 List<String> getCannedSmsResponses() {
2423 return mCannedSmsResponses;
2424 }
2425
2426 /**
Santos Cordon672321e2014-08-21 14:38:58 -07002427 * We need to make sure that before we move a call to the disconnected state, it no
2428 * longer has any parent/child relationships. We want to do this to ensure that the InCall
Tyler Gunn7cc70b42014-09-12 22:17:27 -07002429 * 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 -07002430 * that the insurance policy lives in the framework side of things.
2431 */
2432 private void fixParentAfterDisconnect() {
Tyler Gunn76581712017-05-09 14:39:42 -07002433 setParentAndChildCall(null);
Santos Cordon672321e2014-08-21 14:38:58 -07002434 }
2435
2436 /**
Santos Cordon61d0f702014-02-19 02:52:23 -08002437 * @return True if the call is ringing, else logs the action name.
2438 */
2439 private boolean isRinging(String actionName) {
2440 if (mState == CallState.RINGING) {
2441 return true;
2442 }
2443
Sailesh Nepalf1c191d2014-03-07 18:17:39 -08002444 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -08002445 return false;
2446 }
2447
Ben Gilad8e55d1d2014-02-26 16:25:56 -08002448 @SuppressWarnings("rawtypes")
2449 private void decrementAssociatedCallCount(ServiceBinder binder) {
2450 if (binder != null) {
2451 binder.decrementAssociatedCallCount();
2452 }
2453 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002454
2455 /**
2456 * Looks up contact information based on the current handle.
2457 */
2458 private void startCallerInfoLookup() {
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002459 mCallerInfo = null;
Hall Liu5b70c1c2016-03-03 18:42:57 -08002460 mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener);
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002461 }
2462
2463 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -07002464 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002465 * that was made.
2466 *
2467 * @param callerInfo The new caller information to set.
Santos Cordonfd71f4a2014-05-28 13:59:49 -07002468 */
Hall Liu5b70c1c2016-03-03 18:42:57 -08002469 private void setCallerInfo(Uri handle, CallerInfo callerInfo) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002470 Trace.beginSection("setCallerInfo");
Shigeru Muraif2373e12016-09-12 10:26:23 +09002471 if (callerInfo == null) {
2472 Log.i(this, "CallerInfo lookup returned null, skipping update");
2473 return;
2474 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -07002475
Srikanth Chintala3bf22282017-09-06 10:24:26 +05302476 if ((handle != null) && !handle.equals(mHandle)) {
Hall Liu5b70c1c2016-03-03 18:42:57 -08002477 Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring.");
2478 return;
Santos Cordon99c8a6f2014-05-28 18:28:47 -07002479 }
Hall Liu5b70c1c2016-03-03 18:42:57 -08002480
2481 mCallerInfo = callerInfo;
2482 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
2483
2484 if (mCallerInfo.contactDisplayPhotoUri == null ||
2485 mCallerInfo.cachedPhotoIcon != null || mCallerInfo.cachedPhoto != null) {
2486 for (Listener l : mListeners) {
2487 l.onCallerInfoChanged(this);
2488 }
2489 }
2490
Yorke Leee4a9c412014-11-14 16:59:42 -08002491 Trace.endSection();
Santos Cordon99c8a6f2014-05-28 18:28:47 -07002492 }
2493
Victor Chang9e7f98a2016-01-04 16:03:27 +00002494 public CallerInfo getCallerInfo() {
Yorke Lee6f3f7af2014-07-11 10:59:46 -07002495 return mCallerInfo;
2496 }
2497
Ihab Awadff7493a2014-06-10 13:47:44 -07002498 private void maybeLoadCannedSmsResponses() {
Hall Liu32587202015-11-18 11:10:08 -08002499 if (mCallDirection == CALL_DIRECTION_INCOMING
2500 && isRespondViaSmsCapable()
2501 && !mCannedSmsResponsesLoadingStarted) {
Ihab Awadff7493a2014-06-10 13:47:44 -07002502 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
2503 mCannedSmsResponsesLoadingStarted = true;
Ihab Awad8de76912015-02-17 12:25:52 -08002504 mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
Ihab Awadff7493a2014-06-10 13:47:44 -07002505 new Response<Void, List<String>>() {
2506 @Override
2507 public void onResult(Void request, List<String>... result) {
2508 if (result.length > 0) {
2509 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
2510 mCannedSmsResponses = result[0];
2511 for (Listener l : mListeners) {
2512 l.onCannedSmsResponsesLoaded(Call.this);
2513 }
2514 }
2515 }
2516
2517 @Override
2518 public void onError(Void request, int code, String msg) {
2519 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
2520 msg);
2521 }
Tyler Gunn91d43cf2014-09-17 12:19:39 -07002522 },
2523 mContext
Ihab Awadff7493a2014-06-10 13:47:44 -07002524 );
2525 } else {
2526 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
2527 }
2528 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07002529
2530 /**
2531 * Sets speakerphone option on when call begins.
2532 */
2533 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
2534 mSpeakerphoneOn = startWithSpeakerphone;
2535 }
2536
2537 /**
2538 * Returns speakerphone option.
2539 *
2540 * @return Whether or not speakerphone should be set automatically when call begins.
2541 */
2542 public boolean getStartWithSpeakerphoneOn() {
2543 return mSpeakerphoneOn;
2544 }
Andrew Leee9a77652014-06-26 13:07:57 -07002545
Hall Liu825f6ad2018-01-29 17:34:54 -08002546 public void setRequestedToStartWithRtt() {
2547 mDidRequestToStartWithRtt = true;
2548 }
2549
Hall Liuaeece4e2017-02-14 16:42:12 -08002550 public void stopRtt() {
2551 if (mConnectionService != null) {
2552 mConnectionService.stopRtt(this);
2553 } else {
2554 // If this gets called by the in-call app before the connection service is set, we'll
2555 // just ignore it since it's really not supposed to happen.
2556 Log.w(this, "stopRtt() called before connection service is set.");
2557 }
2558 }
2559
2560 public void sendRttRequest() {
Hall Liuea67a762018-02-09 16:48:57 -08002561 createRttStreams();
Hall Liuaeece4e2017-02-14 16:42:12 -08002562 mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2563 }
2564
Hall Liu825f6ad2018-01-29 17:34:54 -08002565 private boolean areRttStreamsInitialized() {
2566 return mInCallToConnectionServiceStreams != null
Hall Liudd68bc32017-01-25 17:14:23 -08002567 && mConnectionServiceToInCallStreams != null;
Hall Liu825f6ad2018-01-29 17:34:54 -08002568 }
2569
Hall Liuea67a762018-02-09 16:48:57 -08002570 public void createRttStreams() {
2571 if (!areRttStreamsInitialized()) {
2572 Log.i(this, "Initializing RTT streams");
Hall Liudd68bc32017-01-25 17:14:23 -08002573 try {
Hall Liu3a75c622017-11-20 17:45:33 -08002574 mWasEverRtt = true;
Hall Liudd68bc32017-01-25 17:14:23 -08002575 mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe();
2576 mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe();
2577 } catch (IOException e) {
2578 Log.e(this, e, "Failed to create pipes for RTT call.");
2579 }
Hall Liudd68bc32017-01-25 17:14:23 -08002580 }
2581 }
2582
Hall Liuaeece4e2017-02-14 16:42:12 -08002583 public void onRttConnectionFailure(int reason) {
Hall Liu825f6ad2018-01-29 17:34:54 -08002584 Log.i(this, "Got RTT initiation failure with reason %d", reason);
Hall Liuaeece4e2017-02-14 16:42:12 -08002585 for (Listener l : mListeners) {
2586 l.onRttInitiationFailure(this, reason);
2587 }
2588 }
2589
2590 public void onRemoteRttRequest() {
2591 if (isRttCall()) {
2592 Log.w(this, "Remote RTT request on a call that's already RTT");
2593 return;
2594 }
2595
2596 mPendingRttRequestId = mCallsManager.getNextRttRequestId();
2597 for (Listener l : mListeners) {
2598 l.onRemoteRttRequest(this, mPendingRttRequestId);
2599 }
2600 }
2601
2602 public void handleRttRequestResponse(int id, boolean accept) {
2603 if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) {
2604 Log.w(this, "Response received to a nonexistent RTT request: %d", id);
2605 return;
2606 }
2607 if (id != mPendingRttRequestId) {
2608 Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId);
2609 return;
2610 }
Hall Liuaeece4e2017-02-14 16:42:12 -08002611 if (accept) {
Hall Liuea67a762018-02-09 16:48:57 -08002612 createRttStreams();
Hall Liuaeece4e2017-02-14 16:42:12 -08002613 Log.i(this, "RTT request %d accepted.", id);
2614 mConnectionService.respondToRttRequest(
2615 this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2616 } else {
2617 Log.i(this, "RTT request %d rejected.", id);
2618 mConnectionService.respondToRttRequest(this, null, null);
2619 }
2620 }
2621
Hall Liudd68bc32017-01-25 17:14:23 -08002622 public boolean isRttCall() {
2623 return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
2624 }
2625
Hall Liu3a75c622017-11-20 17:45:33 -08002626 public boolean wasEverRttCall() {
2627 return mWasEverRtt;
2628 }
2629
Hall Liudd68bc32017-01-25 17:14:23 -08002630 public ParcelFileDescriptor getCsToInCallRttPipeForCs() {
2631 return mConnectionServiceToInCallStreams == null ? null
2632 : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX];
2633 }
2634
2635 public ParcelFileDescriptor getInCallToCsRttPipeForCs() {
2636 return mInCallToConnectionServiceStreams == null ? null
2637 : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX];
2638 }
2639
2640 public ParcelFileDescriptor getCsToInCallRttPipeForInCall() {
2641 return mConnectionServiceToInCallStreams == null ? null
2642 : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX];
2643 }
2644
2645 public ParcelFileDescriptor getInCallToCsRttPipeForInCall() {
2646 return mInCallToConnectionServiceStreams == null ? null
2647 : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX];
2648 }
2649
2650 public int getRttMode() {
2651 return mRttMode;
2652 }
2653
Andrew Leee9a77652014-06-26 13:07:57 -07002654 /**
Andrew Lee3bcf9352014-07-23 12:36:05 -07002655 * Sets a video call provider for the call.
Andrew Leee9a77652014-06-26 13:07:57 -07002656 */
Ihab Awad6fb37c82014-08-07 19:48:57 -07002657 public void setVideoProvider(IVideoProvider videoProvider) {
Tyler Gunn86014fc2015-06-12 14:31:25 -07002658 Log.v(this, "setVideoProvider");
2659
Ravi Palurib4088ee2016-12-20 20:00:00 +05302660 if (mVideoProviderProxy != null) {
2661 mVideoProviderProxy.clearVideoCallback();
2662 mVideoProviderProxy = null;
2663 }
2664
Tyler Gunn86014fc2015-06-12 14:31:25 -07002665 if (videoProvider != null ) {
2666 try {
Tyler Gunn1bf0e6b2016-11-30 15:19:13 -08002667 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this,
2668 mCallsManager);
Tyler Gunn86014fc2015-06-12 14:31:25 -07002669 } catch (RemoteException ignored) {
2670 // Ignore RemoteException.
2671 }
Tyler Gunn86014fc2015-06-12 14:31:25 -07002672 }
2673
Ihab Awad6fb37c82014-08-07 19:48:57 -07002674 mVideoProvider = videoProvider;
Tyler Gunn86014fc2015-06-12 14:31:25 -07002675
Nancy Chena65d41f2014-06-24 12:06:03 -07002676 for (Listener l : mListeners) {
Andrew Lee3bcf9352014-07-23 12:36:05 -07002677 l.onVideoCallProviderChanged(Call.this);
Nancy Chena65d41f2014-06-24 12:06:03 -07002678 }
2679 }
2680
2681 /**
Tyler Gunn86014fc2015-06-12 14:31:25 -07002682 * @return The {@link Connection.VideoProvider} binder.
Nancy Chena65d41f2014-06-24 12:06:03 -07002683 */
Ihab Awad6fb37c82014-08-07 19:48:57 -07002684 public IVideoProvider getVideoProvider() {
Tyler Gunn86014fc2015-06-12 14:31:25 -07002685 if (mVideoProviderProxy == null) {
2686 return null;
2687 }
2688
2689 return mVideoProviderProxy.getInterface();
2690 }
2691
2692 /**
2693 * @return The {@link VideoProviderProxy} for this call.
2694 */
2695 public VideoProviderProxy getVideoProviderProxy() {
2696 return mVideoProviderProxy;
Andrew Leee9a77652014-06-26 13:07:57 -07002697 }
Tyler Gunne19cc002014-07-01 11:32:53 -07002698
2699 /**
Tyler Gunnc4abd912014-07-08 14:22:10 -07002700 * The current video state for the call.
Tyler Gunn5b882492015-06-03 10:03:13 -07002701 * See {@link VideoProfile} for a list of valid video states.
Tyler Gunnc4abd912014-07-08 14:22:10 -07002702 */
2703 public int getVideoState() {
2704 return mVideoState;
2705 }
2706
2707 /**
Tyler Gunn0a388fc2014-07-17 12:21:17 -07002708 * Returns the video states which were applicable over the duration of a call.
Ihab Awad6fb37c82014-08-07 19:48:57 -07002709 * See {@link VideoProfile} for a list of valid video states.
Tyler Gunn0a388fc2014-07-17 12:21:17 -07002710 *
2711 * @return The video states applicable over the duration of the call.
2712 */
2713 public int getVideoStateHistory() {
2714 return mVideoStateHistory;
2715 }
2716
2717 /**
2718 * Determines the current video state for the call.
2719 * For an outgoing call determines the desired video state for the call.
Tyler Gunn5b882492015-06-03 10:03:13 -07002720 * Valid values: see {@link VideoProfile}
Tyler Gunnc4abd912014-07-08 14:22:10 -07002721 *
Tyler Gunn0a388fc2014-07-17 12:21:17 -07002722 * @param videoState The video state for the call.
Tyler Gunnc4abd912014-07-08 14:22:10 -07002723 */
2724 public void setVideoState(int videoState) {
Tyler Gunn13e8a692016-08-03 18:38:00 -07002725 // If the phone account associated with this call does not support video calling, then we
2726 // will automatically set the video state to audio-only.
2727 if (!isVideoCallingSupported()) {
Tyler Gunnddec5892016-09-28 10:26:13 -07002728 Log.d(this, "setVideoState: videoState=%s defaulted to audio (video not supported)",
2729 VideoProfile.videoStateToString(videoState));
Tyler Gunn13e8a692016-08-03 18:38:00 -07002730 videoState = VideoProfile.STATE_AUDIO_ONLY;
2731 }
2732
Brad Ebingerb079fc52017-07-07 15:59:13 -07002733 // Track Video State history during the duration of the call.
2734 // Only update the history when the call is active or disconnected. This ensures we do
2735 // not include the video state history when:
Tyler Gunneb39aa22015-04-03 11:47:28 -07002736 // - Call is incoming (but not answered).
2737 // - Call it outgoing (but not answered).
2738 // We include the video state when disconnected to ensure that rejected calls reflect the
2739 // appropriate video state.
Brad Ebingerb079fc52017-07-07 15:59:13 -07002740 // For all other times we add to the video state history, see #setState.
Tyler Gunneb39aa22015-04-03 11:47:28 -07002741 if (isActive() || getState() == CallState.DISCONNECTED) {
2742 mVideoStateHistory = mVideoStateHistory | videoState;
2743 }
Tyler Gunn0a388fc2014-07-17 12:21:17 -07002744
Hall Liu9696c212016-06-24 16:09:02 -07002745 int previousVideoState = mVideoState;
Tyler Gunnc4abd912014-07-08 14:22:10 -07002746 mVideoState = videoState;
Hall Liu9696c212016-06-24 16:09:02 -07002747 if (mVideoState != previousVideoState) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002748 Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED,
Hall Liu9696c212016-06-24 16:09:02 -07002749 VideoProfile.videoStateToString(videoState));
2750 for (Listener l : mListeners) {
2751 l.onVideoStateChanged(this, previousVideoState, mVideoState);
2752 }
Andrew Lee4a796602014-07-11 17:23:03 -07002753 }
Hall Liu4640c4f2016-06-22 17:13:20 -07002754
2755 if (VideoProfile.isVideo(videoState)) {
2756 mAnalytics.setCallIsVideo(true);
2757 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07002758 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07002759
Andrew Lee5be64bc2014-09-08 18:35:15 -07002760 public boolean getIsVoipAudioMode() {
2761 return mIsVoipAudioMode;
Sailesh Nepal7e669572014-07-08 21:29:12 -07002762 }
2763
Andrew Lee5be64bc2014-09-08 18:35:15 -07002764 public void setIsVoipAudioMode(boolean audioModeIsVoip) {
2765 mIsVoipAudioMode = audioModeIsVoip;
Sailesh Nepal7e669572014-07-08 21:29:12 -07002766 for (Listener l : mListeners) {
Andrew Lee5be64bc2014-09-08 18:35:15 -07002767 l.onIsVoipAudioModeChanged(this);
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07002768 }
2769 }
2770
2771 public StatusHints getStatusHints() {
2772 return mStatusHints;
2773 }
2774
2775 public void setStatusHints(StatusHints statusHints) {
2776 mStatusHints = statusHints;
2777 for (Listener l : mListeners) {
2778 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07002779 }
2780 }
Sailesh Nepal9d58de52014-07-18 14:53:19 -07002781
Yorke Lee9250e5f2014-10-01 13:39:09 -07002782 public boolean isUnknown() {
Hall Liu32587202015-11-18 11:10:08 -08002783 return mCallDirection == CALL_DIRECTION_UNKNOWN;
Yorke Lee9250e5f2014-10-01 13:39:09 -07002784 }
2785
Tyler Gunnaa479372014-10-07 10:18:42 -07002786 /**
2787 * Determines if this call is in a disconnecting state.
2788 *
2789 * @return {@code true} if this call is locally disconnecting.
2790 */
2791 public boolean isLocallyDisconnecting() {
2792 return mIsLocallyDisconnecting;
2793 }
2794
2795 /**
2796 * Sets whether this call is in a disconnecting state.
2797 *
2798 * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
2799 */
2800 private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
2801 mIsLocallyDisconnecting = isLocallyDisconnecting;
2802 }
2803
Tony Mak578a4e62015-11-23 11:18:51 +00002804 /**
2805 * @return user handle of user initiating the outgoing call.
2806 */
2807 public UserHandle getInitiatingUser() {
2808 return mInitiatingUser;
2809 }
2810
2811 /**
2812 * Set the user handle of user initiating the outgoing call.
2813 * @param initiatingUser
2814 */
2815 public void setInitiatingUser(UserHandle initiatingUser) {
2816 Preconditions.checkNotNull(initiatingUser);
2817 mInitiatingUser = initiatingUser;
2818 }
2819
Santos Cordon0fbe6322014-08-14 04:04:25 -07002820 static int getStateFromConnectionState(int state) {
Santos Cordon72890ce2014-07-21 01:32:04 -07002821 switch (state) {
Ihab Awada02bef52014-08-13 18:18:42 -07002822 case Connection.STATE_INITIALIZING:
2823 return CallState.CONNECTING;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002824 case Connection.STATE_ACTIVE:
Santos Cordon72890ce2014-07-21 01:32:04 -07002825 return CallState.ACTIVE;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002826 case Connection.STATE_DIALING:
Santos Cordon72890ce2014-07-21 01:32:04 -07002827 return CallState.DIALING;
Tyler Gunn1e37be52016-07-11 08:54:23 -07002828 case Connection.STATE_PULLING_CALL:
2829 return CallState.PULLING;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002830 case Connection.STATE_DISCONNECTED:
Santos Cordon72890ce2014-07-21 01:32:04 -07002831 return CallState.DISCONNECTED;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002832 case Connection.STATE_HOLDING:
Santos Cordon72890ce2014-07-21 01:32:04 -07002833 return CallState.ON_HOLD;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002834 case Connection.STATE_NEW:
Santos Cordon72890ce2014-07-21 01:32:04 -07002835 return CallState.NEW;
Ihab Awad6fb37c82014-08-07 19:48:57 -07002836 case Connection.STATE_RINGING:
Santos Cordon72890ce2014-07-21 01:32:04 -07002837 return CallState.RINGING;
2838 }
2839 return CallState.DISCONNECTED;
2840 }
Roshan Pius669f90b2015-09-02 16:21:39 -07002841
2842 /**
2843 * Determines if this call is in disconnected state and waiting to be destroyed.
2844 *
2845 * @return {@code true} if this call is disconected.
2846 */
2847 public boolean isDisconnected() {
2848 return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
2849 }
Tyler Gunn7a1f8232015-12-03 19:52:59 -08002850
2851 /**
Brad Ebinger3da57642016-04-04 17:32:19 -07002852 * Determines if this call has just been created and has not been configured properly yet.
2853 *
2854 * @return {@code true} if this call is new.
2855 */
2856 public boolean isNew() {
2857 return getState() == CallState.NEW;
2858 }
2859
2860 /**
Tyler Gunn7a1f8232015-12-03 19:52:59 -08002861 * Sets the call data usage for the call.
2862 *
2863 * @param callDataUsage The new call data usage (in bytes).
2864 */
2865 public void setCallDataUsage(long callDataUsage) {
2866 mCallDataUsage = callDataUsage;
2867 }
2868
2869 /**
2870 * Returns the call data usage for the call.
2871 *
2872 * @return The call data usage (in bytes).
2873 */
2874 public long getCallDataUsage() {
2875 return mCallDataUsage;
2876 }
Tony Mak3ae2dc82016-01-07 11:49:38 +00002877
Hall Liudd68bc32017-01-25 17:14:23 -08002878 public void setRttMode(int mode) {
2879 mRttMode = mode;
2880 // TODO: hook this up to CallAudioManager
2881 }
2882
Hall Liu4b1759d2016-02-02 18:17:40 -08002883 /**
2884 * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent
2885 * has come back to telecom and was processed.
2886 */
2887 public boolean isNewOutgoingCallIntentBroadcastDone() {
2888 return mIsNewOutgoingCallIntentBroadcastDone;
2889 }
2890
2891 public void setNewOutgoingCallIntentBroadcastIsDone() {
2892 mIsNewOutgoingCallIntentBroadcastDone = true;
2893 }
Tyler Gunndb821912016-02-16 14:35:25 -08002894
2895 /**
2896 * Determines if the call has been held by the remote party.
2897 *
2898 * @return {@code true} if the call is remotely held, {@code false} otherwise.
2899 */
2900 public boolean isRemotelyHeld() {
2901 return mIsRemotelyHeld;
2902 }
2903
2904 /**
2905 * Handles Connection events received from a {@link ConnectionService}.
2906 *
2907 * @param event The event.
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002908 * @param extras The extras.
Tyler Gunndb821912016-02-16 14:35:25 -08002909 */
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002910 public void onConnectionEvent(String event, Bundle extras) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002911 Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event);
Tyler Gunndb821912016-02-16 14:35:25 -08002912 if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
2913 mIsRemotelyHeld = true;
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002914 Log.addEvent(this, LogUtils.Events.REMOTELY_HELD);
Tyler Gunndb821912016-02-16 14:35:25 -08002915 // Inform listeners of the fact that a call hold tone was received. This will trigger
2916 // the CallAudioManager to play a tone via the InCallTonePlayer.
2917 for (Listener l : mListeners) {
2918 l.onHoldToneRequested(this);
2919 }
2920 } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
2921 mIsRemotelyHeld = false;
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002922 Log.addEvent(this, LogUtils.Events.REMOTELY_UNHELD);
Tyler Gunndb821912016-02-16 14:35:25 -08002923 for (Listener l : mListeners) {
2924 l.onHoldToneRequested(this);
2925 }
Tyler Gunnd45e6d92016-03-10 20:15:39 -08002926 } else {
2927 for (Listener l : mListeners) {
2928 l.onConnectionEvent(this, event, extras);
2929 }
Tyler Gunndb821912016-02-16 14:35:25 -08002930 }
2931 }
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07002932
Tyler Gunn7c031f22018-01-18 15:00:41 -08002933 /**
2934 * Notifies interested parties that the handover has completed.
2935 * Notifies:
2936 * 1. {@link InCallController} which communicates this to the
2937 * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}.
2938 * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of
2939 * the successful handover.
2940 */
2941 public void onHandoverComplete() {
2942 Log.i(this, "onHandoverComplete; callId=%s", getId());
2943 if (mConnectionService != null) {
2944 mConnectionService.handoverComplete(this);
2945 }
2946 for (Listener l : mListeners) {
2947 l.onHandoverComplete(this);
2948 }
2949 }
2950
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08002951 public void onHandoverFailed(int handoverError) {
Tyler Gunn7c031f22018-01-18 15:00:41 -08002952 Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError);
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08002953 for (Listener l : mListeners) {
2954 l.onHandoverFailed(this, handoverError);
2955 }
2956 }
2957
Tyler Gunn9b618b82016-10-17 15:54:35 -07002958 public void setOriginalConnectionId(String originalConnectionId) {
2959 mOriginalConnectionId = originalConnectionId;
2960 }
2961
2962 /**
2963 * For calls added via a ConnectionManager using the
2964 * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
2965 * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS,
2966 * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
2967 * originally created it.
2968 *
2969 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
2970 * @return The original connection ID.
2971 */
2972 public String getOriginalConnectionId() {
2973 return mOriginalConnectionId;
2974 }
2975
Pengquan Meng4832f202017-12-20 16:13:04 -08002976 ConnectionServiceFocusManager getConnectionServiceFocusManager() {
2977 return mCallsManager.getConnectionServiceFocusManager();
2978 }
2979
Tyler Gunnfa1e97e2016-04-25 21:36:37 -07002980 /**
2981 * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
2982 * remotely or locally.
2983 *
2984 * @param capabilities The {@link Connection} capabilities for the call.
2985 * @return {@code true} if video is supported, {@code false} otherwise.
2986 */
2987 private boolean doesCallSupportVideo(int capabilities) {
2988 return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 ||
2989 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
2990 }
2991
2992 /**
2993 * Remove any video capabilities set on a {@link Connection} capabilities bitmask.
2994 *
2995 * @param capabilities The capabilities.
2996 * @return The bitmask with video capabilities removed.
2997 */
2998 private int removeVideoCapabilities(int capabilities) {
2999 return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL |
3000 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
3001 }
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003002
3003 /**
3004 * Initiates a handover of this {@link Call} to another {@link PhoneAccount}.
3005 * @param handoverToHandle The {@link PhoneAccountHandle} to handover to.
3006 * @param videoState The video state of the call when handed over.
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07003007 * @param extras Optional extras {@link Bundle} provided by the initiating
3008 * {@link android.telecom.InCallService}.
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003009 */
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07003010 private void requestHandover(PhoneAccountHandle handoverToHandle, int videoState,
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003011 Bundle extras, boolean isLegacy) {
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003012 for (Listener l : mListeners) {
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003013 l.onHandoverRequested(this, handoverToHandle, videoState, extras, isLegacy);
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003014 }
3015 }
Brad Ebingerb079fc52017-07-07 15:59:13 -07003016
3017 /**
3018 * Sets the video history based on the state and state transitions of the call. Always add the
3019 * current video state to the video state history during a call transition except for the
3020 * transitions DIALING->ACTIVE and RINGING->ACTIVE. In these cases, clear the history. If a
3021 * call starts dialing/ringing as a VT call and gets downgraded to audio, we need to record
3022 * the history as an audio call.
3023 */
3024 private void updateVideoHistoryViaState(int oldState, int newState) {
3025 if ((oldState == CallState.DIALING || oldState == CallState.RINGING)
3026 && newState == CallState.ACTIVE) {
3027 mVideoStateHistory = mVideoState;
3028 }
3029
3030 mVideoStateHistory |= mVideoState;
3031 }
3032
Kazuya Ohshiro0063a192017-09-29 14:52:17 +09003033 /**
3034 * Returns whether or not high definition audio was used.
3035 *
3036 * @return true if high definition audio was used during this call.
3037 */
3038 boolean wasHighDefAudio() {
3039 return mWasHighDefAudio;
3040 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08003041}