blob: 3372c0329b9924bffdd6d366f11d2582c31301bd [file] [log] [blame]
Eric Erfanianccca3152017-02-22 16:32:36 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.incallui.call;
18
wangqi9982f0d2017-10-11 17:46:07 -070019import android.Manifest.permission;
wangqida410d32018-03-06 16:51:38 -080020import android.annotation.SuppressLint;
wangqi8d407a02018-02-15 15:32:52 -080021import android.annotation.TargetApi;
Eric Erfanianccca3152017-02-22 16:32:36 -080022import android.content.Context;
23import android.hardware.camera2.CameraCharacteristics;
24import android.net.Uri;
erfanian2cf2c342017-12-21 12:01:33 -080025import android.os.Build;
Eric Erfanianccca3152017-02-22 16:32:36 -080026import android.os.Build.VERSION;
27import android.os.Build.VERSION_CODES;
28import android.os.Bundle;
wangqida410d32018-03-06 16:51:38 -080029import android.os.PersistableBundle;
Eric Erfanianccca3152017-02-22 16:32:36 -080030import android.os.Trace;
31import android.support.annotation.IntDef;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070032import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080033import android.support.annotation.Nullable;
twyendde01c52017-09-22 10:07:31 -070034import android.support.annotation.VisibleForTesting;
wangqif4ba3452018-01-09 11:26:29 -080035import android.support.v4.os.BuildCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import android.telecom.Call;
37import android.telecom.Call.Details;
wangqi219b8702018-02-13 09:34:41 -080038import android.telecom.Call.RttCall;
Eric Erfanian8369df02017-05-03 10:27:13 -070039import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080040import android.telecom.Connection;
41import android.telecom.DisconnectCause;
42import android.telecom.GatewayInfo;
43import android.telecom.InCallService.VideoCall;
44import android.telecom.PhoneAccount;
45import android.telecom.PhoneAccountHandle;
46import android.telecom.StatusHints;
47import android.telecom.TelecomManager;
48import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080049import android.text.TextUtils;
roldenburg08424ee2018-03-28 17:02:46 -070050import android.widget.Toast;
Eric Erfanianccca3152017-02-22 16:32:36 -080051import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080052import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
erfanian2cf2c342017-12-21 12:01:33 -080053import com.android.dialer.assisteddialing.ConcreteCreator;
erfaniand0f207f2017-10-11 12:23:29 -070054import com.android.dialer.assisteddialing.TransformationInfo;
Eric Erfanian8369df02017-05-03 10:27:13 -070055import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080056import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070057import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080058import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080059import com.android.dialer.common.LogUtil;
Eric Erfanian2ca43182017-08-31 06:57:16 -070060import com.android.dialer.compat.telephony.TelephonyManagerCompat;
61import com.android.dialer.configprovider.ConfigProviderBindings;
roldenburg4f026392017-10-13 18:42:20 -070062import com.android.dialer.duo.DuoComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070063import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070064import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070065import com.android.dialer.enrichedcall.EnrichedCallManager;
66import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
67import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
68import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070069import com.android.dialer.enrichedcall.Session;
wangqi9982f0d2017-10-11 17:46:07 -070070import com.android.dialer.location.GeoUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070071import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070072import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070073import com.android.dialer.logging.DialerImpression;
74import com.android.dialer.logging.Logger;
twyen73a74c32018-03-07 12:12:24 -080075import com.android.dialer.preferredsim.PreferredAccountRecorder;
wangqied677ef2018-04-04 12:03:00 -070076import com.android.dialer.rtt.RttTranscript;
twyena4745bd2017-12-12 18:40:11 -080077import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070078import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070079import com.android.dialer.theme.R;
wangqi9982f0d2017-10-11 17:46:07 -070080import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070081import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080082import com.android.incallui.latencyreport.LatencyReport;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070083import com.android.incallui.videotech.VideoTech;
84import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070085import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070086import com.android.incallui.videotech.empty.EmptyVideoTech;
87import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070088import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080089import java.lang.annotation.Retention;
90import java.lang.annotation.RetentionPolicy;
91import java.util.ArrayList;
92import java.util.List;
93import java.util.Locale;
94import java.util.Objects;
95import java.util.UUID;
96import java.util.concurrent.CopyOnWriteArrayList;
97import java.util.concurrent.TimeUnit;
98
99/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -0700100public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -0800101
102 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
103 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
104 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700105
106 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -0700107 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700108 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
109
Eric Erfanianccca3152017-02-22 16:32:36 -0800110 private static final String ID_PREFIX = "DialerCall_";
111 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
112 "emergency_callback_window_millis";
linyuh183cb712017-12-27 17:02:37 -0800113 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800114
115 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700116 * A counter used to append to restricted/private/hidden calls so that users can identify them in
117 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
118 * are no live calls.
119 */
linyuh183cb712017-12-27 17:02:37 -0800120 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700121
122 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800123 * The unique call ID for every call. This will help us to identify each call and allow us the
124 * ability to stitch impressions to calls if needed.
125 */
126 private final String uniqueCallId = UUID.randomUUID().toString();
127
linyuh183cb712017-12-27 17:02:37 -0800128 private final Call telecomCall;
129 private final LatencyReport latencyReport;
130 private final String id;
131 private final int hiddenId;
132 private final List<String> childCallIds = new ArrayList<>();
133 private final LogState logState = new LogState();
134 private final Context context;
135 private final DialerCallDelegate dialerCallDelegate;
136 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
137 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800138 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800139 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800140
erfaniand05d8992018-03-20 19:42:26 -0700141 private boolean isSpeakEasyCall;
linyuh183cb712017-12-27 17:02:37 -0800142 private boolean isEmergencyCall;
143 private Uri handle;
144 private int state = State.INVALID;
145 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800146
roldenburg08424ee2018-03-28 17:02:46 -0700147 private boolean hasShownLteToWiFiHandoverToast;
Eric Erfanianccca3152017-02-22 16:32:36 -0800148 private boolean hasShownWiFiToLteHandoverToast;
149 private boolean doNotShowDialogForHandoffToWifiFailure;
150
linyuh183cb712017-12-27 17:02:37 -0800151 private String childNumber;
152 private String lastForwardedNumber;
wangqif4ba3452018-01-09 11:26:29 -0800153 private boolean isCallForwarded;
linyuh183cb712017-12-27 17:02:37 -0800154 private String callSubject;
155 private PhoneAccountHandle phoneAccountHandle;
156 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
157 private boolean isSpam;
158 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700159
160 @Nullable private Boolean isInUserSpamList;
161
162 @Nullable private Boolean isInUserWhiteList;
163
164 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800165 private boolean didShowCameraPermission;
wangqida410d32018-03-06 16:51:38 -0800166 private boolean didDismissVideoChargesAlertDialog;
167 private PersistableBundle carrierConfig;
Eric Erfanianccca3152017-02-22 16:32:36 -0800168 private String callProviderLabel;
169 private String callbackNumber;
linyuh183cb712017-12-27 17:02:37 -0800170 private int cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
171 private EnrichedCallCapabilities enrichedCallCapabilities;
172 private Session enrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800173
Eric Erfanian2ca43182017-08-31 06:57:16 -0700174 private int answerAndReleaseButtonDisplayedTimes = 0;
175 private boolean releasedByAnsweringSecondCall = false;
176 // Times when a second call is received but AnswerAndRelease button is not shown
177 // since it's not supported.
178 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700179 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700180
wangqi4d705e52017-09-28 12:23:35 -0700181 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
182 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700183 private boolean isVoicemailNumber;
184 private List<PhoneAccountHandle> callCapableAccounts;
185 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700186
Android Dialer974fc292018-02-01 16:12:25 -0800187 private volatile boolean feedbackRequested = false;
188
twyen73a74c32018-03-07 12:12:24 -0800189 @Nullable private PreferredAccountRecorder preferredAccountRecorder;
190
Eric Erfanianccca3152017-02-22 16:32:36 -0800191 public static String getNumberFromHandle(Uri handle) {
192 return handle == null ? "" : handle.getSchemeSpecificPart();
193 }
194
195 /**
196 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700197 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800198 */
199 private boolean isRemotelyHeld;
200
Eric Erfanian2ca43182017-08-31 06:57:16 -0700201 /** Indicates whether this call is currently in the process of being merged into a conference. */
202 private boolean isMergeInProcess;
203
Eric Erfanianccca3152017-02-22 16:32:36 -0800204 /**
205 * Indicates whether the phone account associated with this call supports specifying a call
206 * subject.
207 */
linyuh183cb712017-12-27 17:02:37 -0800208 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800209
wangqied677ef2018-04-04 12:03:00 -0700210 public RttTranscript getRttTranscript() {
211 return rttTranscript;
212 }
213
214 public void setRttTranscript(RttTranscript rttTranscript) {
215 this.rttTranscript = rttTranscript;
216 }
217
218 private RttTranscript rttTranscript;
219
linyuh183cb712017-12-27 17:02:37 -0800220 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800221 new Call.Callback() {
222 @Override
223 public void onStateChanged(Call call, int newState) {
224 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
225 update();
226 }
227
228 @Override
229 public void onParentChanged(Call call, Call newParent) {
230 LogUtil.v(
231 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
232 update();
233 }
234
235 @Override
236 public void onChildrenChanged(Call call, List<Call> children) {
237 update();
238 }
239
240 @Override
241 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700242 LogUtil.v(
243 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800244 update();
245 }
246
247 @Override
248 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
249 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700250 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800251 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800252 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800253 listener.onCannedTextResponsesLoaded(DialerCall.this);
254 }
255 }
256
257 @Override
258 public void onPostDialWait(Call call, String remainingPostDialSequence) {
259 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700260 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800261 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
262 update();
263 }
264
265 @Override
266 public void onVideoCallChanged(Call call, VideoCall videoCall) {
267 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700268 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800269 update();
270 }
271
272 @Override
273 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700274 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700275 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800276 }
277
278 @Override
279 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
280 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700281 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800282 "call %s, conferenceable calls: %d",
283 call,
284 conferenceableCalls.size());
285 update();
286 }
287
288 @Override
wangqi219b8702018-02-13 09:34:41 -0800289 public void onRttModeChanged(Call call, int mode) {
290 LogUtil.v("TelecomCallCallback.onRttModeChanged", "mode=%d", mode);
291 }
292
293 @Override
294 public void onRttRequest(Call call, int id) {
295 LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id);
wangqibc28ea72018-04-02 16:23:00 -0700296 for (DialerCallListener listener : listeners) {
297 listener.onDialerCallUpgradeToRtt(id);
298 }
wangqi219b8702018-02-13 09:34:41 -0800299 }
300
301 @Override
302 public void onRttInitiationFailure(Call call, int reason) {
303 LogUtil.v("TelecomCallCallback.onRttInitiationFailure", "reason=%d", reason);
304 update();
305 }
306
307 @Override
308 public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {
309 LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled);
310 update();
311 }
312
313 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800314 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
315 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700316 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800317 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
318 switch (event) {
319 // The Previous attempt to Merge two calls together has failed in Telecom. We must
320 // now update the UI to possibly re-enable the Merge button based on the number of
321 // currently conferenceable calls available or Connection Capabilities.
322 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
323 update();
324 break;
325 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
326 notifyWiFiToLteHandover();
327 break;
roldenburg08424ee2018-03-28 17:02:46 -0700328 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI:
329 onLteToWifiHandover();
330 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800331 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
332 notifyHandoverToWifiFailed();
333 break;
334 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
335 isRemotelyHeld = true;
336 update();
337 break;
338 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
339 isRemotelyHeld = false;
340 update();
341 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700342 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
343 notifyInternationalCallOnWifi();
344 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700345 case TelephonyManagerCompat.EVENT_MERGE_START:
346 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
347 isMergeInProcess = true;
348 break;
349 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
350 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
351 isMergeInProcess = false;
352 break;
wangqif4ba3452018-01-09 11:26:29 -0800353 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
354 // Only handle this event for P+ since it's unreliable pre-P.
355 if (BuildCompat.isAtLeastP()) {
356 isCallForwarded = true;
357 update();
358 }
359 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800360 default:
361 break;
362 }
363 }
364 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700365
linyuh183cb712017-12-27 17:02:37 -0800366 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800367
368 public DialerCall(
369 Context context,
370 DialerCallDelegate dialerCallDelegate,
371 Call telecomCall,
372 LatencyReport latencyReport,
373 boolean registerCallback) {
374 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800375 this.context = context;
376 this.dialerCallDelegate = dialerCallDelegate;
377 this.telecomCall = telecomCall;
378 this.latencyReport = latencyReport;
379 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800380
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700381 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800382 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700383
384 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700385 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800386 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700387 } else {
linyuh183cb712017-12-27 17:02:37 -0800388 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700389 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800390
391 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800392 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800393 }
394
linyuh183cb712017-12-27 17:02:37 -0800395 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800396 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700397
398 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800399 }
400
twyendde01c52017-09-22 10:07:31 -0700401 /** Test only constructor to avoid initializing dependencies. */
402 @VisibleForTesting
403 DialerCall(Context context) {
linyuh183cb712017-12-27 17:02:37 -0800404 this.context = context;
405 telecomCall = null;
406 latencyReport = null;
407 id = null;
408 hiddenId = 0;
409 dialerCallDelegate = null;
410 videoTechManager = null;
twyendde01c52017-09-22 10:07:31 -0700411 }
412
Eric Erfanianccca3152017-02-22 16:32:36 -0800413 private static int translateState(int state) {
414 switch (state) {
415 case Call.STATE_NEW:
416 case Call.STATE_CONNECTING:
417 return DialerCall.State.CONNECTING;
418 case Call.STATE_SELECT_PHONE_ACCOUNT:
419 return DialerCall.State.SELECT_PHONE_ACCOUNT;
420 case Call.STATE_DIALING:
421 return DialerCall.State.DIALING;
422 case Call.STATE_PULLING_CALL:
423 return DialerCall.State.PULLING;
424 case Call.STATE_RINGING:
425 return DialerCall.State.INCOMING;
426 case Call.STATE_ACTIVE:
427 return DialerCall.State.ACTIVE;
428 case Call.STATE_HOLDING:
429 return DialerCall.State.ONHOLD;
430 case Call.STATE_DISCONNECTED:
431 return DialerCall.State.DISCONNECTED;
432 case Call.STATE_DISCONNECTING:
433 return DialerCall.State.DISCONNECTING;
434 default:
435 return DialerCall.State.INVALID;
436 }
437 }
438
439 public static boolean areSame(DialerCall call1, DialerCall call2) {
440 if (call1 == null && call2 == null) {
441 return true;
442 } else if (call1 == null || call2 == null) {
443 return false;
444 }
445
446 // otherwise compare call Ids
447 return call1.getId().equals(call2.getId());
448 }
449
Eric Erfanianccca3152017-02-22 16:32:36 -0800450 public void addListener(DialerCallListener listener) {
451 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800452 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800453 }
454
455 public void removeListener(DialerCallListener listener) {
456 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800457 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800458 }
459
460 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
461 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800462 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800463 }
464
465 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
466 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800467 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800468 }
469
roldenburg08424ee2018-03-28 17:02:46 -0700470 private void onLteToWifiHandover() {
471 LogUtil.enterBlock("DialerCall.onLteToWifiHandover");
472 if (hasShownLteToWiFiHandoverToast) {
473 return;
474 }
475
476 Toast.makeText(context, R.string.video_call_lte_to_wifi_handover_toast, Toast.LENGTH_LONG)
477 .show();
478 hasShownLteToWiFiHandoverToast = true;
479 }
480
Eric Erfanianccca3152017-02-22 16:32:36 -0800481 public void notifyWiFiToLteHandover() {
482 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800483 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800484 listener.onWiFiToLteHandover();
485 }
486 }
487
488 public void notifyHandoverToWifiFailed() {
489 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800490 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800491 listener.onHandoverToWifiFailure();
492 }
493 }
494
Eric Erfanianc857f902017-05-15 14:05:33 -0700495 public void notifyInternationalCallOnWifi() {
496 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800497 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700498 dialerCallListener.onInternationalCallOnWifi();
499 }
500 }
501
Eric Erfanianccca3152017-02-22 16:32:36 -0800502 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800503 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800504 }
wangqida410d32018-03-06 16:51:38 -0800505
Eric Erfanianccca3152017-02-22 16:32:36 -0800506 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800507 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800508 }
509
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700510 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800511 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700512 }
513
514 public void setCameraDir(int cameraDir) {
515 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
516 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800517 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700518 } else {
linyuh183cb712017-12-27 17:02:37 -0800519 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700520 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800521 }
522
Eric Erfanian2ca43182017-08-31 06:57:16 -0700523 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800524 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700525 }
526
wangqi9982f0d2017-10-11 17:46:07 -0700527 public boolean isVoiceMailNumber() {
528 return isVoicemailNumber;
529 }
530
531 public List<PhoneAccountHandle> getCallCapableAccounts() {
532 return callCapableAccounts;
533 }
534
535 public String getCountryIso() {
536 return countryIso;
537 }
538
539 private void updateIsVoiceMailNumber() {
540 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
541 isVoicemailNumber = true;
roldenburg37a969d2018-02-22 14:46:44 -0800542 return;
wangqi9982f0d2017-10-11 17:46:07 -0700543 }
544
linyuh183cb712017-12-27 17:02:37 -0800545 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700546 isVoicemailNumber = false;
roldenburg37a969d2018-02-22 14:46:44 -0800547 return;
wangqi9982f0d2017-10-11 17:46:07 -0700548 }
549
linyuh183cb712017-12-27 17:02:37 -0800550 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700551 }
552
Eric Erfanianccca3152017-02-22 16:32:36 -0800553 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700554 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800555 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700556 // Clear any cache here that could potentially change on update.
557 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800558 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700559 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800560 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800561 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800562 listener.onDialerCallDisconnect();
563 }
linyuh183cb712017-12-27 17:02:37 -0800564 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700565 .getEnrichedCallManager()
566 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800567 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700568 .getEnrichedCallManager()
569 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800570 } else {
linyuh183cb712017-12-27 17:02:37 -0800571 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800572 listener.onDialerCallUpdate();
573 }
574 }
575 Trace.endSection();
576 }
577
wangqi9982f0d2017-10-11 17:46:07 -0700578 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700579 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700580 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800581 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700582
roldenburg6bd612f2018-01-18 12:57:19 -0800583 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700584
linyuh183cb712017-12-27 17:02:37 -0800585 final int translatedState = translateState(telecomCall.getState());
586 if (state != State.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800588 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800589 }
590
linyuh183cb712017-12-27 17:02:37 -0800591 childCallIds.clear();
592 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800593 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800594 childCallIds.add(
595 dialerCallDelegate
596 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800597 .getId());
598 }
599
600 // The number of conferenced calls can change over the course of the call, so use the
601 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800602 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800603
linyuh183cb712017-12-27 17:02:37 -0800604 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800605
606 // If the handle of the call has changed, update state for the call determining if it is an
607 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800608 Uri newHandle = telecomCall.getDetails().getHandle();
609 if (!Objects.equals(handle, newHandle)) {
610 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800611 updateEmergencyCallState();
612 }
613
linyuh183cb712017-12-27 17:02:37 -0800614 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800615 // If the phone account handle of the call is set, cache capability bit indicating whether
616 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800617 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
618 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
619 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800620
linyuh183cb712017-12-27 17:02:37 -0800621 if (phoneAccountHandle != null) {
622 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800623 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800624 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800625 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
wangqida410d32018-03-06 16:51:38 -0800626 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
627 cacheCarrierConfiguration(phoneAccountHandle);
628 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800629 }
630 }
631 }
linyuh183cb712017-12-27 17:02:37 -0800632 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700633 updateIsVoiceMailNumber();
634 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800635 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700636 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700637 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800638 }
639
640 /**
wangqida410d32018-03-06 16:51:38 -0800641 * Caches frequently used carrier configuration locally.
642 *
643 * @param accountHandle The PhoneAccount handle.
644 */
645 @SuppressLint("MissingPermission")
646 private void cacheCarrierConfiguration(PhoneAccountHandle accountHandle) {
647 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
648 return;
649 }
650 if (VERSION.SDK_INT < VERSION_CODES.O) {
651 return;
652 }
653 // TODO(a bug): This may take several seconds to complete, revisit it to move it to worker
654 // thread.
655 carrierConfig =
656 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, accountHandle)
657 .getCarrierConfig();
658 }
659
660 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800661 * Tests corruption of the {@code callExtras} bundle by calling {@link
662 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
663 * be thrown and caught by this function.
664 *
665 * @param callExtras the bundle to verify
666 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
667 */
668 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
669 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700670 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800671 * bundle, resulting in a IllegalArgumentException while validating data under {@link
672 * Bundle#containsKey(String)}.
673 */
674 try {
675 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
676 return false;
677 } catch (IllegalArgumentException e) {
678 LogUtil.e(
679 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
680 return true;
681 }
682 }
683
684 protected void updateFromCallExtras(Bundle callExtras) {
685 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
686 /**
687 * If the bundle is corrupted, abandon information update as a work around. These are not
688 * critical for the dialer to function.
689 */
690 return;
691 }
692 // Check for a change in the child address and notify any listeners.
693 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
694 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800695 if (!Objects.equals(childNumber, this.childNumber)) {
696 this.childNumber = childNumber;
697 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800698 listener.onDialerCallChildNumberChange();
699 }
700 }
701 }
702
703 // Last forwarded number comes in as an array of strings. We want to choose the
704 // last item in the array. The forwarding numbers arrive independently of when the
705 // call is originally set up, so we need to notify the the UI of the change.
706 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
707 ArrayList<String> lastForwardedNumbers =
708 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
709
710 if (lastForwardedNumbers != null) {
711 String lastForwardedNumber = null;
712 if (!lastForwardedNumbers.isEmpty()) {
713 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
714 }
715
linyuh183cb712017-12-27 17:02:37 -0800716 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
717 this.lastForwardedNumber = lastForwardedNumber;
718 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800719 listener.onDialerCallLastForwardedNumberChange();
720 }
721 }
722 }
723 }
724
725 // DialerCall subject is present in the extras at the start of call, so we do not need to
726 // notify any other listeners of this.
727 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
728 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800729 if (!Objects.equals(this.callSubject, callSubject)) {
730 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800731 }
732 }
733 }
734
Eric Erfanianccca3152017-02-22 16:32:36 -0800735 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800736 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800737 }
738
Eric Erfanianc857f902017-05-15 14:05:33 -0700739 /**
740 * @return name appended with a number if the number is restricted/unknown and the user has
741 * received more than one restricted/unknown call.
742 */
743 @Nullable
744 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800745 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
746 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700747 }
748 return name;
749 }
750
751 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800752 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700753 }
754
755 private boolean isHiddenNumber() {
756 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
757 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
758 }
759
Eric Erfanianccca3152017-02-22 16:32:36 -0800760 public boolean hasShownWiFiToLteHandoverToast() {
761 return hasShownWiFiToLteHandoverToast;
762 }
763
764 public void setHasShownWiFiToLteHandoverToast() {
765 hasShownWiFiToLteHandoverToast = true;
766 }
767
768 public boolean showWifiHandoverAlertAsToast() {
769 return doNotShowDialogForHandoffToWifiFailure;
770 }
771
772 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
773 doNotShowDialogForHandoffToWifiFailure = bool;
774 }
775
wangqida410d32018-03-06 16:51:38 -0800776 public boolean showVideoChargesAlertDialog() {
777 if (carrierConfig == null) {
778 return false;
779 }
780 return carrierConfig.getBoolean(
781 TelephonyManagerCompat.CARRIER_CONFIG_KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL);
782 }
783
Eric Erfanianccca3152017-02-22 16:32:36 -0800784 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800785 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800786 }
787
788 @Nullable
789 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800790 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800791 }
792
793 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800794 telecomCall.reject(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800795 setState(State.BLOCKED);
796 }
797
798 @Nullable
799 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800800 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800801 }
802
803 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800804 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800805 }
806
807 public boolean isPotentialEmergencyCallback() {
808 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
809 // is actually in emergency callback mode (ie data is disabled).
810 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
811 return true;
812 }
813 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
814 // as a potential emergency callback.
815 if (getExtras() != null
816 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
817 > 0) {
818 long lastEmergencyCallMillis =
819 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
820 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
821 return true;
822 }
823 }
824 return false;
825 }
826
827 boolean isInEmergencyCallbackWindow(long timestampMillis) {
828 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800829 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800830 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
831 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
832 }
833
834 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800835 if (telecomCall != null && telecomCall.getParent() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800836 return State.CONFERENCED;
837 } else {
linyuh183cb712017-12-27 17:02:37 -0800838 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800839 }
840 }
841
yueg265089a2017-10-06 14:35:15 -0700842 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800843 return state;
yueg265089a2017-10-06 14:35:15 -0700844 }
845
Eric Erfanianccca3152017-02-22 16:32:36 -0800846 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700847 if (state == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800848 logState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700849 } else if (state == State.DISCONNECTED) {
850 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800851 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
linyuh183cb712017-12-27 17:02:37 -0800852 if (this.state != state) {
853 logState.duration = newDuration;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700854 } else {
855 LogUtil.i(
856 "DialerCall.setState",
857 "ignoring state transition from DISCONNECTED to DISCONNECTED."
858 + " Duration would have changed from %s to %s",
linyuh183cb712017-12-27 17:02:37 -0800859 logState.duration,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700860 newDuration);
861 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800862 }
linyuh183cb712017-12-27 17:02:37 -0800863 this.state = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800864 }
865
866 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800867 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800868 }
869
870 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800871 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800872 }
873
874 @Nullable
875 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800876 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800877 }
878
879 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800880 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800881 }
882
883 @Nullable
884 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800885 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800886 }
887
888 /** @return The child number for the call, or {@code null} if none specified. */
889 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800890 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800891 }
892
893 /** @return The last forwarded number for the call, or {@code null} if none specified. */
894 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800895 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800896 }
897
wangqif4ba3452018-01-09 11:26:29 -0800898 public boolean isCallForwarded() {
899 return isCallForwarded;
900 }
901
Eric Erfanianccca3152017-02-22 16:32:36 -0800902 /** @return The call subject, or {@code null} if none specified. */
903 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800904 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800905 }
906
907 /**
908 * @return {@code true} if the call's phone account supports call subjects, {@code false}
909 * otherwise.
910 */
911 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800912 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800913 }
914
915 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
916 public DisconnectCause getDisconnectCause() {
linyuh183cb712017-12-27 17:02:37 -0800917 if (state == State.DISCONNECTED || state == State.IDLE) {
918 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800919 }
920
921 return new DisconnectCause(DisconnectCause.UNKNOWN);
922 }
923
924 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800925 this.disconnectCause = disconnectCause;
926 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800927 }
928
929 /** Returns the possible text message responses. */
930 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800931 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800932 }
933
934 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
935 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800936 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800937
938 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
939 // We allow you to merge if the capabilities allow it or if it is a call with
940 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800941 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800942 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
943 // Cannot merge calls if there are no calls to merge with.
944 return false;
945 }
946 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
947 }
948 return (capabilities == (capabilities & supportedCapabilities));
949 }
950
951 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800952 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800953 }
954
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700955 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800956 public String getUniqueCallId() {
957 return uniqueCallId;
958 }
959
960 /** Gets the time when the call first became active. */
961 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800962 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800963 }
964
wangqied677ef2018-04-04 12:03:00 -0700965 /**
966 * Gets the time when the call is created (see {@link Details#getCreationTimeMillis()}). This is
967 * the same time that is logged as the start time in the Call Log (see {@link
968 * android.provider.CallLog.Calls#DATE}).
969 */
970 @TargetApi(26)
971 public long getCreationTimeMillis() {
972 return telecomCall.getDetails().getCreationTimeMillis();
973 }
974
Eric Erfanianccca3152017-02-22 16:32:36 -0800975 public boolean isConferenceCall() {
976 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
977 }
978
979 @Nullable
980 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -0800981 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -0800982 }
983
984 @Nullable
985 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -0800986 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800987 }
988
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700989 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -0800990 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -0800991 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800992 }
993
994 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -0800995 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -0800996 }
997
998 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -0800999 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -08001000 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -08001001 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001002 }
1003 return null;
1004 }
1005
1006 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -08001007 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -08001008 }
1009
1010 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -07001011 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001012 }
1013
wangqi8d407a02018-02-15 15:32:52 -08001014 @TargetApi(28)
wangqif6be6172018-03-30 15:57:56 -07001015 public boolean isActiveRttCall() {
wangqi219b8702018-02-13 09:34:41 -08001016 if (BuildCompat.isAtLeastP()) {
1017 return getTelecomCall().isRttActive();
1018 } else {
1019 return false;
1020 }
1021 }
1022
wangqi153af2f2018-02-15 16:21:49 -08001023 @TargetApi(28)
1024 public RttCall getRttCall() {
wangqif6be6172018-03-30 15:57:56 -07001025 if (!isActiveRttCall()) {
wangqi153af2f2018-02-15 16:21:49 -08001026 return null;
1027 }
1028 return getTelecomCall().getRttCall();
1029 }
1030
wangqif6be6172018-03-30 15:57:56 -07001031 @TargetApi(28)
1032 public boolean canUpgradeToRttCall() {
1033 PhoneAccount phoneAccount = getPhoneAccount();
1034 if (phoneAccount == null) {
1035 return false;
1036 }
1037 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
1038 return false;
1039 }
1040 if (isActiveRttCall()) {
1041 return false;
1042 }
1043 if (isVideoCall()) {
1044 return false;
1045 }
1046 if (isConferenceCall()) {
1047 return false;
1048 }
1049 if (CallList.getInstance().hasActiveRttCall()) {
1050 return false;
1051 }
1052 return true;
1053 }
1054
1055 @TargetApi(28)
1056 public void sendRttUpgradeRequest() {
1057 getTelecomCall().sendRttRequest();
1058 }
1059
wangqibc28ea72018-04-02 16:23:00 -07001060 @TargetApi(28)
1061 public void respondToRttRequest(boolean accept, int rttRequestId) {
1062 getTelecomCall().respondToRttRequest(rttRequestId, accept);
1063 }
1064
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001065 public boolean hasReceivedVideoUpgradeRequest() {
1066 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
1067 }
1068
1069 public boolean hasSentVideoUpgradeRequest() {
1070 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -08001071 }
1072
wangqi219b8702018-02-13 09:34:41 -08001073 public boolean hasSentRttUpgradeRequest() {
wangqi219b8702018-02-13 09:34:41 -08001074 return false;
1075 }
1076
Eric Erfanianccca3152017-02-22 16:32:36 -08001077 /**
1078 * Determines if the call handle is an emergency number or not and caches the result to avoid
1079 * repeated calls to isEmergencyNumber.
1080 */
1081 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -08001082 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -08001083 }
1084
Eric Erfanianccca3152017-02-22 16:32:36 -08001085 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -08001086 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -08001087 }
1088
1089 /**
1090 * Determines if the call is an external call.
1091 *
1092 * <p>An external call is one which does not exist locally for the {@link
1093 * android.telecom.ConnectionService} it is associated with.
1094 *
Eric Erfanianccca3152017-02-22 16:32:36 -08001095 * @return {@code true} if the call is an external call, {@code false} otherwise.
1096 */
linyuh437ae952018-03-26 12:46:18 -07001097 boolean isExternalCall() {
1098 return hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
Eric Erfanianccca3152017-02-22 16:32:36 -08001099 }
1100
1101 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001102 * Determines if answering this call will cause an ongoing video call to be dropped.
1103 *
1104 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
1105 * otherwise.
1106 */
1107 public boolean answeringDisconnectsForegroundVideoCall() {
1108 Bundle extras = getExtras();
1109 if (extras == null
1110 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
1111 return false;
1112 }
1113 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
1114 }
1115
1116 private void parseCallSpecificAppData() {
1117 if (isExternalCall()) {
1118 return;
1119 }
1120
linyuh183cb712017-12-27 17:02:37 -08001121 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
1122 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001123
linyuh183cb712017-12-27 17:02:37 -08001124 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -07001125 CallSpecificAppData.newBuilder()
1126 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
1127 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001128 }
1129 if (getState() == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -08001130 logState.callSpecificAppData =
1131 logState
Eric Erfanian8369df02017-05-03 10:27:13 -07001132 .callSpecificAppData
1133 .toBuilder()
1134 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
1135 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001136 }
1137 }
1138
1139 @Override
1140 public String toString() {
linyuh183cb712017-12-27 17:02:37 -08001141 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001142 // This should happen only in testing since otherwise we would never have a null
1143 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -08001144 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -08001145 }
1146
1147 return String.format(
1148 Locale.US,
1149 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001150 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001151 id,
Eric Erfanianccca3152017-02-22 16:32:36 -08001152 State.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001153 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1154 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1155 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001156 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001157 this.telecomCall.getConferenceableCalls(),
1158 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001159 getVideoTech().getSessionModificationState(),
1160 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001161 }
1162
1163 public String toSimpleString() {
1164 return super.toString();
1165 }
1166
1167 @CallHistoryStatus
1168 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001169 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001170 }
1171
1172 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001173 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001174 }
1175
1176 public boolean didShowCameraPermission() {
1177 return didShowCameraPermission;
1178 }
1179
1180 public void setDidShowCameraPermission(boolean didShow) {
1181 didShowCameraPermission = didShow;
1182 }
1183
wangqida410d32018-03-06 16:51:38 -08001184 public boolean didDismissVideoChargesAlertDialog() {
1185 return didDismissVideoChargesAlertDialog;
1186 }
1187
1188 public void setDidDismissVideoChargesAlertDialog(boolean didDismiss) {
1189 didDismissVideoChargesAlertDialog = didDismiss;
1190 }
1191
Eric Erfanian938468d2017-10-24 14:05:52 -07001192 @Nullable
1193 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001194 return isInGlobalSpamList;
1195 }
1196
1197 public void setIsInGlobalSpamList(boolean inSpamList) {
1198 isInGlobalSpamList = inSpamList;
1199 }
1200
Eric Erfanian938468d2017-10-24 14:05:52 -07001201 @Nullable
1202 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001203 return isInUserSpamList;
1204 }
1205
1206 public void setIsInUserSpamList(boolean inSpamList) {
1207 isInUserSpamList = inSpamList;
1208 }
1209
Eric Erfanian938468d2017-10-24 14:05:52 -07001210 @Nullable
1211 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001212 return isInUserWhiteList;
1213 }
1214
1215 public void setIsInUserWhiteList(boolean inWhiteList) {
1216 isInUserWhiteList = inWhiteList;
1217 }
1218
1219 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001220 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001221 }
1222
1223 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001224 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001225 }
1226
1227 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001228 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001229 }
1230
1231 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001232 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001233 }
1234
1235 public boolean isRemotelyHeld() {
1236 return isRemotelyHeld;
1237 }
1238
Eric Erfanian2ca43182017-08-31 06:57:16 -07001239 public boolean isMergeInProcess() {
1240 return isMergeInProcess;
1241 }
1242
Eric Erfanianccca3152017-02-22 16:32:36 -08001243 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001244 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001245 }
1246
erfanian2cf2c342017-12-21 12:01:33 -08001247 /**
1248 * Try and determine if the call used assisted dialing.
1249 *
1250 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1251 * implmentation is complete in P+.
1252 *
1253 * @return a boolean indicating assisted dialing may have been performed
1254 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001255 public boolean isAssistedDialed() {
1256 if (getIntentExtras() != null) {
erfaniand2e5d0b2018-03-02 14:54:35 -08001257 // P and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
erfanian2cf2c342017-12-21 12:01:33 -08001258 // was used. The Dialer client is responsible for performing assisted dialing before
1259 // placing the outgoing call.
1260 //
1261 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1262 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1263 && getAssistedDialingExtras() != null
1264 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1265 return true;
1266 }
1267 }
1268
Eric Erfanian2ca43182017-08-31 06:57:16 -07001269 return false;
1270 }
1271
erfanian2cf2c342017-12-21 12:01:33 -08001272 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001273 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001274 if (getIntentExtras() == null) {
1275 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001276 }
erfanian2cf2c342017-12-21 12:01:33 -08001277
1278 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1279 return null;
1280 }
1281
erfanianf2556612018-01-23 09:55:59 -08001282 // Used in N-OMR1
erfanian2cf2c342017-12-21 12:01:33 -08001283 return TransformationInfo.newInstanceFromBundle(
1284 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001285 }
1286
Eric Erfanianccca3152017-02-22 16:32:36 -08001287 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001288 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001289 }
1290
Eric Erfanian2ca43182017-08-31 06:57:16 -07001291 public int getAnswerAndReleaseButtonDisplayedTimes() {
1292 return answerAndReleaseButtonDisplayedTimes;
1293 }
1294
1295 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1296 answerAndReleaseButtonDisplayedTimes++;
1297 }
1298
1299 public boolean getReleasedByAnsweringSecondCall() {
1300 return releasedByAnsweringSecondCall;
1301 }
1302
1303 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1304 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1305 }
1306
1307 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1308 return secondCallWithoutAnswerAndReleasedButtonTimes;
1309 }
1310
1311 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1312 secondCallWithoutAnswerAndReleasedButtonTimes++;
1313 }
1314
Eric Erfanian8369df02017-05-03 10:27:13 -07001315 @Nullable
1316 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001317 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001318 }
1319
1320 public void setEnrichedCallCapabilities(
1321 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001322 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001323 }
1324
1325 @Nullable
1326 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001327 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001328 }
1329
1330 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001331 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001332 }
1333
Eric Erfanianccca3152017-02-22 16:32:36 -08001334 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001335 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001336 }
1337
Eric Erfanianccca3152017-02-22 16:32:36 -08001338 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1339 LogUtil.i(
1340 "DialerCall.phoneAccountSelected",
1341 "accountHandle: %s, setDefault: %b",
1342 accountHandle,
1343 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001344 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001345 }
1346
1347 public void disconnect() {
1348 LogUtil.i("DialerCall.disconnect", "");
1349 setState(DialerCall.State.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001350 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001351 listener.onDialerCallUpdate();
1352 }
linyuh183cb712017-12-27 17:02:37 -08001353 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001354 }
1355
1356 public void hold() {
1357 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001358 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001359 }
1360
1361 public void unhold() {
1362 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001363 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001364 }
1365
1366 public void splitFromConference() {
1367 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001368 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001369 }
1370
1371 public void answer(int videoState) {
1372 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001373 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001374 }
1375
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001376 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001377 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001378 }
1379
Eric Erfanianccca3152017-02-22 16:32:36 -08001380 public void reject(boolean rejectWithMessage, String message) {
1381 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001382 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001383 }
1384
1385 /** Return the string label to represent the call provider */
1386 public String getCallProviderLabel() {
1387 if (callProviderLabel == null) {
1388 PhoneAccount account = getPhoneAccount();
1389 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001390 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001391 callProviderLabel = account.getLabel().toString();
1392 }
1393 }
1394 if (callProviderLabel == null) {
1395 callProviderLabel = "";
1396 }
1397 }
1398 return callProviderLabel;
1399 }
1400
1401 private PhoneAccount getPhoneAccount() {
1402 PhoneAccountHandle accountHandle = getAccountHandle();
1403 if (accountHandle == null) {
1404 return null;
1405 }
linyuh183cb712017-12-27 17:02:37 -08001406 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001407 }
1408
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001409 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001410 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001411 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001412
1413 // Only store the first video tech type found to be available during the life of the call.
1414 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1415 // Update the video tech.
1416 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1417 }
1418 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001419 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001420 }
1421
Eric Erfanianccca3152017-02-22 16:32:36 -08001422 public String getCallbackNumber() {
1423 if (callbackNumber == null) {
1424 // Show the emergency callback number if either:
1425 // 1. This is an emergency call.
1426 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1427 // number.
1428 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1429
1430 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001431 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001432 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001433 }
1434
Eric Erfanianccca3152017-02-22 16:32:36 -08001435 if (callbackNumber == null) {
1436 callbackNumber = "";
1437 }
1438 }
1439 return callbackNumber;
1440 }
1441
wangqi97539352017-09-25 11:15:16 -07001442 public String getSimCountryIso() {
1443 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001444 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001445 .getSimCountryIso();
1446 if (!TextUtils.isEmpty(simCountryIso)) {
1447 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1448 }
1449 return simCountryIso;
1450 }
1451
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001452 @Override
1453 public void onVideoTechStateChanged() {
1454 update();
1455 }
1456
1457 @Override
1458 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001459 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001460 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001461 listener.onDialerCallSessionModificationStateChange();
1462 }
wangqi9982f0d2017-10-11 17:46:07 -07001463 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001464 }
1465
1466 @Override
1467 public void onCameraDimensionsChanged(int width, int height) {
1468 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1469 }
1470
1471 @Override
1472 public void onPeerDimensionsChanged(int width, int height) {
1473 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1474 }
1475
1476 @Override
1477 public void onVideoUpgradeRequestReceived() {
1478 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1479
linyuh183cb712017-12-27 17:02:37 -08001480 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001481 listener.onDialerCallUpgradeToVideo();
1482 }
1483
1484 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001485
linyuh183cb712017-12-27 17:02:37 -08001486 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001487 .logCallImpression(
1488 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1489 }
1490
1491 @Override
1492 public void onUpgradedToVideo(boolean switchToSpeaker) {
1493 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1494
1495 if (!switchToSpeaker) {
1496 return;
1497 }
1498
1499 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1500
1501 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1502 LogUtil.e(
1503 "DialerCall.onUpgradedToVideo",
1504 "toggling speakerphone not allowed when bluetooth supported.");
1505 return;
1506 }
1507
1508 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1509 return;
1510 }
1511
1512 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001513 }
1514
Eric Erfanian2ca43182017-08-31 06:57:16 -07001515 @Override
1516 public void onCapabilitiesUpdated() {
1517 if (getNumber() == null) {
1518 return;
1519 }
1520 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001521 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001522 if (capabilities != null) {
1523 setEnrichedCallCapabilities(capabilities);
1524 update();
1525 }
1526 }
1527
1528 @Override
1529 public void onEnrichedCallStateChanged() {
1530 updateEnrichedCallSession();
1531 }
1532
1533 @Override
1534 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001535 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001536 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1537 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001538 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001539 .logCallImpression(
1540 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1541 getUniqueCallId(),
1542 getTimeAddedMs());
1543 }
1544 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001545 }
1546
1547 private void updateEnrichedCallSession() {
1548 if (getNumber() == null) {
1549 return;
1550 }
1551 if (getEnrichedCallSession() != null) {
1552 // State changes to existing sessions are currently handled by the UI components (which have
1553 // their own listeners). Someday instead we could remove those and just call update() here and
1554 // have the usual onDialerCallUpdate update the UI.
1555 dispatchOnEnrichedCallSessionUpdate();
1556 return;
1557 }
1558
linyuh183cb712017-12-27 17:02:37 -08001559 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001560
1561 Filter filter =
1562 isIncoming()
1563 ? manager.createIncomingCallComposerFilter()
1564 : manager.createOutgoingCallComposerFilter();
1565
1566 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1567 if (session == null) {
1568 return;
1569 }
1570
1571 session.setUniqueDialerCallId(getUniqueCallId());
1572 setEnrichedCallSession(session);
1573
1574 LogUtil.i(
1575 "DialerCall.updateEnrichedCallSession",
1576 "setting session %d's dialer id to %s",
1577 session.getSessionId(),
1578 getUniqueCallId());
1579
1580 dispatchOnEnrichedCallSessionUpdate();
1581 }
1582
1583 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001584 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001585 listener.onEnrichedCallSessionUpdate();
1586 }
1587 }
1588
1589 void onRemovedFromCallList() {
1590 // Ensure we clean up when this call is removed.
linyuh183cb712017-12-27 17:02:37 -08001591 videoTechManager.dispatchRemovedFromCallList();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001592 }
1593
wangqi4d705e52017-09-28 12:23:35 -07001594 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1595 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001596 }
1597
Android Dialer974fc292018-02-01 16:12:25 -08001598 public void markFeedbackRequested() {
1599 feedbackRequested = true;
1600 }
1601
1602 public boolean isFeedbackRequested() {
1603 return feedbackRequested;
1604 }
1605
Eric Erfanianccca3152017-02-22 16:32:36 -08001606 /**
twyen73a74c32018-03-07 12:12:24 -08001607 * If the in call UI has shown the phone account selection dialog for the call, the {@link
1608 * PreferredAccountRecorder} to record the result from the dialog.
1609 */
1610 @Nullable
1611 public PreferredAccountRecorder getPreferredAccountRecorder() {
1612 return preferredAccountRecorder;
1613 }
1614
1615 public void setPreferredAccountRecorder(PreferredAccountRecorder preferredAccountRecorder) {
1616 this.preferredAccountRecorder = preferredAccountRecorder;
1617 }
1618
erfaniand05d8992018-03-20 19:42:26 -07001619 /** Indicates the call is eligible for SpeakEasy */
1620 public boolean isSpeakEasyEligible() {
1621 // TODO(erfanian): refactor key location
1622 return ConfigProviderBindings.get(context).getBoolean("speak_easy_enabled", false);
1623 }
1624
1625 /** Indicates the user has selected SpeakEasy */
1626 public boolean isSpeakEasyCall() {
1627 if (!isSpeakEasyEligible()) {
1628 return false;
1629 }
1630 return isSpeakEasyCall;
1631 }
1632
1633 /** Sets the user preference for SpeakEasy */
1634 public void setIsSpeakEasyCall(boolean isSpeakEasyCall) {
1635 this.isSpeakEasyCall = isSpeakEasyCall;
1636 }
1637
twyen73a74c32018-03-07 12:12:24 -08001638 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001639 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1640 * means there is no result.
1641 */
1642 @IntDef({
1643 CALL_HISTORY_STATUS_UNKNOWN,
1644 CALL_HISTORY_STATUS_PRESENT,
1645 CALL_HISTORY_STATUS_NOT_PRESENT
1646 })
1647 @Retention(RetentionPolicy.SOURCE)
1648 public @interface CallHistoryStatus {}
1649
1650 /* Defines different states of this call */
1651 public static class State {
1652
1653 public static final int INVALID = 0;
1654 public static final int NEW = 1; /* The call is new. */
1655 public static final int IDLE = 2; /* The call is idle. Nothing active */
1656 public static final int ACTIVE = 3; /* There is an active call */
1657 public static final int INCOMING = 4; /* A normal incoming phone call */
1658 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1659 public static final int DIALING = 6; /* An outgoing call during dial phase */
1660 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1661 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1662 public static final int DISCONNECTING = 9; /* A call is being ended. */
1663 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1664 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1665 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1666 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1667 public static final int BLOCKED = 14; /* The number was found on the block list */
1668 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001669 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001670
1671 public static boolean isConnectingOrConnected(int state) {
1672 switch (state) {
1673 case ACTIVE:
1674 case INCOMING:
1675 case CALL_WAITING:
1676 case CONNECTING:
1677 case DIALING:
1678 case PULLING:
1679 case REDIALING:
1680 case ONHOLD:
1681 case CONFERENCED:
1682 return true;
1683 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001684 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001685 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001686 }
1687
1688 public static boolean isDialing(int state) {
1689 return state == DIALING || state == PULLING || state == REDIALING;
1690 }
1691
1692 public static String toString(int state) {
1693 switch (state) {
1694 case INVALID:
1695 return "INVALID";
1696 case NEW:
1697 return "NEW";
1698 case IDLE:
1699 return "IDLE";
1700 case ACTIVE:
1701 return "ACTIVE";
1702 case INCOMING:
1703 return "INCOMING";
1704 case CALL_WAITING:
1705 return "CALL_WAITING";
1706 case DIALING:
1707 return "DIALING";
1708 case PULLING:
1709 return "PULLING";
1710 case REDIALING:
1711 return "REDIALING";
1712 case ONHOLD:
1713 return "ONHOLD";
1714 case DISCONNECTING:
1715 return "DISCONNECTING";
1716 case DISCONNECTED:
1717 return "DISCONNECTED";
1718 case CONFERENCED:
1719 return "CONFERENCED";
1720 case SELECT_PHONE_ACCOUNT:
1721 return "SELECT_PHONE_ACCOUNT";
1722 case CONNECTING:
1723 return "CONNECTING";
1724 case BLOCKED:
1725 return "BLOCKED";
1726 default:
1727 return "UNKNOWN";
1728 }
1729 }
1730 }
1731
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001732 /** Camera direction constants */
1733 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001734 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1735 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1736 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001737 }
1738
1739 /**
1740 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1741 * existing call member variables, but this duplication helps to ensure that none of these logging
1742 * variables will interface with/and affect call logic.
1743 */
1744 public static class LogState {
1745
1746 public DisconnectCause disconnectCause;
1747 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001748 public ContactLookupResult.Type contactLookupResult =
1749 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001750 public CallSpecificAppData callSpecificAppData;
1751 // If this was a conference call, the total number of calls involved in the conference.
1752 public int conferencedCalls = 0;
1753 public long duration = 0;
1754 public boolean isLogged = false;
1755
Eric Erfanian8369df02017-05-03 10:27:13 -07001756 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001757 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001758 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001759 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001760 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001761 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001762 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001763 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001764 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001765 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001766 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001767 return "Voicemail";
1768 default:
1769 return "Not found";
1770 }
1771 }
1772
1773 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1774 if (callSpecificAppData == null) {
1775 return "null";
1776 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001777 switch (callSpecificAppData.getCallInitiationType()) {
1778 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001779 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001780 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001781 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001782 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001783 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001784 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001785 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001786 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001787 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001788 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001789 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001790 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001791 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001792 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001793 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001794 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001795 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001796 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001797 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001798 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001799 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001800 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001801 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001802 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001803 return "Launcher Shortcut";
1804 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001805 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001806 }
1807 }
1808
1809 @Override
1810 public String toString() {
1811 return String.format(
1812 Locale.US,
1813 "["
1814 + "%s, " // DisconnectCause toString already describes the object type
1815 + "isIncoming: %s, "
1816 + "contactLookup: %s, "
1817 + "callInitiation: %s, "
1818 + "duration: %s"
1819 + "]",
1820 disconnectCause,
1821 isIncoming,
1822 lookupToString(contactLookupResult),
1823 initiationToString(callSpecificAppData),
1824 duration);
1825 }
1826 }
1827
roldenburgd7490db2018-01-09 13:51:29 -08001828 /** Coordinates the available VideoTech implementations for a call. */
1829 @VisibleForTesting
1830 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001831 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001832 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001833 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001834 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001835 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001836
roldenburgd7490db2018-01-09 13:51:29 -08001837 @VisibleForTesting
1838 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001839 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001840
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001841 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001842 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001843 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001844
1845 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001846 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001847
linyuh183cb712017-12-27 17:02:37 -08001848 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001849
roldenburgd7490db2018-01-09 13:51:29 -08001850 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001851 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001852 .getRcsVideoShareFactory()
1853 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001854 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001855 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001856 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001857 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001858
1859 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001860 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001861 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001862
1863 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001864 }
1865
roldenburgd7490db2018-01-09 13:51:29 -08001866 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001867 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001868 if (savedTech == emptyVideoTech) {
1869 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001870 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001871 savedTech = tech;
1872 savedTech.becomePrimary();
1873 break;
1874 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001875 }
roldenburg6bd612f2018-01-18 12:57:19 -08001876 } else if (savedTech instanceof DuoVideoTech
1877 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001878 // RCS Video Share will become available after the capability exchange which is slower than
1879 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1880 // not in the middle of any session changes, let it take over.
1881 savedTech = rcsVideoShare;
1882 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001883 }
1884
roldenburgd7490db2018-01-09 13:51:29 -08001885 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001886 }
1887
roldenburgd7490db2018-01-09 13:51:29 -08001888 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001889 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001890 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001891 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001892 }
1893 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001894
1895 void dispatchRemovedFromCallList() {
1896 for (VideoTech videoTech : videoTechs) {
1897 videoTech.onRemovedFromCallList();
1898 }
1899 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001900 }
1901
Eric Erfanianccca3152017-02-22 16:32:36 -08001902 /** Called when canned text responses have been loaded. */
1903 public interface CannedTextResponsesLoadedListener {
1904 void onCannedTextResponsesLoaded(DialerCall call);
1905 }
1906}