blob: d57de15e5b00debb3516d3453d776ff63c11c9bc [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;
wangqid6b10d52018-04-12 14:44:06 -070060import com.android.dialer.common.concurrent.DefaultFutureCallback;
Eric Erfanian2ca43182017-08-31 06:57:16 -070061import com.android.dialer.compat.telephony.TelephonyManagerCompat;
62import com.android.dialer.configprovider.ConfigProviderBindings;
roldenburg4f026392017-10-13 18:42:20 -070063import com.android.dialer.duo.DuoComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070064import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070065import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070066import com.android.dialer.enrichedcall.EnrichedCallManager;
67import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
68import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
69import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070070import com.android.dialer.enrichedcall.Session;
wangqi9982f0d2017-10-11 17:46:07 -070071import com.android.dialer.location.GeoUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070072import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070073import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070074import com.android.dialer.logging.DialerImpression;
75import com.android.dialer.logging.Logger;
twyen73a74c32018-03-07 12:12:24 -080076import com.android.dialer.preferredsim.PreferredAccountRecorder;
wangqied677ef2018-04-04 12:03:00 -070077import com.android.dialer.rtt.RttTranscript;
wangqid6b10d52018-04-12 14:44:06 -070078import com.android.dialer.rtt.RttTranscriptUtil;
twyena4745bd2017-12-12 18:40:11 -080079import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070080import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070081import com.android.dialer.theme.R;
wangqi9982f0d2017-10-11 17:46:07 -070082import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070083import com.android.incallui.audiomode.AudioModeProvider;
wangqibb94ca62018-04-27 14:34:04 -070084import com.android.incallui.call.state.DialerCallState;
Eric Erfanianccca3152017-02-22 16:32:36 -080085import com.android.incallui.latencyreport.LatencyReport;
erfaniandcc84cd2018-04-17 12:41:05 -070086import com.android.incallui.speakeasy.runtime.Constraints;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070087import com.android.incallui.videotech.VideoTech;
88import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070089import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070090import com.android.incallui.videotech.empty.EmptyVideoTech;
91import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070092import com.android.incallui.videotech.utils.VideoUtils;
wangqid6b10d52018-04-12 14:44:06 -070093import com.google.common.util.concurrent.Futures;
wangqid6b10d52018-04-12 14:44:06 -070094import com.google.common.util.concurrent.MoreExecutors;
Eric Erfanianccca3152017-02-22 16:32:36 -080095import java.lang.annotation.Retention;
96import java.lang.annotation.RetentionPolicy;
97import java.util.ArrayList;
98import java.util.List;
99import java.util.Locale;
100import java.util.Objects;
101import java.util.UUID;
102import java.util.concurrent.CopyOnWriteArrayList;
103import java.util.concurrent.TimeUnit;
104
105/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -0700106public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -0800107
108 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
109 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
110 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700111
112 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -0700113 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700114 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
115
Eric Erfanianccca3152017-02-22 16:32:36 -0800116 private static final String ID_PREFIX = "DialerCall_";
117 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
118 "emergency_callback_window_millis";
linyuh183cb712017-12-27 17:02:37 -0800119 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800120
121 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700122 * A counter used to append to restricted/private/hidden calls so that users can identify them in
123 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
124 * are no live calls.
125 */
linyuh183cb712017-12-27 17:02:37 -0800126 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700127
128 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800129 * The unique call ID for every call. This will help us to identify each call and allow us the
130 * ability to stitch impressions to calls if needed.
131 */
132 private final String uniqueCallId = UUID.randomUUID().toString();
133
linyuh183cb712017-12-27 17:02:37 -0800134 private final Call telecomCall;
135 private final LatencyReport latencyReport;
136 private final String id;
137 private final int hiddenId;
138 private final List<String> childCallIds = new ArrayList<>();
139 private final LogState logState = new LogState();
140 private final Context context;
141 private final DialerCallDelegate dialerCallDelegate;
142 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
143 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800144 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800145 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800146
erfaniand05d8992018-03-20 19:42:26 -0700147 private boolean isSpeakEasyCall;
linyuh183cb712017-12-27 17:02:37 -0800148 private boolean isEmergencyCall;
149 private Uri handle;
wangqibb94ca62018-04-27 14:34:04 -0700150 private int state = DialerCallState.INVALID;
linyuh183cb712017-12-27 17:02:37 -0800151 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800152
roldenburg08424ee2018-03-28 17:02:46 -0700153 private boolean hasShownLteToWiFiHandoverToast;
Eric Erfanianccca3152017-02-22 16:32:36 -0800154 private boolean hasShownWiFiToLteHandoverToast;
155 private boolean doNotShowDialogForHandoffToWifiFailure;
156
linyuh183cb712017-12-27 17:02:37 -0800157 private String childNumber;
158 private String lastForwardedNumber;
wangqif4ba3452018-01-09 11:26:29 -0800159 private boolean isCallForwarded;
linyuh183cb712017-12-27 17:02:37 -0800160 private String callSubject;
twyen27c37182018-04-24 11:48:12 -0700161 @Nullable private PhoneAccountHandle phoneAccountHandle;
linyuh183cb712017-12-27 17:02:37 -0800162 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
163 private boolean isSpam;
164 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700165
166 @Nullable private Boolean isInUserSpamList;
167
168 @Nullable private Boolean isInUserWhiteList;
169
170 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800171 private boolean didShowCameraPermission;
wangqida410d32018-03-06 16:51:38 -0800172 private boolean didDismissVideoChargesAlertDialog;
173 private PersistableBundle carrierConfig;
Eric Erfanianccca3152017-02-22 16:32:36 -0800174 private String callProviderLabel;
175 private String callbackNumber;
linyuh183cb712017-12-27 17:02:37 -0800176 private int cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
177 private EnrichedCallCapabilities enrichedCallCapabilities;
178 private Session enrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800179
Eric Erfanian2ca43182017-08-31 06:57:16 -0700180 private int answerAndReleaseButtonDisplayedTimes = 0;
181 private boolean releasedByAnsweringSecondCall = false;
182 // Times when a second call is received but AnswerAndRelease button is not shown
183 // since it's not supported.
184 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700185 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700186
wangqi4d705e52017-09-28 12:23:35 -0700187 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
188 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700189 private boolean isVoicemailNumber;
190 private List<PhoneAccountHandle> callCapableAccounts;
191 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700192
Android Dialer974fc292018-02-01 16:12:25 -0800193 private volatile boolean feedbackRequested = false;
194
twyen73a74c32018-03-07 12:12:24 -0800195 @Nullable private PreferredAccountRecorder preferredAccountRecorder;
wangqia0c49f62018-04-25 15:04:01 -0700196 private boolean isCallRemoved;
twyen73a74c32018-03-07 12:12:24 -0800197
Eric Erfanianccca3152017-02-22 16:32:36 -0800198 public static String getNumberFromHandle(Uri handle) {
199 return handle == null ? "" : handle.getSchemeSpecificPart();
200 }
201
202 /**
203 * Whether the call is put on hold by remote party. This is different than the {@link
wangqibb94ca62018-04-27 14:34:04 -0700204 * DialerCallState#ONHOLD} state which indicates that the call is being held locally on the
205 * device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800206 */
207 private boolean isRemotelyHeld;
208
Eric Erfanian2ca43182017-08-31 06:57:16 -0700209 /** Indicates whether this call is currently in the process of being merged into a conference. */
210 private boolean isMergeInProcess;
211
Eric Erfanianccca3152017-02-22 16:32:36 -0800212 /**
213 * Indicates whether the phone account associated with this call supports specifying a call
214 * subject.
215 */
linyuh183cb712017-12-27 17:02:37 -0800216 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800217
wangqied677ef2018-04-04 12:03:00 -0700218 public RttTranscript getRttTranscript() {
219 return rttTranscript;
220 }
221
222 public void setRttTranscript(RttTranscript rttTranscript) {
223 this.rttTranscript = rttTranscript;
224 }
225
226 private RttTranscript rttTranscript;
227
linyuh183cb712017-12-27 17:02:37 -0800228 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800229 new Call.Callback() {
230 @Override
231 public void onStateChanged(Call call, int newState) {
232 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
233 update();
234 }
235
236 @Override
237 public void onParentChanged(Call call, Call newParent) {
238 LogUtil.v(
239 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
240 update();
241 }
242
243 @Override
244 public void onChildrenChanged(Call call, List<Call> children) {
245 update();
246 }
247
248 @Override
249 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700250 LogUtil.v(
251 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800252 update();
253 }
254
255 @Override
256 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
257 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700258 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800259 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800260 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800261 listener.onCannedTextResponsesLoaded(DialerCall.this);
262 }
263 }
264
265 @Override
266 public void onPostDialWait(Call call, String remainingPostDialSequence) {
267 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700268 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800269 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
270 update();
271 }
272
273 @Override
274 public void onVideoCallChanged(Call call, VideoCall videoCall) {
275 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700276 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800277 update();
278 }
279
280 @Override
281 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700282 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700283 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800284 }
285
286 @Override
287 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
288 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700289 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800290 "call %s, conferenceable calls: %d",
291 call,
292 conferenceableCalls.size());
293 update();
294 }
295
296 @Override
wangqi219b8702018-02-13 09:34:41 -0800297 public void onRttModeChanged(Call call, int mode) {
298 LogUtil.v("TelecomCallCallback.onRttModeChanged", "mode=%d", mode);
299 }
300
301 @Override
302 public void onRttRequest(Call call, int id) {
303 LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id);
wangqibc28ea72018-04-02 16:23:00 -0700304 for (DialerCallListener listener : listeners) {
305 listener.onDialerCallUpgradeToRtt(id);
306 }
wangqi219b8702018-02-13 09:34:41 -0800307 }
308
309 @Override
310 public void onRttInitiationFailure(Call call, int reason) {
311 LogUtil.v("TelecomCallCallback.onRttInitiationFailure", "reason=%d", reason);
312 update();
313 }
314
315 @Override
316 public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {
317 LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled);
wangqi81e373e2018-04-24 10:50:57 -0700318 if (enabled) {
319 Logger.get(context)
320 .logCallImpression(
321 DialerImpression.Type.RTT_MID_CALL_ENABLED,
322 getUniqueCallId(),
323 getTimeAddedMs());
324 }
wangqi219b8702018-02-13 09:34:41 -0800325 update();
326 }
327
328 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800329 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
330 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700331 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800332 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
333 switch (event) {
334 // The Previous attempt to Merge two calls together has failed in Telecom. We must
335 // now update the UI to possibly re-enable the Merge button based on the number of
336 // currently conferenceable calls available or Connection Capabilities.
337 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
338 update();
339 break;
340 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
341 notifyWiFiToLteHandover();
342 break;
roldenburg08424ee2018-03-28 17:02:46 -0700343 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI:
344 onLteToWifiHandover();
345 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800346 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
347 notifyHandoverToWifiFailed();
348 break;
349 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
350 isRemotelyHeld = true;
351 update();
352 break;
353 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
354 isRemotelyHeld = false;
355 update();
356 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700357 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
358 notifyInternationalCallOnWifi();
359 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700360 case TelephonyManagerCompat.EVENT_MERGE_START:
361 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
362 isMergeInProcess = true;
363 break;
364 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
365 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
366 isMergeInProcess = false;
367 break;
wangqif4ba3452018-01-09 11:26:29 -0800368 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
369 // Only handle this event for P+ since it's unreliable pre-P.
370 if (BuildCompat.isAtLeastP()) {
371 isCallForwarded = true;
372 update();
373 }
374 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800375 default:
376 break;
377 }
378 }
379 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700380
linyuh183cb712017-12-27 17:02:37 -0800381 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800382
383 public DialerCall(
384 Context context,
385 DialerCallDelegate dialerCallDelegate,
386 Call telecomCall,
387 LatencyReport latencyReport,
388 boolean registerCallback) {
389 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800390 this.context = context;
391 this.dialerCallDelegate = dialerCallDelegate;
392 this.telecomCall = telecomCall;
393 this.latencyReport = latencyReport;
394 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800395
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700396 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800397 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700398
399 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700400 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800401 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700402 } else {
linyuh183cb712017-12-27 17:02:37 -0800403 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700404 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800405
406 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800407 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800408 }
409
linyuh183cb712017-12-27 17:02:37 -0800410 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800411 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700412
413 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800414 }
415
twyendde01c52017-09-22 10:07:31 -0700416 /** Test only constructor to avoid initializing dependencies. */
417 @VisibleForTesting
418 DialerCall(Context context) {
linyuh183cb712017-12-27 17:02:37 -0800419 this.context = context;
420 telecomCall = null;
421 latencyReport = null;
422 id = null;
423 hiddenId = 0;
424 dialerCallDelegate = null;
425 videoTechManager = null;
twyendde01c52017-09-22 10:07:31 -0700426 }
427
Eric Erfanianccca3152017-02-22 16:32:36 -0800428 private static int translateState(int state) {
429 switch (state) {
430 case Call.STATE_NEW:
431 case Call.STATE_CONNECTING:
wangqibb94ca62018-04-27 14:34:04 -0700432 return DialerCallState.CONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800433 case Call.STATE_SELECT_PHONE_ACCOUNT:
wangqibb94ca62018-04-27 14:34:04 -0700434 return DialerCallState.SELECT_PHONE_ACCOUNT;
Eric Erfanianccca3152017-02-22 16:32:36 -0800435 case Call.STATE_DIALING:
wangqibb94ca62018-04-27 14:34:04 -0700436 return DialerCallState.DIALING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800437 case Call.STATE_PULLING_CALL:
wangqibb94ca62018-04-27 14:34:04 -0700438 return DialerCallState.PULLING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800439 case Call.STATE_RINGING:
wangqibb94ca62018-04-27 14:34:04 -0700440 return DialerCallState.INCOMING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800441 case Call.STATE_ACTIVE:
wangqibb94ca62018-04-27 14:34:04 -0700442 return DialerCallState.ACTIVE;
Eric Erfanianccca3152017-02-22 16:32:36 -0800443 case Call.STATE_HOLDING:
wangqibb94ca62018-04-27 14:34:04 -0700444 return DialerCallState.ONHOLD;
Eric Erfanianccca3152017-02-22 16:32:36 -0800445 case Call.STATE_DISCONNECTED:
wangqibb94ca62018-04-27 14:34:04 -0700446 return DialerCallState.DISCONNECTED;
Eric Erfanianccca3152017-02-22 16:32:36 -0800447 case Call.STATE_DISCONNECTING:
wangqibb94ca62018-04-27 14:34:04 -0700448 return DialerCallState.DISCONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800449 default:
wangqibb94ca62018-04-27 14:34:04 -0700450 return DialerCallState.INVALID;
Eric Erfanianccca3152017-02-22 16:32:36 -0800451 }
452 }
453
454 public static boolean areSame(DialerCall call1, DialerCall call2) {
455 if (call1 == null && call2 == null) {
456 return true;
457 } else if (call1 == null || call2 == null) {
458 return false;
459 }
460
461 // otherwise compare call Ids
462 return call1.getId().equals(call2.getId());
463 }
464
Eric Erfanianccca3152017-02-22 16:32:36 -0800465 public void addListener(DialerCallListener listener) {
466 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800467 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800468 }
469
470 public void removeListener(DialerCallListener listener) {
471 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800472 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800473 }
474
475 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
476 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800477 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800478 }
479
480 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
481 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800482 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800483 }
484
roldenburg08424ee2018-03-28 17:02:46 -0700485 private void onLteToWifiHandover() {
486 LogUtil.enterBlock("DialerCall.onLteToWifiHandover");
487 if (hasShownLteToWiFiHandoverToast) {
488 return;
489 }
490
491 Toast.makeText(context, R.string.video_call_lte_to_wifi_handover_toast, Toast.LENGTH_LONG)
492 .show();
493 hasShownLteToWiFiHandoverToast = true;
494 }
495
Eric Erfanianccca3152017-02-22 16:32:36 -0800496 public void notifyWiFiToLteHandover() {
497 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800498 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800499 listener.onWiFiToLteHandover();
500 }
501 }
502
503 public void notifyHandoverToWifiFailed() {
504 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800505 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800506 listener.onHandoverToWifiFailure();
507 }
508 }
509
Eric Erfanianc857f902017-05-15 14:05:33 -0700510 public void notifyInternationalCallOnWifi() {
511 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800512 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700513 dialerCallListener.onInternationalCallOnWifi();
514 }
515 }
516
Eric Erfanianccca3152017-02-22 16:32:36 -0800517 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800518 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800519 }
wangqida410d32018-03-06 16:51:38 -0800520
Eric Erfanianccca3152017-02-22 16:32:36 -0800521 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800522 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800523 }
524
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700525 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800526 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700527 }
528
529 public void setCameraDir(int cameraDir) {
530 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
531 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800532 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700533 } else {
linyuh183cb712017-12-27 17:02:37 -0800534 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700535 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800536 }
537
Eric Erfanian2ca43182017-08-31 06:57:16 -0700538 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800539 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700540 }
541
wangqi9982f0d2017-10-11 17:46:07 -0700542 public boolean isVoiceMailNumber() {
543 return isVoicemailNumber;
544 }
545
546 public List<PhoneAccountHandle> getCallCapableAccounts() {
547 return callCapableAccounts;
548 }
549
550 public String getCountryIso() {
551 return countryIso;
552 }
553
554 private void updateIsVoiceMailNumber() {
555 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
556 isVoicemailNumber = true;
roldenburg37a969d2018-02-22 14:46:44 -0800557 return;
wangqi9982f0d2017-10-11 17:46:07 -0700558 }
559
linyuh183cb712017-12-27 17:02:37 -0800560 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700561 isVoicemailNumber = false;
roldenburg37a969d2018-02-22 14:46:44 -0800562 return;
wangqi9982f0d2017-10-11 17:46:07 -0700563 }
564
linyuh183cb712017-12-27 17:02:37 -0800565 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700566 }
567
Eric Erfanianccca3152017-02-22 16:32:36 -0800568 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700569 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800570 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700571 // Clear any cache here that could potentially change on update.
572 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800573 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700574 updateFromTelecomCall();
wangqibb94ca62018-04-27 14:34:04 -0700575 if (oldState != getState() && getState() == DialerCallState.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800576 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800577 listener.onDialerCallDisconnect();
578 }
linyuh183cb712017-12-27 17:02:37 -0800579 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700580 .getEnrichedCallManager()
581 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800582 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700583 .getEnrichedCallManager()
584 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800585 } else {
linyuh183cb712017-12-27 17:02:37 -0800586 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 listener.onDialerCallUpdate();
588 }
589 }
590 Trace.endSection();
591 }
592
wangqi9982f0d2017-10-11 17:46:07 -0700593 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700594 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700595 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800596 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700597
roldenburg6bd612f2018-01-18 12:57:19 -0800598 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700599
linyuh183cb712017-12-27 17:02:37 -0800600 final int translatedState = translateState(telecomCall.getState());
wangqibb94ca62018-04-27 14:34:04 -0700601 if (state != DialerCallState.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800602 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800603 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800604 }
605
linyuh183cb712017-12-27 17:02:37 -0800606 childCallIds.clear();
607 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800608 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800609 childCallIds.add(
610 dialerCallDelegate
611 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800612 .getId());
613 }
614
615 // The number of conferenced calls can change over the course of the call, so use the
616 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800617 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800618
linyuh183cb712017-12-27 17:02:37 -0800619 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800620
621 // If the handle of the call has changed, update state for the call determining if it is an
622 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800623 Uri newHandle = telecomCall.getDetails().getHandle();
624 if (!Objects.equals(handle, newHandle)) {
625 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800626 updateEmergencyCallState();
627 }
628
linyuh183cb712017-12-27 17:02:37 -0800629 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800630 // If the phone account handle of the call is set, cache capability bit indicating whether
631 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800632 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
633 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
634 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800635
linyuh183cb712017-12-27 17:02:37 -0800636 if (phoneAccountHandle != null) {
637 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800638 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800639 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800640 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
wangqida410d32018-03-06 16:51:38 -0800641 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
642 cacheCarrierConfiguration(phoneAccountHandle);
643 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800644 }
645 }
646 }
linyuh183cb712017-12-27 17:02:37 -0800647 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700648 updateIsVoiceMailNumber();
649 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800650 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700651 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700652 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800653 }
654
655 /**
wangqida410d32018-03-06 16:51:38 -0800656 * Caches frequently used carrier configuration locally.
657 *
658 * @param accountHandle The PhoneAccount handle.
659 */
660 @SuppressLint("MissingPermission")
661 private void cacheCarrierConfiguration(PhoneAccountHandle accountHandle) {
662 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
663 return;
664 }
665 if (VERSION.SDK_INT < VERSION_CODES.O) {
666 return;
667 }
668 // TODO(a bug): This may take several seconds to complete, revisit it to move it to worker
669 // thread.
670 carrierConfig =
671 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, accountHandle)
672 .getCarrierConfig();
673 }
674
675 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800676 * Tests corruption of the {@code callExtras} bundle by calling {@link
677 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
678 * be thrown and caught by this function.
679 *
680 * @param callExtras the bundle to verify
681 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
682 */
683 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
684 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700685 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800686 * bundle, resulting in a IllegalArgumentException while validating data under {@link
687 * Bundle#containsKey(String)}.
688 */
689 try {
690 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
691 return false;
692 } catch (IllegalArgumentException e) {
693 LogUtil.e(
694 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
695 return true;
696 }
697 }
698
699 protected void updateFromCallExtras(Bundle callExtras) {
700 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
701 /**
702 * If the bundle is corrupted, abandon information update as a work around. These are not
703 * critical for the dialer to function.
704 */
705 return;
706 }
707 // Check for a change in the child address and notify any listeners.
708 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
709 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800710 if (!Objects.equals(childNumber, this.childNumber)) {
711 this.childNumber = childNumber;
712 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800713 listener.onDialerCallChildNumberChange();
714 }
715 }
716 }
717
718 // Last forwarded number comes in as an array of strings. We want to choose the
719 // last item in the array. The forwarding numbers arrive independently of when the
720 // call is originally set up, so we need to notify the the UI of the change.
721 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
722 ArrayList<String> lastForwardedNumbers =
723 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
724
725 if (lastForwardedNumbers != null) {
726 String lastForwardedNumber = null;
727 if (!lastForwardedNumbers.isEmpty()) {
728 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
729 }
730
linyuh183cb712017-12-27 17:02:37 -0800731 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
732 this.lastForwardedNumber = lastForwardedNumber;
733 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800734 listener.onDialerCallLastForwardedNumberChange();
735 }
736 }
737 }
738 }
739
740 // DialerCall subject is present in the extras at the start of call, so we do not need to
741 // notify any other listeners of this.
742 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
743 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800744 if (!Objects.equals(this.callSubject, callSubject)) {
745 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800746 }
747 }
748 }
749
Eric Erfanianccca3152017-02-22 16:32:36 -0800750 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800751 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800752 }
753
Eric Erfanianc857f902017-05-15 14:05:33 -0700754 /**
755 * @return name appended with a number if the number is restricted/unknown and the user has
756 * received more than one restricted/unknown call.
757 */
758 @Nullable
759 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800760 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
761 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700762 }
763 return name;
764 }
765
766 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800767 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700768 }
769
770 private boolean isHiddenNumber() {
771 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
772 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
773 }
774
Eric Erfanianccca3152017-02-22 16:32:36 -0800775 public boolean hasShownWiFiToLteHandoverToast() {
776 return hasShownWiFiToLteHandoverToast;
777 }
778
779 public void setHasShownWiFiToLteHandoverToast() {
780 hasShownWiFiToLteHandoverToast = true;
781 }
782
783 public boolean showWifiHandoverAlertAsToast() {
784 return doNotShowDialogForHandoffToWifiFailure;
785 }
786
787 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
788 doNotShowDialogForHandoffToWifiFailure = bool;
789 }
790
wangqida410d32018-03-06 16:51:38 -0800791 public boolean showVideoChargesAlertDialog() {
792 if (carrierConfig == null) {
793 return false;
794 }
795 return carrierConfig.getBoolean(
796 TelephonyManagerCompat.CARRIER_CONFIG_KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL);
797 }
798
Eric Erfanianccca3152017-02-22 16:32:36 -0800799 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800800 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800801 }
802
803 @Nullable
804 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800805 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800806 }
807
808 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800809 telecomCall.reject(false, null);
wangqibb94ca62018-04-27 14:34:04 -0700810 setState(DialerCallState.BLOCKED);
Eric Erfanianccca3152017-02-22 16:32:36 -0800811 }
812
813 @Nullable
814 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800815 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800816 }
817
818 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800819 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800820 }
821
822 public boolean isPotentialEmergencyCallback() {
823 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
824 // is actually in emergency callback mode (ie data is disabled).
825 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
826 return true;
827 }
828 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
829 // as a potential emergency callback.
830 if (getExtras() != null
831 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
832 > 0) {
833 long lastEmergencyCallMillis =
834 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
835 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
836 return true;
837 }
838 }
839 return false;
840 }
841
842 boolean isInEmergencyCallbackWindow(long timestampMillis) {
843 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800844 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800845 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
846 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
847 }
848
849 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800850 if (telecomCall != null && telecomCall.getParent() != null) {
wangqibb94ca62018-04-27 14:34:04 -0700851 return DialerCallState.CONFERENCED;
Eric Erfanianccca3152017-02-22 16:32:36 -0800852 } else {
linyuh183cb712017-12-27 17:02:37 -0800853 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800854 }
855 }
856
yueg265089a2017-10-06 14:35:15 -0700857 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800858 return state;
yueg265089a2017-10-06 14:35:15 -0700859 }
860
Eric Erfanianccca3152017-02-22 16:32:36 -0800861 public void setState(int state) {
wangqibb94ca62018-04-27 14:34:04 -0700862 if (state == DialerCallState.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800863 logState.isIncoming = true;
wangqibb94ca62018-04-27 14:34:04 -0700864 } else if (state == DialerCallState.DISCONNECTED) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700865 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800866 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
linyuh183cb712017-12-27 17:02:37 -0800867 if (this.state != state) {
868 logState.duration = newDuration;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700869 } else {
870 LogUtil.i(
871 "DialerCall.setState",
872 "ignoring state transition from DISCONNECTED to DISCONNECTED."
873 + " Duration would have changed from %s to %s",
linyuh183cb712017-12-27 17:02:37 -0800874 logState.duration,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700875 newDuration);
876 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800877 }
linyuh183cb712017-12-27 17:02:37 -0800878 this.state = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800879 }
880
881 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800882 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800883 }
884
885 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800886 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800887 }
888
889 @Nullable
890 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800891 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800892 }
893
894 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800895 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800896 }
897
898 @Nullable
899 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800900 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800901 }
902
903 /** @return The child number for the call, or {@code null} if none specified. */
904 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800905 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800906 }
907
908 /** @return The last forwarded number for the call, or {@code null} if none specified. */
909 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800910 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800911 }
912
wangqif4ba3452018-01-09 11:26:29 -0800913 public boolean isCallForwarded() {
914 return isCallForwarded;
915 }
916
Eric Erfanianccca3152017-02-22 16:32:36 -0800917 /** @return The call subject, or {@code null} if none specified. */
918 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800919 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800920 }
921
922 /**
923 * @return {@code true} if the call's phone account supports call subjects, {@code false}
924 * otherwise.
925 */
926 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800927 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800928 }
929
930 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
931 public DisconnectCause getDisconnectCause() {
wangqibb94ca62018-04-27 14:34:04 -0700932 if (state == DialerCallState.DISCONNECTED || state == DialerCallState.IDLE) {
linyuh183cb712017-12-27 17:02:37 -0800933 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800934 }
935
936 return new DisconnectCause(DisconnectCause.UNKNOWN);
937 }
938
939 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800940 this.disconnectCause = disconnectCause;
941 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800942 }
943
944 /** Returns the possible text message responses. */
945 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800946 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800947 }
948
949 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
950 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800951 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800952
953 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
954 // We allow you to merge if the capabilities allow it or if it is a call with
955 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800956 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800957 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
958 // Cannot merge calls if there are no calls to merge with.
959 return false;
960 }
961 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
962 }
963 return (capabilities == (capabilities & supportedCapabilities));
964 }
965
966 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800967 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800968 }
969
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700970 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800971 public String getUniqueCallId() {
972 return uniqueCallId;
973 }
974
975 /** Gets the time when the call first became active. */
976 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800977 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800978 }
979
wangqied677ef2018-04-04 12:03:00 -0700980 /**
981 * Gets the time when the call is created (see {@link Details#getCreationTimeMillis()}). This is
982 * the same time that is logged as the start time in the Call Log (see {@link
983 * android.provider.CallLog.Calls#DATE}).
984 */
985 @TargetApi(26)
986 public long getCreationTimeMillis() {
987 return telecomCall.getDetails().getCreationTimeMillis();
988 }
989
Eric Erfanianccca3152017-02-22 16:32:36 -0800990 public boolean isConferenceCall() {
991 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
992 }
993
994 @Nullable
995 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -0800996 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -0800997 }
998
999 @Nullable
1000 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -08001001 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -08001002 }
1003
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001004 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -08001005 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -08001006 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -08001007 }
1008
1009 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -08001010 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -08001011 }
1012
1013 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -08001014 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -08001015 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -08001016 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001017 }
1018 return null;
1019 }
1020
1021 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -08001022 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -08001023 }
1024
1025 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -07001026 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001027 }
1028
wangqi8d407a02018-02-15 15:32:52 -08001029 @TargetApi(28)
wangqif6be6172018-03-30 15:57:56 -07001030 public boolean isActiveRttCall() {
wangqi219b8702018-02-13 09:34:41 -08001031 if (BuildCompat.isAtLeastP()) {
1032 return getTelecomCall().isRttActive();
1033 } else {
1034 return false;
1035 }
1036 }
1037
wangqi153af2f2018-02-15 16:21:49 -08001038 @TargetApi(28)
1039 public RttCall getRttCall() {
wangqif6be6172018-03-30 15:57:56 -07001040 if (!isActiveRttCall()) {
wangqi153af2f2018-02-15 16:21:49 -08001041 return null;
1042 }
1043 return getTelecomCall().getRttCall();
1044 }
1045
wangqif6be6172018-03-30 15:57:56 -07001046 @TargetApi(28)
1047 public boolean canUpgradeToRttCall() {
1048 PhoneAccount phoneAccount = getPhoneAccount();
1049 if (phoneAccount == null) {
1050 return false;
1051 }
1052 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
1053 return false;
1054 }
1055 if (isActiveRttCall()) {
1056 return false;
1057 }
1058 if (isVideoCall()) {
1059 return false;
1060 }
1061 if (isConferenceCall()) {
1062 return false;
1063 }
1064 if (CallList.getInstance().hasActiveRttCall()) {
1065 return false;
1066 }
1067 return true;
1068 }
1069
1070 @TargetApi(28)
1071 public void sendRttUpgradeRequest() {
1072 getTelecomCall().sendRttRequest();
1073 }
1074
wangqibc28ea72018-04-02 16:23:00 -07001075 @TargetApi(28)
1076 public void respondToRttRequest(boolean accept, int rttRequestId) {
wangqi81e373e2018-04-24 10:50:57 -07001077 Logger.get(context)
1078 .logCallImpression(
1079 accept
1080 ? DialerImpression.Type.RTT_MID_CALL_ACCEPTED
1081 : DialerImpression.Type.RTT_MID_CALL_REJECTED,
1082 getUniqueCallId(),
1083 getTimeAddedMs());
wangqibc28ea72018-04-02 16:23:00 -07001084 getTelecomCall().respondToRttRequest(rttRequestId, accept);
1085 }
1086
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001087 public boolean hasReceivedVideoUpgradeRequest() {
1088 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
1089 }
1090
1091 public boolean hasSentVideoUpgradeRequest() {
1092 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -08001093 }
1094
wangqi219b8702018-02-13 09:34:41 -08001095 public boolean hasSentRttUpgradeRequest() {
wangqi219b8702018-02-13 09:34:41 -08001096 return false;
1097 }
1098
Eric Erfanianccca3152017-02-22 16:32:36 -08001099 /**
1100 * Determines if the call handle is an emergency number or not and caches the result to avoid
1101 * repeated calls to isEmergencyNumber.
1102 */
1103 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -08001104 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -08001105 }
1106
Eric Erfanianccca3152017-02-22 16:32:36 -08001107 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -08001108 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -08001109 }
1110
1111 /**
1112 * Determines if the call is an external call.
1113 *
1114 * <p>An external call is one which does not exist locally for the {@link
1115 * android.telecom.ConnectionService} it is associated with.
1116 *
Eric Erfanianccca3152017-02-22 16:32:36 -08001117 * @return {@code true} if the call is an external call, {@code false} otherwise.
1118 */
linyuh437ae952018-03-26 12:46:18 -07001119 boolean isExternalCall() {
1120 return hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
Eric Erfanianccca3152017-02-22 16:32:36 -08001121 }
1122
1123 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001124 * Determines if answering this call will cause an ongoing video call to be dropped.
1125 *
1126 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
1127 * otherwise.
1128 */
1129 public boolean answeringDisconnectsForegroundVideoCall() {
1130 Bundle extras = getExtras();
1131 if (extras == null
1132 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
1133 return false;
1134 }
1135 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
1136 }
1137
1138 private void parseCallSpecificAppData() {
1139 if (isExternalCall()) {
1140 return;
1141 }
1142
linyuh183cb712017-12-27 17:02:37 -08001143 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
1144 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001145
linyuh183cb712017-12-27 17:02:37 -08001146 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -07001147 CallSpecificAppData.newBuilder()
1148 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
1149 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001150 }
wangqibb94ca62018-04-27 14:34:04 -07001151 if (getState() == DialerCallState.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -08001152 logState.callSpecificAppData =
1153 logState
Eric Erfanian8369df02017-05-03 10:27:13 -07001154 .callSpecificAppData
1155 .toBuilder()
1156 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
1157 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001158 }
1159 }
1160
1161 @Override
1162 public String toString() {
linyuh183cb712017-12-27 17:02:37 -08001163 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001164 // This should happen only in testing since otherwise we would never have a null
1165 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -08001166 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -08001167 }
1168
1169 return String.format(
1170 Locale.US,
1171 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001172 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001173 id,
wangqibb94ca62018-04-27 14:34:04 -07001174 DialerCallState.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001175 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1176 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1177 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001178 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001179 this.telecomCall.getConferenceableCalls(),
1180 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001181 getVideoTech().getSessionModificationState(),
1182 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001183 }
1184
1185 public String toSimpleString() {
1186 return super.toString();
1187 }
1188
1189 @CallHistoryStatus
1190 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001191 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001192 }
1193
1194 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001195 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001196 }
1197
1198 public boolean didShowCameraPermission() {
1199 return didShowCameraPermission;
1200 }
1201
1202 public void setDidShowCameraPermission(boolean didShow) {
1203 didShowCameraPermission = didShow;
1204 }
1205
wangqida410d32018-03-06 16:51:38 -08001206 public boolean didDismissVideoChargesAlertDialog() {
1207 return didDismissVideoChargesAlertDialog;
1208 }
1209
1210 public void setDidDismissVideoChargesAlertDialog(boolean didDismiss) {
1211 didDismissVideoChargesAlertDialog = didDismiss;
1212 }
1213
Eric Erfanian938468d2017-10-24 14:05:52 -07001214 @Nullable
1215 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001216 return isInGlobalSpamList;
1217 }
1218
1219 public void setIsInGlobalSpamList(boolean inSpamList) {
1220 isInGlobalSpamList = inSpamList;
1221 }
1222
Eric Erfanian938468d2017-10-24 14:05:52 -07001223 @Nullable
1224 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001225 return isInUserSpamList;
1226 }
1227
1228 public void setIsInUserSpamList(boolean inSpamList) {
1229 isInUserSpamList = inSpamList;
1230 }
1231
Eric Erfanian938468d2017-10-24 14:05:52 -07001232 @Nullable
1233 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001234 return isInUserWhiteList;
1235 }
1236
1237 public void setIsInUserWhiteList(boolean inWhiteList) {
1238 isInUserWhiteList = inWhiteList;
1239 }
1240
1241 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001242 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001243 }
1244
1245 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001246 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001247 }
1248
1249 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001250 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001251 }
1252
1253 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001254 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001255 }
1256
1257 public boolean isRemotelyHeld() {
1258 return isRemotelyHeld;
1259 }
1260
Eric Erfanian2ca43182017-08-31 06:57:16 -07001261 public boolean isMergeInProcess() {
1262 return isMergeInProcess;
1263 }
1264
Eric Erfanianccca3152017-02-22 16:32:36 -08001265 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001266 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001267 }
1268
erfanian2cf2c342017-12-21 12:01:33 -08001269 /**
1270 * Try and determine if the call used assisted dialing.
1271 *
1272 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1273 * implmentation is complete in P+.
1274 *
1275 * @return a boolean indicating assisted dialing may have been performed
1276 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001277 public boolean isAssistedDialed() {
1278 if (getIntentExtras() != null) {
erfaniand2e5d0b2018-03-02 14:54:35 -08001279 // P and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
erfanian2cf2c342017-12-21 12:01:33 -08001280 // was used. The Dialer client is responsible for performing assisted dialing before
1281 // placing the outgoing call.
1282 //
1283 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1284 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1285 && getAssistedDialingExtras() != null
1286 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1287 return true;
1288 }
1289 }
1290
Eric Erfanian2ca43182017-08-31 06:57:16 -07001291 return false;
1292 }
1293
erfanian2cf2c342017-12-21 12:01:33 -08001294 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001295 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001296 if (getIntentExtras() == null) {
1297 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001298 }
erfanian2cf2c342017-12-21 12:01:33 -08001299
1300 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1301 return null;
1302 }
1303
erfanianf2556612018-01-23 09:55:59 -08001304 // Used in N-OMR1
erfanian2cf2c342017-12-21 12:01:33 -08001305 return TransformationInfo.newInstanceFromBundle(
1306 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001307 }
1308
Eric Erfanianccca3152017-02-22 16:32:36 -08001309 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001310 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001311 }
1312
Eric Erfanian2ca43182017-08-31 06:57:16 -07001313 public int getAnswerAndReleaseButtonDisplayedTimes() {
1314 return answerAndReleaseButtonDisplayedTimes;
1315 }
1316
1317 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1318 answerAndReleaseButtonDisplayedTimes++;
1319 }
1320
1321 public boolean getReleasedByAnsweringSecondCall() {
1322 return releasedByAnsweringSecondCall;
1323 }
1324
1325 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1326 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1327 }
1328
1329 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1330 return secondCallWithoutAnswerAndReleasedButtonTimes;
1331 }
1332
1333 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1334 secondCallWithoutAnswerAndReleasedButtonTimes++;
1335 }
1336
Eric Erfanian8369df02017-05-03 10:27:13 -07001337 @Nullable
1338 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001339 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001340 }
1341
1342 public void setEnrichedCallCapabilities(
1343 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001344 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001345 }
1346
1347 @Nullable
1348 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001349 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001350 }
1351
1352 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001353 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001354 }
1355
Eric Erfanianccca3152017-02-22 16:32:36 -08001356 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001357 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001358 }
1359
Eric Erfanianccca3152017-02-22 16:32:36 -08001360 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1361 LogUtil.i(
1362 "DialerCall.phoneAccountSelected",
1363 "accountHandle: %s, setDefault: %b",
1364 accountHandle,
1365 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001366 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001367 }
1368
1369 public void disconnect() {
1370 LogUtil.i("DialerCall.disconnect", "");
wangqibb94ca62018-04-27 14:34:04 -07001371 setState(DialerCallState.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001372 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001373 listener.onDialerCallUpdate();
1374 }
linyuh183cb712017-12-27 17:02:37 -08001375 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001376 }
1377
1378 public void hold() {
1379 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001380 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001381 }
1382
1383 public void unhold() {
1384 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001385 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001386 }
1387
1388 public void splitFromConference() {
1389 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001390 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001391 }
1392
1393 public void answer(int videoState) {
1394 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001395 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001396 }
1397
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001398 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001399 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001400 }
1401
Eric Erfanianccca3152017-02-22 16:32:36 -08001402 public void reject(boolean rejectWithMessage, String message) {
1403 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001404 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001405 }
1406
1407 /** Return the string label to represent the call provider */
1408 public String getCallProviderLabel() {
1409 if (callProviderLabel == null) {
1410 PhoneAccount account = getPhoneAccount();
1411 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001412 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001413 callProviderLabel = account.getLabel().toString();
1414 }
1415 }
1416 if (callProviderLabel == null) {
1417 callProviderLabel = "";
1418 }
1419 }
1420 return callProviderLabel;
1421 }
1422
1423 private PhoneAccount getPhoneAccount() {
1424 PhoneAccountHandle accountHandle = getAccountHandle();
1425 if (accountHandle == null) {
1426 return null;
1427 }
linyuh183cb712017-12-27 17:02:37 -08001428 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001429 }
1430
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001431 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001432 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001433 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001434
1435 // Only store the first video tech type found to be available during the life of the call.
1436 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1437 // Update the video tech.
1438 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1439 }
1440 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001441 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001442 }
1443
Eric Erfanianccca3152017-02-22 16:32:36 -08001444 public String getCallbackNumber() {
1445 if (callbackNumber == null) {
1446 // Show the emergency callback number if either:
1447 // 1. This is an emergency call.
1448 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1449 // number.
1450 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1451
1452 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001453 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001454 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001455 }
1456
Eric Erfanianccca3152017-02-22 16:32:36 -08001457 if (callbackNumber == null) {
1458 callbackNumber = "";
1459 }
1460 }
1461 return callbackNumber;
1462 }
1463
wangqi97539352017-09-25 11:15:16 -07001464 public String getSimCountryIso() {
1465 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001466 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001467 .getSimCountryIso();
1468 if (!TextUtils.isEmpty(simCountryIso)) {
1469 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1470 }
1471 return simCountryIso;
1472 }
1473
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001474 @Override
1475 public void onVideoTechStateChanged() {
1476 update();
1477 }
1478
1479 @Override
1480 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001481 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001482 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001483 listener.onDialerCallSessionModificationStateChange();
1484 }
wangqi9982f0d2017-10-11 17:46:07 -07001485 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001486 }
1487
1488 @Override
1489 public void onCameraDimensionsChanged(int width, int height) {
1490 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1491 }
1492
1493 @Override
1494 public void onPeerDimensionsChanged(int width, int height) {
1495 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1496 }
1497
1498 @Override
1499 public void onVideoUpgradeRequestReceived() {
1500 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1501
linyuh183cb712017-12-27 17:02:37 -08001502 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001503 listener.onDialerCallUpgradeToVideo();
1504 }
1505
1506 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001507
linyuh183cb712017-12-27 17:02:37 -08001508 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001509 .logCallImpression(
1510 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1511 }
1512
1513 @Override
1514 public void onUpgradedToVideo(boolean switchToSpeaker) {
1515 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1516
1517 if (!switchToSpeaker) {
1518 return;
1519 }
1520
1521 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1522
1523 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1524 LogUtil.e(
1525 "DialerCall.onUpgradedToVideo",
1526 "toggling speakerphone not allowed when bluetooth supported.");
1527 return;
1528 }
1529
1530 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1531 return;
1532 }
1533
1534 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001535 }
1536
Eric Erfanian2ca43182017-08-31 06:57:16 -07001537 @Override
1538 public void onCapabilitiesUpdated() {
1539 if (getNumber() == null) {
1540 return;
1541 }
1542 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001543 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001544 if (capabilities != null) {
1545 setEnrichedCallCapabilities(capabilities);
1546 update();
1547 }
1548 }
1549
1550 @Override
1551 public void onEnrichedCallStateChanged() {
1552 updateEnrichedCallSession();
1553 }
1554
1555 @Override
1556 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001557 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001558 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1559 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001560 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001561 .logCallImpression(
1562 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1563 getUniqueCallId(),
1564 getTimeAddedMs());
1565 }
1566 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001567 }
1568
1569 private void updateEnrichedCallSession() {
1570 if (getNumber() == null) {
1571 return;
1572 }
1573 if (getEnrichedCallSession() != null) {
1574 // State changes to existing sessions are currently handled by the UI components (which have
1575 // their own listeners). Someday instead we could remove those and just call update() here and
1576 // have the usual onDialerCallUpdate update the UI.
1577 dispatchOnEnrichedCallSessionUpdate();
1578 return;
1579 }
1580
linyuh183cb712017-12-27 17:02:37 -08001581 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001582
1583 Filter filter =
1584 isIncoming()
1585 ? manager.createIncomingCallComposerFilter()
1586 : manager.createOutgoingCallComposerFilter();
1587
1588 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1589 if (session == null) {
1590 return;
1591 }
1592
1593 session.setUniqueDialerCallId(getUniqueCallId());
1594 setEnrichedCallSession(session);
1595
1596 LogUtil.i(
1597 "DialerCall.updateEnrichedCallSession",
1598 "setting session %d's dialer id to %s",
1599 session.getSessionId(),
1600 getUniqueCallId());
1601
1602 dispatchOnEnrichedCallSessionUpdate();
1603 }
1604
1605 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001606 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001607 listener.onEnrichedCallSessionUpdate();
1608 }
1609 }
1610
1611 void onRemovedFromCallList() {
wangqif1861052018-04-19 15:06:06 -07001612 LogUtil.enterBlock("DialerCall.onRemovedFromCallList");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001613 // Ensure we clean up when this call is removed.
wangqia0c49f62018-04-25 15:04:01 -07001614 if (videoTechManager != null) {
1615 videoTechManager.dispatchRemovedFromCallList();
1616 }
1617 // TODO(a bug): Add tests for it to make sure no crash on subsequent call to this method.
1618 if (rttTranscript != null && !isCallRemoved) {
wangqif1861052018-04-19 15:06:06 -07001619 Futures.addCallback(
1620 RttTranscriptUtil.saveRttTranscript(context, rttTranscript),
1621 new DefaultFutureCallback<>(),
1622 MoreExecutors.directExecutor());
wangqif1861052018-04-19 15:06:06 -07001623 }
wangqia0c49f62018-04-25 15:04:01 -07001624 isCallRemoved = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001625 }
1626
wangqi4d705e52017-09-28 12:23:35 -07001627 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1628 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001629 }
1630
Android Dialer974fc292018-02-01 16:12:25 -08001631 public void markFeedbackRequested() {
1632 feedbackRequested = true;
1633 }
1634
1635 public boolean isFeedbackRequested() {
1636 return feedbackRequested;
1637 }
1638
Eric Erfanianccca3152017-02-22 16:32:36 -08001639 /**
twyen73a74c32018-03-07 12:12:24 -08001640 * If the in call UI has shown the phone account selection dialog for the call, the {@link
1641 * PreferredAccountRecorder} to record the result from the dialog.
1642 */
1643 @Nullable
1644 public PreferredAccountRecorder getPreferredAccountRecorder() {
1645 return preferredAccountRecorder;
1646 }
1647
1648 public void setPreferredAccountRecorder(PreferredAccountRecorder preferredAccountRecorder) {
1649 this.preferredAccountRecorder = preferredAccountRecorder;
1650 }
1651
erfaniand05d8992018-03-20 19:42:26 -07001652 /** Indicates the call is eligible for SpeakEasy */
1653 public boolean isSpeakEasyEligible() {
erfaniandcc84cd2018-04-17 12:41:05 -07001654 if (!Constraints.isAvailable(context)) {
1655 return false;
1656 }
1657
1658 return !isPotentialEmergencyCallback()
1659 && !isEmergencyCall()
1660 && !isActiveRttCall()
1661 && !isConferenceCall()
1662 && !isVideoCall()
1663 && !isVoiceMailNumber();
erfaniand05d8992018-03-20 19:42:26 -07001664 }
1665
1666 /** Indicates the user has selected SpeakEasy */
1667 public boolean isSpeakEasyCall() {
1668 if (!isSpeakEasyEligible()) {
1669 return false;
1670 }
1671 return isSpeakEasyCall;
1672 }
1673
1674 /** Sets the user preference for SpeakEasy */
1675 public void setIsSpeakEasyCall(boolean isSpeakEasyCall) {
1676 this.isSpeakEasyCall = isSpeakEasyCall;
1677 }
1678
twyen73a74c32018-03-07 12:12:24 -08001679 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001680 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1681 * means there is no result.
1682 */
1683 @IntDef({
1684 CALL_HISTORY_STATUS_UNKNOWN,
1685 CALL_HISTORY_STATUS_PRESENT,
1686 CALL_HISTORY_STATUS_NOT_PRESENT
1687 })
1688 @Retention(RetentionPolicy.SOURCE)
1689 public @interface CallHistoryStatus {}
1690
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001691 /** Camera direction constants */
1692 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001693 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1694 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1695 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001696 }
1697
1698 /**
1699 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1700 * existing call member variables, but this duplication helps to ensure that none of these logging
1701 * variables will interface with/and affect call logic.
1702 */
1703 public static class LogState {
1704
1705 public DisconnectCause disconnectCause;
1706 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001707 public ContactLookupResult.Type contactLookupResult =
1708 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001709 public CallSpecificAppData callSpecificAppData;
1710 // If this was a conference call, the total number of calls involved in the conference.
1711 public int conferencedCalls = 0;
1712 public long duration = 0;
1713 public boolean isLogged = false;
1714
Eric Erfanian8369df02017-05-03 10:27:13 -07001715 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001716 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001717 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001718 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001719 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001720 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001721 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001722 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001723 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001724 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001725 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001726 return "Voicemail";
1727 default:
1728 return "Not found";
1729 }
1730 }
1731
1732 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1733 if (callSpecificAppData == null) {
1734 return "null";
1735 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001736 switch (callSpecificAppData.getCallInitiationType()) {
1737 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001738 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001739 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001740 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001741 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001742 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001743 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001744 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001745 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001746 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001747 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001748 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001749 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001750 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001751 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001752 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001753 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001754 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001755 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001756 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001757 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001758 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001759 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001760 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001761 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001762 return "Launcher Shortcut";
1763 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001764 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001765 }
1766 }
1767
1768 @Override
1769 public String toString() {
1770 return String.format(
1771 Locale.US,
1772 "["
1773 + "%s, " // DisconnectCause toString already describes the object type
1774 + "isIncoming: %s, "
1775 + "contactLookup: %s, "
1776 + "callInitiation: %s, "
1777 + "duration: %s"
1778 + "]",
1779 disconnectCause,
1780 isIncoming,
1781 lookupToString(contactLookupResult),
1782 initiationToString(callSpecificAppData),
1783 duration);
1784 }
1785 }
1786
roldenburgd7490db2018-01-09 13:51:29 -08001787 /** Coordinates the available VideoTech implementations for a call. */
1788 @VisibleForTesting
1789 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001790 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001791 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001792 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001793 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001794 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001795
roldenburgd7490db2018-01-09 13:51:29 -08001796 @VisibleForTesting
1797 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001798 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001799
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001800 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001801 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001802 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001803
1804 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001805 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001806
linyuh183cb712017-12-27 17:02:37 -08001807 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001808
roldenburgd7490db2018-01-09 13:51:29 -08001809 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001810 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001811 .getRcsVideoShareFactory()
1812 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001813 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001814 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001815 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001816 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001817
1818 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001819 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001820 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001821
1822 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001823 }
1824
roldenburgd7490db2018-01-09 13:51:29 -08001825 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001826 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001827 if (savedTech == emptyVideoTech) {
1828 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001829 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001830 savedTech = tech;
1831 savedTech.becomePrimary();
1832 break;
1833 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001834 }
roldenburg6bd612f2018-01-18 12:57:19 -08001835 } else if (savedTech instanceof DuoVideoTech
1836 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001837 // RCS Video Share will become available after the capability exchange which is slower than
1838 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1839 // not in the middle of any session changes, let it take over.
1840 savedTech = rcsVideoShare;
1841 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001842 }
1843
roldenburgd7490db2018-01-09 13:51:29 -08001844 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001845 }
1846
roldenburgd7490db2018-01-09 13:51:29 -08001847 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001848 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001849 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001850 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001851 }
1852 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001853
1854 void dispatchRemovedFromCallList() {
1855 for (VideoTech videoTech : videoTechs) {
1856 videoTech.onRemovedFromCallList();
1857 }
1858 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001859 }
1860
Eric Erfanianccca3152017-02-22 16:32:36 -08001861 /** Called when canned text responses have been loaded. */
1862 public interface CannedTextResponsesLoadedListener {
1863 void onCannedTextResponsesLoaded(DialerCall call);
1864 }
1865}