blob: da05b9d2ab7073da6ad63c2b365bcc7b3280fe9a [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;
Eric Erfanianccca3152017-02-22 16:32:36 -080084import com.android.incallui.latencyreport.LatencyReport;
erfaniandcc84cd2018-04-17 12:41:05 -070085import com.android.incallui.speakeasy.runtime.Constraints;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070086import com.android.incallui.videotech.VideoTech;
87import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070088import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070089import com.android.incallui.videotech.empty.EmptyVideoTech;
90import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070091import com.android.incallui.videotech.utils.VideoUtils;
wangqid6b10d52018-04-12 14:44:06 -070092import com.google.common.util.concurrent.Futures;
wangqid6b10d52018-04-12 14:44:06 -070093import com.google.common.util.concurrent.MoreExecutors;
Eric Erfanianccca3152017-02-22 16:32:36 -080094import java.lang.annotation.Retention;
95import java.lang.annotation.RetentionPolicy;
96import java.util.ArrayList;
97import java.util.List;
98import java.util.Locale;
99import java.util.Objects;
100import java.util.UUID;
101import java.util.concurrent.CopyOnWriteArrayList;
102import java.util.concurrent.TimeUnit;
103
104/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -0700105public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -0800106
107 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
108 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
109 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700110
111 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -0700112 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700113 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
114
Eric Erfanianccca3152017-02-22 16:32:36 -0800115 private static final String ID_PREFIX = "DialerCall_";
116 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
117 "emergency_callback_window_millis";
linyuh183cb712017-12-27 17:02:37 -0800118 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800119
120 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700121 * A counter used to append to restricted/private/hidden calls so that users can identify them in
122 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
123 * are no live calls.
124 */
linyuh183cb712017-12-27 17:02:37 -0800125 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700126
127 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800128 * The unique call ID for every call. This will help us to identify each call and allow us the
129 * ability to stitch impressions to calls if needed.
130 */
131 private final String uniqueCallId = UUID.randomUUID().toString();
132
linyuh183cb712017-12-27 17:02:37 -0800133 private final Call telecomCall;
134 private final LatencyReport latencyReport;
135 private final String id;
136 private final int hiddenId;
137 private final List<String> childCallIds = new ArrayList<>();
138 private final LogState logState = new LogState();
139 private final Context context;
140 private final DialerCallDelegate dialerCallDelegate;
141 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
142 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800143 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800144 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800145
erfaniand05d8992018-03-20 19:42:26 -0700146 private boolean isSpeakEasyCall;
linyuh183cb712017-12-27 17:02:37 -0800147 private boolean isEmergencyCall;
148 private Uri handle;
149 private int state = State.INVALID;
150 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800151
roldenburg08424ee2018-03-28 17:02:46 -0700152 private boolean hasShownLteToWiFiHandoverToast;
Eric Erfanianccca3152017-02-22 16:32:36 -0800153 private boolean hasShownWiFiToLteHandoverToast;
154 private boolean doNotShowDialogForHandoffToWifiFailure;
155
linyuh183cb712017-12-27 17:02:37 -0800156 private String childNumber;
157 private String lastForwardedNumber;
wangqif4ba3452018-01-09 11:26:29 -0800158 private boolean isCallForwarded;
linyuh183cb712017-12-27 17:02:37 -0800159 private String callSubject;
twyen27c37182018-04-24 11:48:12 -0700160 @Nullable private PhoneAccountHandle phoneAccountHandle;
linyuh183cb712017-12-27 17:02:37 -0800161 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
162 private boolean isSpam;
163 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700164
165 @Nullable private Boolean isInUserSpamList;
166
167 @Nullable private Boolean isInUserWhiteList;
168
169 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800170 private boolean didShowCameraPermission;
wangqida410d32018-03-06 16:51:38 -0800171 private boolean didDismissVideoChargesAlertDialog;
172 private PersistableBundle carrierConfig;
Eric Erfanianccca3152017-02-22 16:32:36 -0800173 private String callProviderLabel;
174 private String callbackNumber;
linyuh183cb712017-12-27 17:02:37 -0800175 private int cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
176 private EnrichedCallCapabilities enrichedCallCapabilities;
177 private Session enrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800178
Eric Erfanian2ca43182017-08-31 06:57:16 -0700179 private int answerAndReleaseButtonDisplayedTimes = 0;
180 private boolean releasedByAnsweringSecondCall = false;
181 // Times when a second call is received but AnswerAndRelease button is not shown
182 // since it's not supported.
183 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700184 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700185
wangqi4d705e52017-09-28 12:23:35 -0700186 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
187 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700188 private boolean isVoicemailNumber;
189 private List<PhoneAccountHandle> callCapableAccounts;
190 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700191
Android Dialer974fc292018-02-01 16:12:25 -0800192 private volatile boolean feedbackRequested = false;
193
twyen73a74c32018-03-07 12:12:24 -0800194 @Nullable private PreferredAccountRecorder preferredAccountRecorder;
wangqia0c49f62018-04-25 15:04:01 -0700195 private boolean isCallRemoved;
twyen73a74c32018-03-07 12:12:24 -0800196
Eric Erfanianccca3152017-02-22 16:32:36 -0800197 public static String getNumberFromHandle(Uri handle) {
198 return handle == null ? "" : handle.getSchemeSpecificPart();
199 }
200
201 /**
202 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700203 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800204 */
205 private boolean isRemotelyHeld;
206
Eric Erfanian2ca43182017-08-31 06:57:16 -0700207 /** Indicates whether this call is currently in the process of being merged into a conference. */
208 private boolean isMergeInProcess;
209
Eric Erfanianccca3152017-02-22 16:32:36 -0800210 /**
211 * Indicates whether the phone account associated with this call supports specifying a call
212 * subject.
213 */
linyuh183cb712017-12-27 17:02:37 -0800214 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800215
wangqied677ef2018-04-04 12:03:00 -0700216 public RttTranscript getRttTranscript() {
217 return rttTranscript;
218 }
219
220 public void setRttTranscript(RttTranscript rttTranscript) {
221 this.rttTranscript = rttTranscript;
222 }
223
224 private RttTranscript rttTranscript;
225
linyuh183cb712017-12-27 17:02:37 -0800226 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800227 new Call.Callback() {
228 @Override
229 public void onStateChanged(Call call, int newState) {
230 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
231 update();
232 }
233
234 @Override
235 public void onParentChanged(Call call, Call newParent) {
236 LogUtil.v(
237 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
238 update();
239 }
240
241 @Override
242 public void onChildrenChanged(Call call, List<Call> children) {
243 update();
244 }
245
246 @Override
247 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700248 LogUtil.v(
249 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800250 update();
251 }
252
253 @Override
254 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
255 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700256 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800257 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800258 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800259 listener.onCannedTextResponsesLoaded(DialerCall.this);
260 }
261 }
262
263 @Override
264 public void onPostDialWait(Call call, String remainingPostDialSequence) {
265 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700266 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800267 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
268 update();
269 }
270
271 @Override
272 public void onVideoCallChanged(Call call, VideoCall videoCall) {
273 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700274 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800275 update();
276 }
277
278 @Override
279 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700280 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700281 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800282 }
283
284 @Override
285 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
286 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700287 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800288 "call %s, conferenceable calls: %d",
289 call,
290 conferenceableCalls.size());
291 update();
292 }
293
294 @Override
wangqi219b8702018-02-13 09:34:41 -0800295 public void onRttModeChanged(Call call, int mode) {
296 LogUtil.v("TelecomCallCallback.onRttModeChanged", "mode=%d", mode);
297 }
298
299 @Override
300 public void onRttRequest(Call call, int id) {
301 LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id);
wangqibc28ea72018-04-02 16:23:00 -0700302 for (DialerCallListener listener : listeners) {
303 listener.onDialerCallUpgradeToRtt(id);
304 }
wangqi219b8702018-02-13 09:34:41 -0800305 }
306
307 @Override
308 public void onRttInitiationFailure(Call call, int reason) {
309 LogUtil.v("TelecomCallCallback.onRttInitiationFailure", "reason=%d", reason);
310 update();
311 }
312
313 @Override
314 public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {
315 LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled);
wangqi81e373e2018-04-24 10:50:57 -0700316 if (enabled) {
317 Logger.get(context)
318 .logCallImpression(
319 DialerImpression.Type.RTT_MID_CALL_ENABLED,
320 getUniqueCallId(),
321 getTimeAddedMs());
322 }
wangqi219b8702018-02-13 09:34:41 -0800323 update();
324 }
325
326 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800327 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
328 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700329 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800330 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
331 switch (event) {
332 // The Previous attempt to Merge two calls together has failed in Telecom. We must
333 // now update the UI to possibly re-enable the Merge button based on the number of
334 // currently conferenceable calls available or Connection Capabilities.
335 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
336 update();
337 break;
338 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
339 notifyWiFiToLteHandover();
340 break;
roldenburg08424ee2018-03-28 17:02:46 -0700341 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI:
342 onLteToWifiHandover();
343 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800344 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
345 notifyHandoverToWifiFailed();
346 break;
347 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
348 isRemotelyHeld = true;
349 update();
350 break;
351 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
352 isRemotelyHeld = false;
353 update();
354 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700355 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
356 notifyInternationalCallOnWifi();
357 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700358 case TelephonyManagerCompat.EVENT_MERGE_START:
359 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
360 isMergeInProcess = true;
361 break;
362 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
363 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
364 isMergeInProcess = false;
365 break;
wangqif4ba3452018-01-09 11:26:29 -0800366 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
367 // Only handle this event for P+ since it's unreliable pre-P.
368 if (BuildCompat.isAtLeastP()) {
369 isCallForwarded = true;
370 update();
371 }
372 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800373 default:
374 break;
375 }
376 }
377 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700378
linyuh183cb712017-12-27 17:02:37 -0800379 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800380
381 public DialerCall(
382 Context context,
383 DialerCallDelegate dialerCallDelegate,
384 Call telecomCall,
385 LatencyReport latencyReport,
386 boolean registerCallback) {
387 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800388 this.context = context;
389 this.dialerCallDelegate = dialerCallDelegate;
390 this.telecomCall = telecomCall;
391 this.latencyReport = latencyReport;
392 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800393
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700394 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800395 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700396
397 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700398 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800399 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700400 } else {
linyuh183cb712017-12-27 17:02:37 -0800401 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700402 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800403
404 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800405 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800406 }
407
linyuh183cb712017-12-27 17:02:37 -0800408 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800409 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700410
411 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800412 }
413
twyendde01c52017-09-22 10:07:31 -0700414 /** Test only constructor to avoid initializing dependencies. */
415 @VisibleForTesting
416 DialerCall(Context context) {
linyuh183cb712017-12-27 17:02:37 -0800417 this.context = context;
418 telecomCall = null;
419 latencyReport = null;
420 id = null;
421 hiddenId = 0;
422 dialerCallDelegate = null;
423 videoTechManager = null;
twyendde01c52017-09-22 10:07:31 -0700424 }
425
Eric Erfanianccca3152017-02-22 16:32:36 -0800426 private static int translateState(int state) {
427 switch (state) {
428 case Call.STATE_NEW:
429 case Call.STATE_CONNECTING:
430 return DialerCall.State.CONNECTING;
431 case Call.STATE_SELECT_PHONE_ACCOUNT:
432 return DialerCall.State.SELECT_PHONE_ACCOUNT;
433 case Call.STATE_DIALING:
434 return DialerCall.State.DIALING;
435 case Call.STATE_PULLING_CALL:
436 return DialerCall.State.PULLING;
437 case Call.STATE_RINGING:
438 return DialerCall.State.INCOMING;
439 case Call.STATE_ACTIVE:
440 return DialerCall.State.ACTIVE;
441 case Call.STATE_HOLDING:
442 return DialerCall.State.ONHOLD;
443 case Call.STATE_DISCONNECTED:
444 return DialerCall.State.DISCONNECTED;
445 case Call.STATE_DISCONNECTING:
446 return DialerCall.State.DISCONNECTING;
447 default:
448 return DialerCall.State.INVALID;
449 }
450 }
451
452 public static boolean areSame(DialerCall call1, DialerCall call2) {
453 if (call1 == null && call2 == null) {
454 return true;
455 } else if (call1 == null || call2 == null) {
456 return false;
457 }
458
459 // otherwise compare call Ids
460 return call1.getId().equals(call2.getId());
461 }
462
Eric Erfanianccca3152017-02-22 16:32:36 -0800463 public void addListener(DialerCallListener listener) {
464 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800465 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800466 }
467
468 public void removeListener(DialerCallListener listener) {
469 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800470 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800471 }
472
473 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
474 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800475 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800476 }
477
478 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
479 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800480 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800481 }
482
roldenburg08424ee2018-03-28 17:02:46 -0700483 private void onLteToWifiHandover() {
484 LogUtil.enterBlock("DialerCall.onLteToWifiHandover");
485 if (hasShownLteToWiFiHandoverToast) {
486 return;
487 }
488
489 Toast.makeText(context, R.string.video_call_lte_to_wifi_handover_toast, Toast.LENGTH_LONG)
490 .show();
491 hasShownLteToWiFiHandoverToast = true;
492 }
493
Eric Erfanianccca3152017-02-22 16:32:36 -0800494 public void notifyWiFiToLteHandover() {
495 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800496 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800497 listener.onWiFiToLteHandover();
498 }
499 }
500
501 public void notifyHandoverToWifiFailed() {
502 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800503 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800504 listener.onHandoverToWifiFailure();
505 }
506 }
507
Eric Erfanianc857f902017-05-15 14:05:33 -0700508 public void notifyInternationalCallOnWifi() {
509 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800510 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700511 dialerCallListener.onInternationalCallOnWifi();
512 }
513 }
514
Eric Erfanianccca3152017-02-22 16:32:36 -0800515 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800516 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800517 }
wangqida410d32018-03-06 16:51:38 -0800518
Eric Erfanianccca3152017-02-22 16:32:36 -0800519 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800520 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800521 }
522
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700523 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800524 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700525 }
526
527 public void setCameraDir(int cameraDir) {
528 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
529 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800530 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700531 } else {
linyuh183cb712017-12-27 17:02:37 -0800532 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700533 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800534 }
535
Eric Erfanian2ca43182017-08-31 06:57:16 -0700536 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800537 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700538 }
539
wangqi9982f0d2017-10-11 17:46:07 -0700540 public boolean isVoiceMailNumber() {
541 return isVoicemailNumber;
542 }
543
544 public List<PhoneAccountHandle> getCallCapableAccounts() {
545 return callCapableAccounts;
546 }
547
548 public String getCountryIso() {
549 return countryIso;
550 }
551
552 private void updateIsVoiceMailNumber() {
553 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
554 isVoicemailNumber = true;
roldenburg37a969d2018-02-22 14:46:44 -0800555 return;
wangqi9982f0d2017-10-11 17:46:07 -0700556 }
557
linyuh183cb712017-12-27 17:02:37 -0800558 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700559 isVoicemailNumber = false;
roldenburg37a969d2018-02-22 14:46:44 -0800560 return;
wangqi9982f0d2017-10-11 17:46:07 -0700561 }
562
linyuh183cb712017-12-27 17:02:37 -0800563 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700564 }
565
Eric Erfanianccca3152017-02-22 16:32:36 -0800566 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700567 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800568 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700569 // Clear any cache here that could potentially change on update.
570 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800571 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700572 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800573 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800574 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800575 listener.onDialerCallDisconnect();
576 }
linyuh183cb712017-12-27 17:02:37 -0800577 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700578 .getEnrichedCallManager()
579 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800580 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700581 .getEnrichedCallManager()
582 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800583 } else {
linyuh183cb712017-12-27 17:02:37 -0800584 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800585 listener.onDialerCallUpdate();
586 }
587 }
588 Trace.endSection();
589 }
590
wangqi9982f0d2017-10-11 17:46:07 -0700591 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700592 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700593 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800594 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700595
roldenburg6bd612f2018-01-18 12:57:19 -0800596 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700597
linyuh183cb712017-12-27 17:02:37 -0800598 final int translatedState = translateState(telecomCall.getState());
599 if (state != State.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800600 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800601 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800602 }
603
linyuh183cb712017-12-27 17:02:37 -0800604 childCallIds.clear();
605 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800606 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800607 childCallIds.add(
608 dialerCallDelegate
609 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800610 .getId());
611 }
612
613 // The number of conferenced calls can change over the course of the call, so use the
614 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800615 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800616
linyuh183cb712017-12-27 17:02:37 -0800617 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800618
619 // If the handle of the call has changed, update state for the call determining if it is an
620 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800621 Uri newHandle = telecomCall.getDetails().getHandle();
622 if (!Objects.equals(handle, newHandle)) {
623 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800624 updateEmergencyCallState();
625 }
626
linyuh183cb712017-12-27 17:02:37 -0800627 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800628 // If the phone account handle of the call is set, cache capability bit indicating whether
629 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800630 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
631 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
632 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800633
linyuh183cb712017-12-27 17:02:37 -0800634 if (phoneAccountHandle != null) {
635 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800636 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800637 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800638 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
wangqida410d32018-03-06 16:51:38 -0800639 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
640 cacheCarrierConfiguration(phoneAccountHandle);
641 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800642 }
643 }
644 }
linyuh183cb712017-12-27 17:02:37 -0800645 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700646 updateIsVoiceMailNumber();
647 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800648 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700649 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700650 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800651 }
652
653 /**
wangqida410d32018-03-06 16:51:38 -0800654 * Caches frequently used carrier configuration locally.
655 *
656 * @param accountHandle The PhoneAccount handle.
657 */
658 @SuppressLint("MissingPermission")
659 private void cacheCarrierConfiguration(PhoneAccountHandle accountHandle) {
660 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
661 return;
662 }
663 if (VERSION.SDK_INT < VERSION_CODES.O) {
664 return;
665 }
666 // TODO(a bug): This may take several seconds to complete, revisit it to move it to worker
667 // thread.
668 carrierConfig =
669 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, accountHandle)
670 .getCarrierConfig();
671 }
672
673 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800674 * Tests corruption of the {@code callExtras} bundle by calling {@link
675 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
676 * be thrown and caught by this function.
677 *
678 * @param callExtras the bundle to verify
679 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
680 */
681 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
682 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700683 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800684 * bundle, resulting in a IllegalArgumentException while validating data under {@link
685 * Bundle#containsKey(String)}.
686 */
687 try {
688 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
689 return false;
690 } catch (IllegalArgumentException e) {
691 LogUtil.e(
692 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
693 return true;
694 }
695 }
696
697 protected void updateFromCallExtras(Bundle callExtras) {
698 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
699 /**
700 * If the bundle is corrupted, abandon information update as a work around. These are not
701 * critical for the dialer to function.
702 */
703 return;
704 }
705 // Check for a change in the child address and notify any listeners.
706 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
707 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800708 if (!Objects.equals(childNumber, this.childNumber)) {
709 this.childNumber = childNumber;
710 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800711 listener.onDialerCallChildNumberChange();
712 }
713 }
714 }
715
716 // Last forwarded number comes in as an array of strings. We want to choose the
717 // last item in the array. The forwarding numbers arrive independently of when the
718 // call is originally set up, so we need to notify the the UI of the change.
719 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
720 ArrayList<String> lastForwardedNumbers =
721 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
722
723 if (lastForwardedNumbers != null) {
724 String lastForwardedNumber = null;
725 if (!lastForwardedNumbers.isEmpty()) {
726 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
727 }
728
linyuh183cb712017-12-27 17:02:37 -0800729 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
730 this.lastForwardedNumber = lastForwardedNumber;
731 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800732 listener.onDialerCallLastForwardedNumberChange();
733 }
734 }
735 }
736 }
737
738 // DialerCall subject is present in the extras at the start of call, so we do not need to
739 // notify any other listeners of this.
740 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
741 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800742 if (!Objects.equals(this.callSubject, callSubject)) {
743 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800744 }
745 }
746 }
747
Eric Erfanianccca3152017-02-22 16:32:36 -0800748 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800749 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800750 }
751
Eric Erfanianc857f902017-05-15 14:05:33 -0700752 /**
753 * @return name appended with a number if the number is restricted/unknown and the user has
754 * received more than one restricted/unknown call.
755 */
756 @Nullable
757 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800758 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
759 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700760 }
761 return name;
762 }
763
764 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800765 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700766 }
767
768 private boolean isHiddenNumber() {
769 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
770 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
771 }
772
Eric Erfanianccca3152017-02-22 16:32:36 -0800773 public boolean hasShownWiFiToLteHandoverToast() {
774 return hasShownWiFiToLteHandoverToast;
775 }
776
777 public void setHasShownWiFiToLteHandoverToast() {
778 hasShownWiFiToLteHandoverToast = true;
779 }
780
781 public boolean showWifiHandoverAlertAsToast() {
782 return doNotShowDialogForHandoffToWifiFailure;
783 }
784
785 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
786 doNotShowDialogForHandoffToWifiFailure = bool;
787 }
788
wangqida410d32018-03-06 16:51:38 -0800789 public boolean showVideoChargesAlertDialog() {
790 if (carrierConfig == null) {
791 return false;
792 }
793 return carrierConfig.getBoolean(
794 TelephonyManagerCompat.CARRIER_CONFIG_KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL);
795 }
796
Eric Erfanianccca3152017-02-22 16:32:36 -0800797 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800798 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800799 }
800
801 @Nullable
802 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800803 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800804 }
805
806 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800807 telecomCall.reject(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800808 setState(State.BLOCKED);
809 }
810
811 @Nullable
812 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800813 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800814 }
815
816 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800817 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800818 }
819
820 public boolean isPotentialEmergencyCallback() {
821 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
822 // is actually in emergency callback mode (ie data is disabled).
823 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
824 return true;
825 }
826 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
827 // as a potential emergency callback.
828 if (getExtras() != null
829 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
830 > 0) {
831 long lastEmergencyCallMillis =
832 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
833 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
834 return true;
835 }
836 }
837 return false;
838 }
839
840 boolean isInEmergencyCallbackWindow(long timestampMillis) {
841 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800842 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800843 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
844 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
845 }
846
847 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800848 if (telecomCall != null && telecomCall.getParent() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800849 return State.CONFERENCED;
850 } else {
linyuh183cb712017-12-27 17:02:37 -0800851 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800852 }
853 }
854
yueg265089a2017-10-06 14:35:15 -0700855 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800856 return state;
yueg265089a2017-10-06 14:35:15 -0700857 }
858
Eric Erfanianccca3152017-02-22 16:32:36 -0800859 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700860 if (state == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800861 logState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700862 } else if (state == State.DISCONNECTED) {
863 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800864 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
linyuh183cb712017-12-27 17:02:37 -0800865 if (this.state != state) {
866 logState.duration = newDuration;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700867 } else {
868 LogUtil.i(
869 "DialerCall.setState",
870 "ignoring state transition from DISCONNECTED to DISCONNECTED."
871 + " Duration would have changed from %s to %s",
linyuh183cb712017-12-27 17:02:37 -0800872 logState.duration,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700873 newDuration);
874 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800875 }
linyuh183cb712017-12-27 17:02:37 -0800876 this.state = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800877 }
878
879 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800880 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800881 }
882
883 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800884 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800885 }
886
887 @Nullable
888 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800889 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800890 }
891
892 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800893 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800894 }
895
896 @Nullable
897 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800898 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800899 }
900
901 /** @return The child number for the call, or {@code null} if none specified. */
902 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800903 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800904 }
905
906 /** @return The last forwarded number for the call, or {@code null} if none specified. */
907 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800908 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800909 }
910
wangqif4ba3452018-01-09 11:26:29 -0800911 public boolean isCallForwarded() {
912 return isCallForwarded;
913 }
914
Eric Erfanianccca3152017-02-22 16:32:36 -0800915 /** @return The call subject, or {@code null} if none specified. */
916 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800917 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800918 }
919
920 /**
921 * @return {@code true} if the call's phone account supports call subjects, {@code false}
922 * otherwise.
923 */
924 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800925 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800926 }
927
928 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
929 public DisconnectCause getDisconnectCause() {
linyuh183cb712017-12-27 17:02:37 -0800930 if (state == State.DISCONNECTED || state == State.IDLE) {
931 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800932 }
933
934 return new DisconnectCause(DisconnectCause.UNKNOWN);
935 }
936
937 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800938 this.disconnectCause = disconnectCause;
939 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800940 }
941
942 /** Returns the possible text message responses. */
943 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800944 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800945 }
946
947 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
948 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800949 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800950
951 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
952 // We allow you to merge if the capabilities allow it or if it is a call with
953 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800954 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800955 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
956 // Cannot merge calls if there are no calls to merge with.
957 return false;
958 }
959 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
960 }
961 return (capabilities == (capabilities & supportedCapabilities));
962 }
963
964 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800965 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800966 }
967
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700968 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800969 public String getUniqueCallId() {
970 return uniqueCallId;
971 }
972
973 /** Gets the time when the call first became active. */
974 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800975 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800976 }
977
wangqied677ef2018-04-04 12:03:00 -0700978 /**
979 * Gets the time when the call is created (see {@link Details#getCreationTimeMillis()}). This is
980 * the same time that is logged as the start time in the Call Log (see {@link
981 * android.provider.CallLog.Calls#DATE}).
982 */
983 @TargetApi(26)
984 public long getCreationTimeMillis() {
985 return telecomCall.getDetails().getCreationTimeMillis();
986 }
987
Eric Erfanianccca3152017-02-22 16:32:36 -0800988 public boolean isConferenceCall() {
989 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
990 }
991
992 @Nullable
993 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -0800994 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -0800995 }
996
997 @Nullable
998 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -0800999 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -08001000 }
1001
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001002 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -08001003 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -08001004 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -08001005 }
1006
1007 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -08001008 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -08001009 }
1010
1011 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -08001012 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -08001013 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -08001014 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001015 }
1016 return null;
1017 }
1018
1019 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -08001020 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -08001021 }
1022
1023 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -07001024 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001025 }
1026
wangqi8d407a02018-02-15 15:32:52 -08001027 @TargetApi(28)
wangqif6be6172018-03-30 15:57:56 -07001028 public boolean isActiveRttCall() {
wangqi219b8702018-02-13 09:34:41 -08001029 if (BuildCompat.isAtLeastP()) {
1030 return getTelecomCall().isRttActive();
1031 } else {
1032 return false;
1033 }
1034 }
1035
wangqi153af2f2018-02-15 16:21:49 -08001036 @TargetApi(28)
1037 public RttCall getRttCall() {
wangqif6be6172018-03-30 15:57:56 -07001038 if (!isActiveRttCall()) {
wangqi153af2f2018-02-15 16:21:49 -08001039 return null;
1040 }
1041 return getTelecomCall().getRttCall();
1042 }
1043
wangqif6be6172018-03-30 15:57:56 -07001044 @TargetApi(28)
1045 public boolean canUpgradeToRttCall() {
1046 PhoneAccount phoneAccount = getPhoneAccount();
1047 if (phoneAccount == null) {
1048 return false;
1049 }
1050 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
1051 return false;
1052 }
1053 if (isActiveRttCall()) {
1054 return false;
1055 }
1056 if (isVideoCall()) {
1057 return false;
1058 }
1059 if (isConferenceCall()) {
1060 return false;
1061 }
1062 if (CallList.getInstance().hasActiveRttCall()) {
1063 return false;
1064 }
1065 return true;
1066 }
1067
1068 @TargetApi(28)
1069 public void sendRttUpgradeRequest() {
1070 getTelecomCall().sendRttRequest();
1071 }
1072
wangqibc28ea72018-04-02 16:23:00 -07001073 @TargetApi(28)
1074 public void respondToRttRequest(boolean accept, int rttRequestId) {
wangqi81e373e2018-04-24 10:50:57 -07001075 Logger.get(context)
1076 .logCallImpression(
1077 accept
1078 ? DialerImpression.Type.RTT_MID_CALL_ACCEPTED
1079 : DialerImpression.Type.RTT_MID_CALL_REJECTED,
1080 getUniqueCallId(),
1081 getTimeAddedMs());
wangqibc28ea72018-04-02 16:23:00 -07001082 getTelecomCall().respondToRttRequest(rttRequestId, accept);
1083 }
1084
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001085 public boolean hasReceivedVideoUpgradeRequest() {
1086 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
1087 }
1088
1089 public boolean hasSentVideoUpgradeRequest() {
1090 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -08001091 }
1092
wangqi219b8702018-02-13 09:34:41 -08001093 public boolean hasSentRttUpgradeRequest() {
wangqi219b8702018-02-13 09:34:41 -08001094 return false;
1095 }
1096
Eric Erfanianccca3152017-02-22 16:32:36 -08001097 /**
1098 * Determines if the call handle is an emergency number or not and caches the result to avoid
1099 * repeated calls to isEmergencyNumber.
1100 */
1101 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -08001102 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -08001103 }
1104
Eric Erfanianccca3152017-02-22 16:32:36 -08001105 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -08001106 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -08001107 }
1108
1109 /**
1110 * Determines if the call is an external call.
1111 *
1112 * <p>An external call is one which does not exist locally for the {@link
1113 * android.telecom.ConnectionService} it is associated with.
1114 *
Eric Erfanianccca3152017-02-22 16:32:36 -08001115 * @return {@code true} if the call is an external call, {@code false} otherwise.
1116 */
linyuh437ae952018-03-26 12:46:18 -07001117 boolean isExternalCall() {
1118 return hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
Eric Erfanianccca3152017-02-22 16:32:36 -08001119 }
1120
1121 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001122 * Determines if answering this call will cause an ongoing video call to be dropped.
1123 *
1124 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
1125 * otherwise.
1126 */
1127 public boolean answeringDisconnectsForegroundVideoCall() {
1128 Bundle extras = getExtras();
1129 if (extras == null
1130 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
1131 return false;
1132 }
1133 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
1134 }
1135
1136 private void parseCallSpecificAppData() {
1137 if (isExternalCall()) {
1138 return;
1139 }
1140
linyuh183cb712017-12-27 17:02:37 -08001141 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
1142 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001143
linyuh183cb712017-12-27 17:02:37 -08001144 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -07001145 CallSpecificAppData.newBuilder()
1146 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
1147 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001148 }
1149 if (getState() == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -08001150 logState.callSpecificAppData =
1151 logState
Eric Erfanian8369df02017-05-03 10:27:13 -07001152 .callSpecificAppData
1153 .toBuilder()
1154 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
1155 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001156 }
1157 }
1158
1159 @Override
1160 public String toString() {
linyuh183cb712017-12-27 17:02:37 -08001161 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001162 // This should happen only in testing since otherwise we would never have a null
1163 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -08001164 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -08001165 }
1166
1167 return String.format(
1168 Locale.US,
1169 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001170 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001171 id,
Eric Erfanianccca3152017-02-22 16:32:36 -08001172 State.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001173 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1174 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1175 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001176 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001177 this.telecomCall.getConferenceableCalls(),
1178 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001179 getVideoTech().getSessionModificationState(),
1180 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001181 }
1182
1183 public String toSimpleString() {
1184 return super.toString();
1185 }
1186
1187 @CallHistoryStatus
1188 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001189 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001190 }
1191
1192 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001193 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001194 }
1195
1196 public boolean didShowCameraPermission() {
1197 return didShowCameraPermission;
1198 }
1199
1200 public void setDidShowCameraPermission(boolean didShow) {
1201 didShowCameraPermission = didShow;
1202 }
1203
wangqida410d32018-03-06 16:51:38 -08001204 public boolean didDismissVideoChargesAlertDialog() {
1205 return didDismissVideoChargesAlertDialog;
1206 }
1207
1208 public void setDidDismissVideoChargesAlertDialog(boolean didDismiss) {
1209 didDismissVideoChargesAlertDialog = didDismiss;
1210 }
1211
Eric Erfanian938468d2017-10-24 14:05:52 -07001212 @Nullable
1213 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001214 return isInGlobalSpamList;
1215 }
1216
1217 public void setIsInGlobalSpamList(boolean inSpamList) {
1218 isInGlobalSpamList = inSpamList;
1219 }
1220
Eric Erfanian938468d2017-10-24 14:05:52 -07001221 @Nullable
1222 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001223 return isInUserSpamList;
1224 }
1225
1226 public void setIsInUserSpamList(boolean inSpamList) {
1227 isInUserSpamList = inSpamList;
1228 }
1229
Eric Erfanian938468d2017-10-24 14:05:52 -07001230 @Nullable
1231 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001232 return isInUserWhiteList;
1233 }
1234
1235 public void setIsInUserWhiteList(boolean inWhiteList) {
1236 isInUserWhiteList = inWhiteList;
1237 }
1238
1239 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001240 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001241 }
1242
1243 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001244 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001245 }
1246
1247 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001248 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001249 }
1250
1251 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001252 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001253 }
1254
1255 public boolean isRemotelyHeld() {
1256 return isRemotelyHeld;
1257 }
1258
Eric Erfanian2ca43182017-08-31 06:57:16 -07001259 public boolean isMergeInProcess() {
1260 return isMergeInProcess;
1261 }
1262
Eric Erfanianccca3152017-02-22 16:32:36 -08001263 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001264 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001265 }
1266
erfanian2cf2c342017-12-21 12:01:33 -08001267 /**
1268 * Try and determine if the call used assisted dialing.
1269 *
1270 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1271 * implmentation is complete in P+.
1272 *
1273 * @return a boolean indicating assisted dialing may have been performed
1274 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001275 public boolean isAssistedDialed() {
1276 if (getIntentExtras() != null) {
erfaniand2e5d0b2018-03-02 14:54:35 -08001277 // P and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
erfanian2cf2c342017-12-21 12:01:33 -08001278 // was used. The Dialer client is responsible for performing assisted dialing before
1279 // placing the outgoing call.
1280 //
1281 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1282 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1283 && getAssistedDialingExtras() != null
1284 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1285 return true;
1286 }
1287 }
1288
Eric Erfanian2ca43182017-08-31 06:57:16 -07001289 return false;
1290 }
1291
erfanian2cf2c342017-12-21 12:01:33 -08001292 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001293 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001294 if (getIntentExtras() == null) {
1295 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001296 }
erfanian2cf2c342017-12-21 12:01:33 -08001297
1298 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1299 return null;
1300 }
1301
erfanianf2556612018-01-23 09:55:59 -08001302 // Used in N-OMR1
erfanian2cf2c342017-12-21 12:01:33 -08001303 return TransformationInfo.newInstanceFromBundle(
1304 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001305 }
1306
Eric Erfanianccca3152017-02-22 16:32:36 -08001307 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001308 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001309 }
1310
Eric Erfanian2ca43182017-08-31 06:57:16 -07001311 public int getAnswerAndReleaseButtonDisplayedTimes() {
1312 return answerAndReleaseButtonDisplayedTimes;
1313 }
1314
1315 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1316 answerAndReleaseButtonDisplayedTimes++;
1317 }
1318
1319 public boolean getReleasedByAnsweringSecondCall() {
1320 return releasedByAnsweringSecondCall;
1321 }
1322
1323 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1324 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1325 }
1326
1327 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1328 return secondCallWithoutAnswerAndReleasedButtonTimes;
1329 }
1330
1331 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1332 secondCallWithoutAnswerAndReleasedButtonTimes++;
1333 }
1334
Eric Erfanian8369df02017-05-03 10:27:13 -07001335 @Nullable
1336 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001337 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001338 }
1339
1340 public void setEnrichedCallCapabilities(
1341 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001342 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001343 }
1344
1345 @Nullable
1346 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001347 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001348 }
1349
1350 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001351 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001352 }
1353
Eric Erfanianccca3152017-02-22 16:32:36 -08001354 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001355 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001356 }
1357
Eric Erfanianccca3152017-02-22 16:32:36 -08001358 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1359 LogUtil.i(
1360 "DialerCall.phoneAccountSelected",
1361 "accountHandle: %s, setDefault: %b",
1362 accountHandle,
1363 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001364 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001365 }
1366
1367 public void disconnect() {
1368 LogUtil.i("DialerCall.disconnect", "");
1369 setState(DialerCall.State.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001370 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001371 listener.onDialerCallUpdate();
1372 }
linyuh183cb712017-12-27 17:02:37 -08001373 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001374 }
1375
1376 public void hold() {
1377 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001378 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001379 }
1380
1381 public void unhold() {
1382 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001383 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001384 }
1385
1386 public void splitFromConference() {
1387 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001388 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001389 }
1390
1391 public void answer(int videoState) {
1392 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001393 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001394 }
1395
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001396 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001397 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001398 }
1399
Eric Erfanianccca3152017-02-22 16:32:36 -08001400 public void reject(boolean rejectWithMessage, String message) {
1401 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001402 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001403 }
1404
1405 /** Return the string label to represent the call provider */
1406 public String getCallProviderLabel() {
1407 if (callProviderLabel == null) {
1408 PhoneAccount account = getPhoneAccount();
1409 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001410 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001411 callProviderLabel = account.getLabel().toString();
1412 }
1413 }
1414 if (callProviderLabel == null) {
1415 callProviderLabel = "";
1416 }
1417 }
1418 return callProviderLabel;
1419 }
1420
1421 private PhoneAccount getPhoneAccount() {
1422 PhoneAccountHandle accountHandle = getAccountHandle();
1423 if (accountHandle == null) {
1424 return null;
1425 }
linyuh183cb712017-12-27 17:02:37 -08001426 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001427 }
1428
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001429 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001430 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001431 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001432
1433 // Only store the first video tech type found to be available during the life of the call.
1434 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1435 // Update the video tech.
1436 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1437 }
1438 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001439 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001440 }
1441
Eric Erfanianccca3152017-02-22 16:32:36 -08001442 public String getCallbackNumber() {
1443 if (callbackNumber == null) {
1444 // Show the emergency callback number if either:
1445 // 1. This is an emergency call.
1446 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1447 // number.
1448 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1449
1450 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001451 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001452 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001453 }
1454
Eric Erfanianccca3152017-02-22 16:32:36 -08001455 if (callbackNumber == null) {
1456 callbackNumber = "";
1457 }
1458 }
1459 return callbackNumber;
1460 }
1461
wangqi97539352017-09-25 11:15:16 -07001462 public String getSimCountryIso() {
1463 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001464 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001465 .getSimCountryIso();
1466 if (!TextUtils.isEmpty(simCountryIso)) {
1467 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1468 }
1469 return simCountryIso;
1470 }
1471
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001472 @Override
1473 public void onVideoTechStateChanged() {
1474 update();
1475 }
1476
1477 @Override
1478 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001479 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001480 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001481 listener.onDialerCallSessionModificationStateChange();
1482 }
wangqi9982f0d2017-10-11 17:46:07 -07001483 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001484 }
1485
1486 @Override
1487 public void onCameraDimensionsChanged(int width, int height) {
1488 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1489 }
1490
1491 @Override
1492 public void onPeerDimensionsChanged(int width, int height) {
1493 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1494 }
1495
1496 @Override
1497 public void onVideoUpgradeRequestReceived() {
1498 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1499
linyuh183cb712017-12-27 17:02:37 -08001500 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001501 listener.onDialerCallUpgradeToVideo();
1502 }
1503
1504 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001505
linyuh183cb712017-12-27 17:02:37 -08001506 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001507 .logCallImpression(
1508 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1509 }
1510
1511 @Override
1512 public void onUpgradedToVideo(boolean switchToSpeaker) {
1513 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1514
1515 if (!switchToSpeaker) {
1516 return;
1517 }
1518
1519 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1520
1521 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1522 LogUtil.e(
1523 "DialerCall.onUpgradedToVideo",
1524 "toggling speakerphone not allowed when bluetooth supported.");
1525 return;
1526 }
1527
1528 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1529 return;
1530 }
1531
1532 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001533 }
1534
Eric Erfanian2ca43182017-08-31 06:57:16 -07001535 @Override
1536 public void onCapabilitiesUpdated() {
1537 if (getNumber() == null) {
1538 return;
1539 }
1540 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001541 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001542 if (capabilities != null) {
1543 setEnrichedCallCapabilities(capabilities);
1544 update();
1545 }
1546 }
1547
1548 @Override
1549 public void onEnrichedCallStateChanged() {
1550 updateEnrichedCallSession();
1551 }
1552
1553 @Override
1554 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001555 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001556 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1557 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001558 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001559 .logCallImpression(
1560 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1561 getUniqueCallId(),
1562 getTimeAddedMs());
1563 }
1564 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001565 }
1566
1567 private void updateEnrichedCallSession() {
1568 if (getNumber() == null) {
1569 return;
1570 }
1571 if (getEnrichedCallSession() != null) {
1572 // State changes to existing sessions are currently handled by the UI components (which have
1573 // their own listeners). Someday instead we could remove those and just call update() here and
1574 // have the usual onDialerCallUpdate update the UI.
1575 dispatchOnEnrichedCallSessionUpdate();
1576 return;
1577 }
1578
linyuh183cb712017-12-27 17:02:37 -08001579 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001580
1581 Filter filter =
1582 isIncoming()
1583 ? manager.createIncomingCallComposerFilter()
1584 : manager.createOutgoingCallComposerFilter();
1585
1586 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1587 if (session == null) {
1588 return;
1589 }
1590
1591 session.setUniqueDialerCallId(getUniqueCallId());
1592 setEnrichedCallSession(session);
1593
1594 LogUtil.i(
1595 "DialerCall.updateEnrichedCallSession",
1596 "setting session %d's dialer id to %s",
1597 session.getSessionId(),
1598 getUniqueCallId());
1599
1600 dispatchOnEnrichedCallSessionUpdate();
1601 }
1602
1603 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001604 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001605 listener.onEnrichedCallSessionUpdate();
1606 }
1607 }
1608
1609 void onRemovedFromCallList() {
wangqif1861052018-04-19 15:06:06 -07001610 LogUtil.enterBlock("DialerCall.onRemovedFromCallList");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001611 // Ensure we clean up when this call is removed.
wangqia0c49f62018-04-25 15:04:01 -07001612 if (videoTechManager != null) {
1613 videoTechManager.dispatchRemovedFromCallList();
1614 }
1615 // TODO(a bug): Add tests for it to make sure no crash on subsequent call to this method.
1616 if (rttTranscript != null && !isCallRemoved) {
wangqif1861052018-04-19 15:06:06 -07001617 Futures.addCallback(
1618 RttTranscriptUtil.saveRttTranscript(context, rttTranscript),
1619 new DefaultFutureCallback<>(),
1620 MoreExecutors.directExecutor());
wangqif1861052018-04-19 15:06:06 -07001621 }
wangqia0c49f62018-04-25 15:04:01 -07001622 isCallRemoved = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001623 }
1624
wangqi4d705e52017-09-28 12:23:35 -07001625 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1626 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001627 }
1628
Android Dialer974fc292018-02-01 16:12:25 -08001629 public void markFeedbackRequested() {
1630 feedbackRequested = true;
1631 }
1632
1633 public boolean isFeedbackRequested() {
1634 return feedbackRequested;
1635 }
1636
Eric Erfanianccca3152017-02-22 16:32:36 -08001637 /**
twyen73a74c32018-03-07 12:12:24 -08001638 * If the in call UI has shown the phone account selection dialog for the call, the {@link
1639 * PreferredAccountRecorder} to record the result from the dialog.
1640 */
1641 @Nullable
1642 public PreferredAccountRecorder getPreferredAccountRecorder() {
1643 return preferredAccountRecorder;
1644 }
1645
1646 public void setPreferredAccountRecorder(PreferredAccountRecorder preferredAccountRecorder) {
1647 this.preferredAccountRecorder = preferredAccountRecorder;
1648 }
1649
erfaniand05d8992018-03-20 19:42:26 -07001650 /** Indicates the call is eligible for SpeakEasy */
1651 public boolean isSpeakEasyEligible() {
erfaniandcc84cd2018-04-17 12:41:05 -07001652 if (!Constraints.isAvailable(context)) {
1653 return false;
1654 }
1655
1656 return !isPotentialEmergencyCallback()
1657 && !isEmergencyCall()
1658 && !isActiveRttCall()
1659 && !isConferenceCall()
1660 && !isVideoCall()
1661 && !isVoiceMailNumber();
erfaniand05d8992018-03-20 19:42:26 -07001662 }
1663
1664 /** Indicates the user has selected SpeakEasy */
1665 public boolean isSpeakEasyCall() {
1666 if (!isSpeakEasyEligible()) {
1667 return false;
1668 }
1669 return isSpeakEasyCall;
1670 }
1671
1672 /** Sets the user preference for SpeakEasy */
1673 public void setIsSpeakEasyCall(boolean isSpeakEasyCall) {
1674 this.isSpeakEasyCall = isSpeakEasyCall;
1675 }
1676
twyen73a74c32018-03-07 12:12:24 -08001677 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001678 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1679 * means there is no result.
1680 */
1681 @IntDef({
1682 CALL_HISTORY_STATUS_UNKNOWN,
1683 CALL_HISTORY_STATUS_PRESENT,
1684 CALL_HISTORY_STATUS_NOT_PRESENT
1685 })
1686 @Retention(RetentionPolicy.SOURCE)
1687 public @interface CallHistoryStatus {}
1688
1689 /* Defines different states of this call */
1690 public static class State {
1691
1692 public static final int INVALID = 0;
1693 public static final int NEW = 1; /* The call is new. */
1694 public static final int IDLE = 2; /* The call is idle. Nothing active */
1695 public static final int ACTIVE = 3; /* There is an active call */
1696 public static final int INCOMING = 4; /* A normal incoming phone call */
1697 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1698 public static final int DIALING = 6; /* An outgoing call during dial phase */
1699 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1700 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1701 public static final int DISCONNECTING = 9; /* A call is being ended. */
1702 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1703 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1704 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1705 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1706 public static final int BLOCKED = 14; /* The number was found on the block list */
1707 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001708 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001709
1710 public static boolean isConnectingOrConnected(int state) {
1711 switch (state) {
1712 case ACTIVE:
1713 case INCOMING:
1714 case CALL_WAITING:
1715 case CONNECTING:
1716 case DIALING:
1717 case PULLING:
1718 case REDIALING:
1719 case ONHOLD:
1720 case CONFERENCED:
1721 return true;
1722 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001723 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001724 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001725 }
1726
1727 public static boolean isDialing(int state) {
1728 return state == DIALING || state == PULLING || state == REDIALING;
1729 }
1730
1731 public static String toString(int state) {
1732 switch (state) {
1733 case INVALID:
1734 return "INVALID";
1735 case NEW:
1736 return "NEW";
1737 case IDLE:
1738 return "IDLE";
1739 case ACTIVE:
1740 return "ACTIVE";
1741 case INCOMING:
1742 return "INCOMING";
1743 case CALL_WAITING:
1744 return "CALL_WAITING";
1745 case DIALING:
1746 return "DIALING";
1747 case PULLING:
1748 return "PULLING";
1749 case REDIALING:
1750 return "REDIALING";
1751 case ONHOLD:
1752 return "ONHOLD";
1753 case DISCONNECTING:
1754 return "DISCONNECTING";
1755 case DISCONNECTED:
1756 return "DISCONNECTED";
1757 case CONFERENCED:
1758 return "CONFERENCED";
1759 case SELECT_PHONE_ACCOUNT:
1760 return "SELECT_PHONE_ACCOUNT";
1761 case CONNECTING:
1762 return "CONNECTING";
1763 case BLOCKED:
1764 return "BLOCKED";
1765 default:
1766 return "UNKNOWN";
1767 }
1768 }
1769 }
1770
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001771 /** Camera direction constants */
1772 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001773 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1774 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1775 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001776 }
1777
1778 /**
1779 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1780 * existing call member variables, but this duplication helps to ensure that none of these logging
1781 * variables will interface with/and affect call logic.
1782 */
1783 public static class LogState {
1784
1785 public DisconnectCause disconnectCause;
1786 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001787 public ContactLookupResult.Type contactLookupResult =
1788 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001789 public CallSpecificAppData callSpecificAppData;
1790 // If this was a conference call, the total number of calls involved in the conference.
1791 public int conferencedCalls = 0;
1792 public long duration = 0;
1793 public boolean isLogged = false;
1794
Eric Erfanian8369df02017-05-03 10:27:13 -07001795 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001796 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001797 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001798 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001799 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001800 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001801 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001802 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001803 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001804 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001805 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001806 return "Voicemail";
1807 default:
1808 return "Not found";
1809 }
1810 }
1811
1812 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1813 if (callSpecificAppData == null) {
1814 return "null";
1815 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001816 switch (callSpecificAppData.getCallInitiationType()) {
1817 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001818 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001819 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001820 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001821 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001822 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001823 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001824 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001825 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001826 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001827 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001828 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001829 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001830 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001831 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001832 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001833 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001834 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001835 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001836 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001837 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001838 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001839 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001840 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001841 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001842 return "Launcher Shortcut";
1843 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001844 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001845 }
1846 }
1847
1848 @Override
1849 public String toString() {
1850 return String.format(
1851 Locale.US,
1852 "["
1853 + "%s, " // DisconnectCause toString already describes the object type
1854 + "isIncoming: %s, "
1855 + "contactLookup: %s, "
1856 + "callInitiation: %s, "
1857 + "duration: %s"
1858 + "]",
1859 disconnectCause,
1860 isIncoming,
1861 lookupToString(contactLookupResult),
1862 initiationToString(callSpecificAppData),
1863 duration);
1864 }
1865 }
1866
roldenburgd7490db2018-01-09 13:51:29 -08001867 /** Coordinates the available VideoTech implementations for a call. */
1868 @VisibleForTesting
1869 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001870 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001871 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001872 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001873 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001874 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001875
roldenburgd7490db2018-01-09 13:51:29 -08001876 @VisibleForTesting
1877 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001878 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001879
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001880 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001881 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001882 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001883
1884 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001885 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001886
linyuh183cb712017-12-27 17:02:37 -08001887 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001888
roldenburgd7490db2018-01-09 13:51:29 -08001889 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001890 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001891 .getRcsVideoShareFactory()
1892 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001893 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001894 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001895 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001896 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001897
1898 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001899 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001900 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001901
1902 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001903 }
1904
roldenburgd7490db2018-01-09 13:51:29 -08001905 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001906 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001907 if (savedTech == emptyVideoTech) {
1908 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001909 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001910 savedTech = tech;
1911 savedTech.becomePrimary();
1912 break;
1913 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001914 }
roldenburg6bd612f2018-01-18 12:57:19 -08001915 } else if (savedTech instanceof DuoVideoTech
1916 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001917 // RCS Video Share will become available after the capability exchange which is slower than
1918 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1919 // not in the middle of any session changes, let it take over.
1920 savedTech = rcsVideoShare;
1921 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001922 }
1923
roldenburgd7490db2018-01-09 13:51:29 -08001924 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001925 }
1926
roldenburgd7490db2018-01-09 13:51:29 -08001927 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001928 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001929 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001930 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001931 }
1932 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001933
1934 void dispatchRemovedFromCallList() {
1935 for (VideoTech videoTech : videoTechs) {
1936 videoTech.onRemovedFromCallList();
1937 }
1938 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001939 }
1940
Eric Erfanianccca3152017-02-22 16:32:36 -08001941 /** Called when canned text responses have been loaded. */
1942 public interface CannedTextResponsesLoadedListener {
1943 void onCannedTextResponsesLoaded(DialerCall call);
1944 }
1945}