blob: 9dfe7abcb1822c8b0bfbfb9a0c600a2db6c67fa6 [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;
195
Eric Erfanianccca3152017-02-22 16:32:36 -0800196 public static String getNumberFromHandle(Uri handle) {
197 return handle == null ? "" : handle.getSchemeSpecificPart();
198 }
199
200 /**
201 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700202 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800203 */
204 private boolean isRemotelyHeld;
205
Eric Erfanian2ca43182017-08-31 06:57:16 -0700206 /** Indicates whether this call is currently in the process of being merged into a conference. */
207 private boolean isMergeInProcess;
208
Eric Erfanianccca3152017-02-22 16:32:36 -0800209 /**
210 * Indicates whether the phone account associated with this call supports specifying a call
211 * subject.
212 */
linyuh183cb712017-12-27 17:02:37 -0800213 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800214
wangqied677ef2018-04-04 12:03:00 -0700215 public RttTranscript getRttTranscript() {
216 return rttTranscript;
217 }
218
219 public void setRttTranscript(RttTranscript rttTranscript) {
220 this.rttTranscript = rttTranscript;
221 }
222
223 private RttTranscript rttTranscript;
224
linyuh183cb712017-12-27 17:02:37 -0800225 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800226 new Call.Callback() {
227 @Override
228 public void onStateChanged(Call call, int newState) {
229 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
230 update();
231 }
232
233 @Override
234 public void onParentChanged(Call call, Call newParent) {
235 LogUtil.v(
236 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
237 update();
238 }
239
240 @Override
241 public void onChildrenChanged(Call call, List<Call> children) {
242 update();
243 }
244
245 @Override
246 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700247 LogUtil.v(
248 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800249 update();
250 }
251
252 @Override
253 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
254 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700255 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800256 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800257 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800258 listener.onCannedTextResponsesLoaded(DialerCall.this);
259 }
260 }
261
262 @Override
263 public void onPostDialWait(Call call, String remainingPostDialSequence) {
264 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700265 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800266 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
267 update();
268 }
269
270 @Override
271 public void onVideoCallChanged(Call call, VideoCall videoCall) {
272 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700273 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800274 update();
275 }
276
277 @Override
278 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700279 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700280 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800281 }
282
283 @Override
284 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
285 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700286 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800287 "call %s, conferenceable calls: %d",
288 call,
289 conferenceableCalls.size());
290 update();
291 }
292
293 @Override
wangqi219b8702018-02-13 09:34:41 -0800294 public void onRttModeChanged(Call call, int mode) {
295 LogUtil.v("TelecomCallCallback.onRttModeChanged", "mode=%d", mode);
296 }
297
298 @Override
299 public void onRttRequest(Call call, int id) {
300 LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id);
wangqibc28ea72018-04-02 16:23:00 -0700301 for (DialerCallListener listener : listeners) {
302 listener.onDialerCallUpgradeToRtt(id);
303 }
wangqi219b8702018-02-13 09:34:41 -0800304 }
305
306 @Override
307 public void onRttInitiationFailure(Call call, int reason) {
308 LogUtil.v("TelecomCallCallback.onRttInitiationFailure", "reason=%d", reason);
309 update();
310 }
311
312 @Override
313 public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {
314 LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled);
wangqi81e373e2018-04-24 10:50:57 -0700315 if (enabled) {
316 Logger.get(context)
317 .logCallImpression(
318 DialerImpression.Type.RTT_MID_CALL_ENABLED,
319 getUniqueCallId(),
320 getTimeAddedMs());
321 }
wangqi219b8702018-02-13 09:34:41 -0800322 update();
323 }
324
325 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800326 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
327 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700328 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800329 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
330 switch (event) {
331 // The Previous attempt to Merge two calls together has failed in Telecom. We must
332 // now update the UI to possibly re-enable the Merge button based on the number of
333 // currently conferenceable calls available or Connection Capabilities.
334 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
335 update();
336 break;
337 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
338 notifyWiFiToLteHandover();
339 break;
roldenburg08424ee2018-03-28 17:02:46 -0700340 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI:
341 onLteToWifiHandover();
342 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800343 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
344 notifyHandoverToWifiFailed();
345 break;
346 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
347 isRemotelyHeld = true;
348 update();
349 break;
350 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
351 isRemotelyHeld = false;
352 update();
353 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700354 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
355 notifyInternationalCallOnWifi();
356 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700357 case TelephonyManagerCompat.EVENT_MERGE_START:
358 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
359 isMergeInProcess = true;
360 break;
361 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
362 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
363 isMergeInProcess = false;
364 break;
wangqif4ba3452018-01-09 11:26:29 -0800365 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
366 // Only handle this event for P+ since it's unreliable pre-P.
367 if (BuildCompat.isAtLeastP()) {
368 isCallForwarded = true;
369 update();
370 }
371 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800372 default:
373 break;
374 }
375 }
376 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700377
linyuh183cb712017-12-27 17:02:37 -0800378 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800379
380 public DialerCall(
381 Context context,
382 DialerCallDelegate dialerCallDelegate,
383 Call telecomCall,
384 LatencyReport latencyReport,
385 boolean registerCallback) {
386 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800387 this.context = context;
388 this.dialerCallDelegate = dialerCallDelegate;
389 this.telecomCall = telecomCall;
390 this.latencyReport = latencyReport;
391 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800392
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700393 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800394 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700395
396 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700397 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800398 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700399 } else {
linyuh183cb712017-12-27 17:02:37 -0800400 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700401 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800402
403 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800404 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800405 }
406
linyuh183cb712017-12-27 17:02:37 -0800407 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800408 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700409
410 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800411 }
412
twyendde01c52017-09-22 10:07:31 -0700413 /** Test only constructor to avoid initializing dependencies. */
414 @VisibleForTesting
415 DialerCall(Context context) {
linyuh183cb712017-12-27 17:02:37 -0800416 this.context = context;
417 telecomCall = null;
418 latencyReport = null;
419 id = null;
420 hiddenId = 0;
421 dialerCallDelegate = null;
422 videoTechManager = null;
twyendde01c52017-09-22 10:07:31 -0700423 }
424
Eric Erfanianccca3152017-02-22 16:32:36 -0800425 private static int translateState(int state) {
426 switch (state) {
427 case Call.STATE_NEW:
428 case Call.STATE_CONNECTING:
429 return DialerCall.State.CONNECTING;
430 case Call.STATE_SELECT_PHONE_ACCOUNT:
431 return DialerCall.State.SELECT_PHONE_ACCOUNT;
432 case Call.STATE_DIALING:
433 return DialerCall.State.DIALING;
434 case Call.STATE_PULLING_CALL:
435 return DialerCall.State.PULLING;
436 case Call.STATE_RINGING:
437 return DialerCall.State.INCOMING;
438 case Call.STATE_ACTIVE:
439 return DialerCall.State.ACTIVE;
440 case Call.STATE_HOLDING:
441 return DialerCall.State.ONHOLD;
442 case Call.STATE_DISCONNECTED:
443 return DialerCall.State.DISCONNECTED;
444 case Call.STATE_DISCONNECTING:
445 return DialerCall.State.DISCONNECTING;
446 default:
447 return DialerCall.State.INVALID;
448 }
449 }
450
451 public static boolean areSame(DialerCall call1, DialerCall call2) {
452 if (call1 == null && call2 == null) {
453 return true;
454 } else if (call1 == null || call2 == null) {
455 return false;
456 }
457
458 // otherwise compare call Ids
459 return call1.getId().equals(call2.getId());
460 }
461
Eric Erfanianccca3152017-02-22 16:32:36 -0800462 public void addListener(DialerCallListener listener) {
463 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800464 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800465 }
466
467 public void removeListener(DialerCallListener listener) {
468 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800469 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800470 }
471
472 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
473 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800474 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800475 }
476
477 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
478 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800479 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800480 }
481
roldenburg08424ee2018-03-28 17:02:46 -0700482 private void onLteToWifiHandover() {
483 LogUtil.enterBlock("DialerCall.onLteToWifiHandover");
484 if (hasShownLteToWiFiHandoverToast) {
485 return;
486 }
487
488 Toast.makeText(context, R.string.video_call_lte_to_wifi_handover_toast, Toast.LENGTH_LONG)
489 .show();
490 hasShownLteToWiFiHandoverToast = true;
491 }
492
Eric Erfanianccca3152017-02-22 16:32:36 -0800493 public void notifyWiFiToLteHandover() {
494 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800495 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800496 listener.onWiFiToLteHandover();
497 }
498 }
499
500 public void notifyHandoverToWifiFailed() {
501 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800502 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800503 listener.onHandoverToWifiFailure();
504 }
505 }
506
Eric Erfanianc857f902017-05-15 14:05:33 -0700507 public void notifyInternationalCallOnWifi() {
508 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800509 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700510 dialerCallListener.onInternationalCallOnWifi();
511 }
512 }
513
Eric Erfanianccca3152017-02-22 16:32:36 -0800514 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800515 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800516 }
wangqida410d32018-03-06 16:51:38 -0800517
Eric Erfanianccca3152017-02-22 16:32:36 -0800518 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800519 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800520 }
521
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700522 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800523 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700524 }
525
526 public void setCameraDir(int cameraDir) {
527 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
528 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800529 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700530 } else {
linyuh183cb712017-12-27 17:02:37 -0800531 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700532 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800533 }
534
Eric Erfanian2ca43182017-08-31 06:57:16 -0700535 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800536 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700537 }
538
wangqi9982f0d2017-10-11 17:46:07 -0700539 public boolean isVoiceMailNumber() {
540 return isVoicemailNumber;
541 }
542
543 public List<PhoneAccountHandle> getCallCapableAccounts() {
544 return callCapableAccounts;
545 }
546
547 public String getCountryIso() {
548 return countryIso;
549 }
550
551 private void updateIsVoiceMailNumber() {
552 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
553 isVoicemailNumber = true;
roldenburg37a969d2018-02-22 14:46:44 -0800554 return;
wangqi9982f0d2017-10-11 17:46:07 -0700555 }
556
linyuh183cb712017-12-27 17:02:37 -0800557 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700558 isVoicemailNumber = false;
roldenburg37a969d2018-02-22 14:46:44 -0800559 return;
wangqi9982f0d2017-10-11 17:46:07 -0700560 }
561
linyuh183cb712017-12-27 17:02:37 -0800562 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700563 }
564
Eric Erfanianccca3152017-02-22 16:32:36 -0800565 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700566 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800567 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700568 // Clear any cache here that could potentially change on update.
569 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800570 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700571 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800572 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800573 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800574 listener.onDialerCallDisconnect();
575 }
linyuh183cb712017-12-27 17:02:37 -0800576 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700577 .getEnrichedCallManager()
578 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800579 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700580 .getEnrichedCallManager()
581 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800582 } else {
linyuh183cb712017-12-27 17:02:37 -0800583 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800584 listener.onDialerCallUpdate();
585 }
586 }
587 Trace.endSection();
588 }
589
wangqi9982f0d2017-10-11 17:46:07 -0700590 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700591 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700592 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800593 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700594
roldenburg6bd612f2018-01-18 12:57:19 -0800595 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700596
linyuh183cb712017-12-27 17:02:37 -0800597 final int translatedState = translateState(telecomCall.getState());
598 if (state != State.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800599 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800600 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800601 }
602
linyuh183cb712017-12-27 17:02:37 -0800603 childCallIds.clear();
604 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800605 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800606 childCallIds.add(
607 dialerCallDelegate
608 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800609 .getId());
610 }
611
612 // The number of conferenced calls can change over the course of the call, so use the
613 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800614 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800615
linyuh183cb712017-12-27 17:02:37 -0800616 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800617
618 // If the handle of the call has changed, update state for the call determining if it is an
619 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800620 Uri newHandle = telecomCall.getDetails().getHandle();
621 if (!Objects.equals(handle, newHandle)) {
622 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800623 updateEmergencyCallState();
624 }
625
linyuh183cb712017-12-27 17:02:37 -0800626 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800627 // If the phone account handle of the call is set, cache capability bit indicating whether
628 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800629 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
630 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
631 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800632
linyuh183cb712017-12-27 17:02:37 -0800633 if (phoneAccountHandle != null) {
634 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800635 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800636 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800637 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
wangqida410d32018-03-06 16:51:38 -0800638 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
639 cacheCarrierConfiguration(phoneAccountHandle);
640 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800641 }
642 }
643 }
linyuh183cb712017-12-27 17:02:37 -0800644 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700645 updateIsVoiceMailNumber();
646 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800647 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700648 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700649 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800650 }
651
652 /**
wangqida410d32018-03-06 16:51:38 -0800653 * Caches frequently used carrier configuration locally.
654 *
655 * @param accountHandle The PhoneAccount handle.
656 */
657 @SuppressLint("MissingPermission")
658 private void cacheCarrierConfiguration(PhoneAccountHandle accountHandle) {
659 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
660 return;
661 }
662 if (VERSION.SDK_INT < VERSION_CODES.O) {
663 return;
664 }
665 // TODO(a bug): This may take several seconds to complete, revisit it to move it to worker
666 // thread.
667 carrierConfig =
668 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, accountHandle)
669 .getCarrierConfig();
670 }
671
672 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800673 * Tests corruption of the {@code callExtras} bundle by calling {@link
674 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
675 * be thrown and caught by this function.
676 *
677 * @param callExtras the bundle to verify
678 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
679 */
680 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
681 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700682 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800683 * bundle, resulting in a IllegalArgumentException while validating data under {@link
684 * Bundle#containsKey(String)}.
685 */
686 try {
687 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
688 return false;
689 } catch (IllegalArgumentException e) {
690 LogUtil.e(
691 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
692 return true;
693 }
694 }
695
696 protected void updateFromCallExtras(Bundle callExtras) {
697 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
698 /**
699 * If the bundle is corrupted, abandon information update as a work around. These are not
700 * critical for the dialer to function.
701 */
702 return;
703 }
704 // Check for a change in the child address and notify any listeners.
705 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
706 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800707 if (!Objects.equals(childNumber, this.childNumber)) {
708 this.childNumber = childNumber;
709 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800710 listener.onDialerCallChildNumberChange();
711 }
712 }
713 }
714
715 // Last forwarded number comes in as an array of strings. We want to choose the
716 // last item in the array. The forwarding numbers arrive independently of when the
717 // call is originally set up, so we need to notify the the UI of the change.
718 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
719 ArrayList<String> lastForwardedNumbers =
720 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
721
722 if (lastForwardedNumbers != null) {
723 String lastForwardedNumber = null;
724 if (!lastForwardedNumbers.isEmpty()) {
725 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
726 }
727
linyuh183cb712017-12-27 17:02:37 -0800728 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
729 this.lastForwardedNumber = lastForwardedNumber;
730 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800731 listener.onDialerCallLastForwardedNumberChange();
732 }
733 }
734 }
735 }
736
737 // DialerCall subject is present in the extras at the start of call, so we do not need to
738 // notify any other listeners of this.
739 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
740 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800741 if (!Objects.equals(this.callSubject, callSubject)) {
742 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800743 }
744 }
745 }
746
Eric Erfanianccca3152017-02-22 16:32:36 -0800747 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800748 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800749 }
750
Eric Erfanianc857f902017-05-15 14:05:33 -0700751 /**
752 * @return name appended with a number if the number is restricted/unknown and the user has
753 * received more than one restricted/unknown call.
754 */
755 @Nullable
756 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800757 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
758 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700759 }
760 return name;
761 }
762
763 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800764 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700765 }
766
767 private boolean isHiddenNumber() {
768 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
769 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
770 }
771
Eric Erfanianccca3152017-02-22 16:32:36 -0800772 public boolean hasShownWiFiToLteHandoverToast() {
773 return hasShownWiFiToLteHandoverToast;
774 }
775
776 public void setHasShownWiFiToLteHandoverToast() {
777 hasShownWiFiToLteHandoverToast = true;
778 }
779
780 public boolean showWifiHandoverAlertAsToast() {
781 return doNotShowDialogForHandoffToWifiFailure;
782 }
783
784 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
785 doNotShowDialogForHandoffToWifiFailure = bool;
786 }
787
wangqida410d32018-03-06 16:51:38 -0800788 public boolean showVideoChargesAlertDialog() {
789 if (carrierConfig == null) {
790 return false;
791 }
792 return carrierConfig.getBoolean(
793 TelephonyManagerCompat.CARRIER_CONFIG_KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL);
794 }
795
Eric Erfanianccca3152017-02-22 16:32:36 -0800796 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800797 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800798 }
799
800 @Nullable
801 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800802 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800803 }
804
805 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800806 telecomCall.reject(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800807 setState(State.BLOCKED);
808 }
809
810 @Nullable
811 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800812 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800813 }
814
815 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800816 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800817 }
818
819 public boolean isPotentialEmergencyCallback() {
820 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
821 // is actually in emergency callback mode (ie data is disabled).
822 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
823 return true;
824 }
825 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
826 // as a potential emergency callback.
827 if (getExtras() != null
828 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
829 > 0) {
830 long lastEmergencyCallMillis =
831 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
832 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
833 return true;
834 }
835 }
836 return false;
837 }
838
839 boolean isInEmergencyCallbackWindow(long timestampMillis) {
840 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800841 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800842 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
843 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
844 }
845
846 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800847 if (telecomCall != null && telecomCall.getParent() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800848 return State.CONFERENCED;
849 } else {
linyuh183cb712017-12-27 17:02:37 -0800850 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800851 }
852 }
853
yueg265089a2017-10-06 14:35:15 -0700854 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800855 return state;
yueg265089a2017-10-06 14:35:15 -0700856 }
857
Eric Erfanianccca3152017-02-22 16:32:36 -0800858 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700859 if (state == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800860 logState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700861 } else if (state == State.DISCONNECTED) {
862 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800863 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
linyuh183cb712017-12-27 17:02:37 -0800864 if (this.state != state) {
865 logState.duration = newDuration;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700866 } else {
867 LogUtil.i(
868 "DialerCall.setState",
869 "ignoring state transition from DISCONNECTED to DISCONNECTED."
870 + " Duration would have changed from %s to %s",
linyuh183cb712017-12-27 17:02:37 -0800871 logState.duration,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700872 newDuration);
873 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800874 }
linyuh183cb712017-12-27 17:02:37 -0800875 this.state = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800876 }
877
878 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800879 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800880 }
881
882 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800883 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800884 }
885
886 @Nullable
887 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800888 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800889 }
890
891 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800892 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800893 }
894
895 @Nullable
896 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800897 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800898 }
899
900 /** @return The child number for the call, or {@code null} if none specified. */
901 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800902 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800903 }
904
905 /** @return The last forwarded number for the call, or {@code null} if none specified. */
906 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800907 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800908 }
909
wangqif4ba3452018-01-09 11:26:29 -0800910 public boolean isCallForwarded() {
911 return isCallForwarded;
912 }
913
Eric Erfanianccca3152017-02-22 16:32:36 -0800914 /** @return The call subject, or {@code null} if none specified. */
915 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800916 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800917 }
918
919 /**
920 * @return {@code true} if the call's phone account supports call subjects, {@code false}
921 * otherwise.
922 */
923 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800924 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800925 }
926
927 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
928 public DisconnectCause getDisconnectCause() {
linyuh183cb712017-12-27 17:02:37 -0800929 if (state == State.DISCONNECTED || state == State.IDLE) {
930 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800931 }
932
933 return new DisconnectCause(DisconnectCause.UNKNOWN);
934 }
935
936 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800937 this.disconnectCause = disconnectCause;
938 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800939 }
940
941 /** Returns the possible text message responses. */
942 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800943 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800944 }
945
946 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
947 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800948 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800949
950 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
951 // We allow you to merge if the capabilities allow it or if it is a call with
952 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800953 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800954 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
955 // Cannot merge calls if there are no calls to merge with.
956 return false;
957 }
958 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
959 }
960 return (capabilities == (capabilities & supportedCapabilities));
961 }
962
963 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800964 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800965 }
966
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700967 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800968 public String getUniqueCallId() {
969 return uniqueCallId;
970 }
971
972 /** Gets the time when the call first became active. */
973 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800974 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800975 }
976
wangqied677ef2018-04-04 12:03:00 -0700977 /**
978 * Gets the time when the call is created (see {@link Details#getCreationTimeMillis()}). This is
979 * the same time that is logged as the start time in the Call Log (see {@link
980 * android.provider.CallLog.Calls#DATE}).
981 */
982 @TargetApi(26)
983 public long getCreationTimeMillis() {
984 return telecomCall.getDetails().getCreationTimeMillis();
985 }
986
Eric Erfanianccca3152017-02-22 16:32:36 -0800987 public boolean isConferenceCall() {
988 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
989 }
990
991 @Nullable
992 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -0800993 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -0800994 }
995
996 @Nullable
997 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -0800998 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800999 }
1000
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001001 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -08001002 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -08001003 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -08001004 }
1005
1006 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -08001007 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -08001008 }
1009
1010 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -08001011 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -08001012 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -08001013 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001014 }
1015 return null;
1016 }
1017
1018 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -08001019 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -08001020 }
1021
1022 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -07001023 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001024 }
1025
wangqi8d407a02018-02-15 15:32:52 -08001026 @TargetApi(28)
wangqif6be6172018-03-30 15:57:56 -07001027 public boolean isActiveRttCall() {
wangqi219b8702018-02-13 09:34:41 -08001028 if (BuildCompat.isAtLeastP()) {
1029 return getTelecomCall().isRttActive();
1030 } else {
1031 return false;
1032 }
1033 }
1034
wangqi153af2f2018-02-15 16:21:49 -08001035 @TargetApi(28)
1036 public RttCall getRttCall() {
wangqif6be6172018-03-30 15:57:56 -07001037 if (!isActiveRttCall()) {
wangqi153af2f2018-02-15 16:21:49 -08001038 return null;
1039 }
1040 return getTelecomCall().getRttCall();
1041 }
1042
wangqif6be6172018-03-30 15:57:56 -07001043 @TargetApi(28)
1044 public boolean canUpgradeToRttCall() {
1045 PhoneAccount phoneAccount = getPhoneAccount();
1046 if (phoneAccount == null) {
1047 return false;
1048 }
1049 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
1050 return false;
1051 }
1052 if (isActiveRttCall()) {
1053 return false;
1054 }
1055 if (isVideoCall()) {
1056 return false;
1057 }
1058 if (isConferenceCall()) {
1059 return false;
1060 }
1061 if (CallList.getInstance().hasActiveRttCall()) {
1062 return false;
1063 }
1064 return true;
1065 }
1066
1067 @TargetApi(28)
1068 public void sendRttUpgradeRequest() {
1069 getTelecomCall().sendRttRequest();
1070 }
1071
wangqibc28ea72018-04-02 16:23:00 -07001072 @TargetApi(28)
1073 public void respondToRttRequest(boolean accept, int rttRequestId) {
wangqi81e373e2018-04-24 10:50:57 -07001074 Logger.get(context)
1075 .logCallImpression(
1076 accept
1077 ? DialerImpression.Type.RTT_MID_CALL_ACCEPTED
1078 : DialerImpression.Type.RTT_MID_CALL_REJECTED,
1079 getUniqueCallId(),
1080 getTimeAddedMs());
wangqibc28ea72018-04-02 16:23:00 -07001081 getTelecomCall().respondToRttRequest(rttRequestId, accept);
1082 }
1083
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001084 public boolean hasReceivedVideoUpgradeRequest() {
1085 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
1086 }
1087
1088 public boolean hasSentVideoUpgradeRequest() {
1089 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -08001090 }
1091
wangqi219b8702018-02-13 09:34:41 -08001092 public boolean hasSentRttUpgradeRequest() {
wangqi219b8702018-02-13 09:34:41 -08001093 return false;
1094 }
1095
Eric Erfanianccca3152017-02-22 16:32:36 -08001096 /**
1097 * Determines if the call handle is an emergency number or not and caches the result to avoid
1098 * repeated calls to isEmergencyNumber.
1099 */
1100 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -08001101 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -08001102 }
1103
Eric Erfanianccca3152017-02-22 16:32:36 -08001104 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -08001105 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -08001106 }
1107
1108 /**
1109 * Determines if the call is an external call.
1110 *
1111 * <p>An external call is one which does not exist locally for the {@link
1112 * android.telecom.ConnectionService} it is associated with.
1113 *
Eric Erfanianccca3152017-02-22 16:32:36 -08001114 * @return {@code true} if the call is an external call, {@code false} otherwise.
1115 */
linyuh437ae952018-03-26 12:46:18 -07001116 boolean isExternalCall() {
1117 return hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
Eric Erfanianccca3152017-02-22 16:32:36 -08001118 }
1119
1120 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001121 * Determines if answering this call will cause an ongoing video call to be dropped.
1122 *
1123 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
1124 * otherwise.
1125 */
1126 public boolean answeringDisconnectsForegroundVideoCall() {
1127 Bundle extras = getExtras();
1128 if (extras == null
1129 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
1130 return false;
1131 }
1132 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
1133 }
1134
1135 private void parseCallSpecificAppData() {
1136 if (isExternalCall()) {
1137 return;
1138 }
1139
linyuh183cb712017-12-27 17:02:37 -08001140 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
1141 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001142
linyuh183cb712017-12-27 17:02:37 -08001143 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -07001144 CallSpecificAppData.newBuilder()
1145 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
1146 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001147 }
1148 if (getState() == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -08001149 logState.callSpecificAppData =
1150 logState
Eric Erfanian8369df02017-05-03 10:27:13 -07001151 .callSpecificAppData
1152 .toBuilder()
1153 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
1154 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001155 }
1156 }
1157
1158 @Override
1159 public String toString() {
linyuh183cb712017-12-27 17:02:37 -08001160 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001161 // This should happen only in testing since otherwise we would never have a null
1162 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -08001163 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -08001164 }
1165
1166 return String.format(
1167 Locale.US,
1168 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001169 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001170 id,
Eric Erfanianccca3152017-02-22 16:32:36 -08001171 State.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001172 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1173 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1174 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001175 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001176 this.telecomCall.getConferenceableCalls(),
1177 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001178 getVideoTech().getSessionModificationState(),
1179 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001180 }
1181
1182 public String toSimpleString() {
1183 return super.toString();
1184 }
1185
1186 @CallHistoryStatus
1187 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001188 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001189 }
1190
1191 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001192 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001193 }
1194
1195 public boolean didShowCameraPermission() {
1196 return didShowCameraPermission;
1197 }
1198
1199 public void setDidShowCameraPermission(boolean didShow) {
1200 didShowCameraPermission = didShow;
1201 }
1202
wangqida410d32018-03-06 16:51:38 -08001203 public boolean didDismissVideoChargesAlertDialog() {
1204 return didDismissVideoChargesAlertDialog;
1205 }
1206
1207 public void setDidDismissVideoChargesAlertDialog(boolean didDismiss) {
1208 didDismissVideoChargesAlertDialog = didDismiss;
1209 }
1210
Eric Erfanian938468d2017-10-24 14:05:52 -07001211 @Nullable
1212 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001213 return isInGlobalSpamList;
1214 }
1215
1216 public void setIsInGlobalSpamList(boolean inSpamList) {
1217 isInGlobalSpamList = inSpamList;
1218 }
1219
Eric Erfanian938468d2017-10-24 14:05:52 -07001220 @Nullable
1221 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001222 return isInUserSpamList;
1223 }
1224
1225 public void setIsInUserSpamList(boolean inSpamList) {
1226 isInUserSpamList = inSpamList;
1227 }
1228
Eric Erfanian938468d2017-10-24 14:05:52 -07001229 @Nullable
1230 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001231 return isInUserWhiteList;
1232 }
1233
1234 public void setIsInUserWhiteList(boolean inWhiteList) {
1235 isInUserWhiteList = inWhiteList;
1236 }
1237
1238 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001239 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001240 }
1241
1242 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001243 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001244 }
1245
1246 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001247 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001248 }
1249
1250 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001251 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001252 }
1253
1254 public boolean isRemotelyHeld() {
1255 return isRemotelyHeld;
1256 }
1257
Eric Erfanian2ca43182017-08-31 06:57:16 -07001258 public boolean isMergeInProcess() {
1259 return isMergeInProcess;
1260 }
1261
Eric Erfanianccca3152017-02-22 16:32:36 -08001262 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001263 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001264 }
1265
erfanian2cf2c342017-12-21 12:01:33 -08001266 /**
1267 * Try and determine if the call used assisted dialing.
1268 *
1269 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1270 * implmentation is complete in P+.
1271 *
1272 * @return a boolean indicating assisted dialing may have been performed
1273 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001274 public boolean isAssistedDialed() {
1275 if (getIntentExtras() != null) {
erfaniand2e5d0b2018-03-02 14:54:35 -08001276 // P and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
erfanian2cf2c342017-12-21 12:01:33 -08001277 // was used. The Dialer client is responsible for performing assisted dialing before
1278 // placing the outgoing call.
1279 //
1280 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1281 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1282 && getAssistedDialingExtras() != null
1283 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1284 return true;
1285 }
1286 }
1287
Eric Erfanian2ca43182017-08-31 06:57:16 -07001288 return false;
1289 }
1290
erfanian2cf2c342017-12-21 12:01:33 -08001291 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001292 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001293 if (getIntentExtras() == null) {
1294 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001295 }
erfanian2cf2c342017-12-21 12:01:33 -08001296
1297 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1298 return null;
1299 }
1300
erfanianf2556612018-01-23 09:55:59 -08001301 // Used in N-OMR1
erfanian2cf2c342017-12-21 12:01:33 -08001302 return TransformationInfo.newInstanceFromBundle(
1303 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001304 }
1305
Eric Erfanianccca3152017-02-22 16:32:36 -08001306 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001307 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001308 }
1309
Eric Erfanian2ca43182017-08-31 06:57:16 -07001310 public int getAnswerAndReleaseButtonDisplayedTimes() {
1311 return answerAndReleaseButtonDisplayedTimes;
1312 }
1313
1314 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1315 answerAndReleaseButtonDisplayedTimes++;
1316 }
1317
1318 public boolean getReleasedByAnsweringSecondCall() {
1319 return releasedByAnsweringSecondCall;
1320 }
1321
1322 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1323 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1324 }
1325
1326 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1327 return secondCallWithoutAnswerAndReleasedButtonTimes;
1328 }
1329
1330 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1331 secondCallWithoutAnswerAndReleasedButtonTimes++;
1332 }
1333
Eric Erfanian8369df02017-05-03 10:27:13 -07001334 @Nullable
1335 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001336 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001337 }
1338
1339 public void setEnrichedCallCapabilities(
1340 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001341 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001342 }
1343
1344 @Nullable
1345 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001346 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001347 }
1348
1349 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001350 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001351 }
1352
Eric Erfanianccca3152017-02-22 16:32:36 -08001353 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001354 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001355 }
1356
Eric Erfanianccca3152017-02-22 16:32:36 -08001357 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1358 LogUtil.i(
1359 "DialerCall.phoneAccountSelected",
1360 "accountHandle: %s, setDefault: %b",
1361 accountHandle,
1362 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001363 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001364 }
1365
1366 public void disconnect() {
1367 LogUtil.i("DialerCall.disconnect", "");
1368 setState(DialerCall.State.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001369 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001370 listener.onDialerCallUpdate();
1371 }
linyuh183cb712017-12-27 17:02:37 -08001372 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001373 }
1374
1375 public void hold() {
1376 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001377 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001378 }
1379
1380 public void unhold() {
1381 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001382 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001383 }
1384
1385 public void splitFromConference() {
1386 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001387 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001388 }
1389
1390 public void answer(int videoState) {
1391 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001392 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001393 }
1394
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001395 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001396 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001397 }
1398
Eric Erfanianccca3152017-02-22 16:32:36 -08001399 public void reject(boolean rejectWithMessage, String message) {
1400 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001401 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001402 }
1403
1404 /** Return the string label to represent the call provider */
1405 public String getCallProviderLabel() {
1406 if (callProviderLabel == null) {
1407 PhoneAccount account = getPhoneAccount();
1408 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001409 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001410 callProviderLabel = account.getLabel().toString();
1411 }
1412 }
1413 if (callProviderLabel == null) {
1414 callProviderLabel = "";
1415 }
1416 }
1417 return callProviderLabel;
1418 }
1419
1420 private PhoneAccount getPhoneAccount() {
1421 PhoneAccountHandle accountHandle = getAccountHandle();
1422 if (accountHandle == null) {
1423 return null;
1424 }
linyuh183cb712017-12-27 17:02:37 -08001425 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001426 }
1427
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001428 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001429 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001430 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001431
1432 // Only store the first video tech type found to be available during the life of the call.
1433 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1434 // Update the video tech.
1435 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1436 }
1437 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001438 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001439 }
1440
Eric Erfanianccca3152017-02-22 16:32:36 -08001441 public String getCallbackNumber() {
1442 if (callbackNumber == null) {
1443 // Show the emergency callback number if either:
1444 // 1. This is an emergency call.
1445 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1446 // number.
1447 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1448
1449 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001450 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001451 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001452 }
1453
Eric Erfanianccca3152017-02-22 16:32:36 -08001454 if (callbackNumber == null) {
1455 callbackNumber = "";
1456 }
1457 }
1458 return callbackNumber;
1459 }
1460
wangqi97539352017-09-25 11:15:16 -07001461 public String getSimCountryIso() {
1462 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001463 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001464 .getSimCountryIso();
1465 if (!TextUtils.isEmpty(simCountryIso)) {
1466 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1467 }
1468 return simCountryIso;
1469 }
1470
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001471 @Override
1472 public void onVideoTechStateChanged() {
1473 update();
1474 }
1475
1476 @Override
1477 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001478 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001479 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001480 listener.onDialerCallSessionModificationStateChange();
1481 }
wangqi9982f0d2017-10-11 17:46:07 -07001482 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001483 }
1484
1485 @Override
1486 public void onCameraDimensionsChanged(int width, int height) {
1487 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1488 }
1489
1490 @Override
1491 public void onPeerDimensionsChanged(int width, int height) {
1492 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1493 }
1494
1495 @Override
1496 public void onVideoUpgradeRequestReceived() {
1497 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1498
linyuh183cb712017-12-27 17:02:37 -08001499 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001500 listener.onDialerCallUpgradeToVideo();
1501 }
1502
1503 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001504
linyuh183cb712017-12-27 17:02:37 -08001505 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001506 .logCallImpression(
1507 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1508 }
1509
1510 @Override
1511 public void onUpgradedToVideo(boolean switchToSpeaker) {
1512 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1513
1514 if (!switchToSpeaker) {
1515 return;
1516 }
1517
1518 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1519
1520 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1521 LogUtil.e(
1522 "DialerCall.onUpgradedToVideo",
1523 "toggling speakerphone not allowed when bluetooth supported.");
1524 return;
1525 }
1526
1527 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1528 return;
1529 }
1530
1531 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001532 }
1533
Eric Erfanian2ca43182017-08-31 06:57:16 -07001534 @Override
1535 public void onCapabilitiesUpdated() {
1536 if (getNumber() == null) {
1537 return;
1538 }
1539 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001540 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001541 if (capabilities != null) {
1542 setEnrichedCallCapabilities(capabilities);
1543 update();
1544 }
1545 }
1546
1547 @Override
1548 public void onEnrichedCallStateChanged() {
1549 updateEnrichedCallSession();
1550 }
1551
1552 @Override
1553 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001554 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001555 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1556 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001557 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001558 .logCallImpression(
1559 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1560 getUniqueCallId(),
1561 getTimeAddedMs());
1562 }
1563 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001564 }
1565
1566 private void updateEnrichedCallSession() {
1567 if (getNumber() == null) {
1568 return;
1569 }
1570 if (getEnrichedCallSession() != null) {
1571 // State changes to existing sessions are currently handled by the UI components (which have
1572 // their own listeners). Someday instead we could remove those and just call update() here and
1573 // have the usual onDialerCallUpdate update the UI.
1574 dispatchOnEnrichedCallSessionUpdate();
1575 return;
1576 }
1577
linyuh183cb712017-12-27 17:02:37 -08001578 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001579
1580 Filter filter =
1581 isIncoming()
1582 ? manager.createIncomingCallComposerFilter()
1583 : manager.createOutgoingCallComposerFilter();
1584
1585 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1586 if (session == null) {
1587 return;
1588 }
1589
1590 session.setUniqueDialerCallId(getUniqueCallId());
1591 setEnrichedCallSession(session);
1592
1593 LogUtil.i(
1594 "DialerCall.updateEnrichedCallSession",
1595 "setting session %d's dialer id to %s",
1596 session.getSessionId(),
1597 getUniqueCallId());
1598
1599 dispatchOnEnrichedCallSessionUpdate();
1600 }
1601
1602 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001603 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001604 listener.onEnrichedCallSessionUpdate();
1605 }
1606 }
1607
1608 void onRemovedFromCallList() {
wangqif1861052018-04-19 15:06:06 -07001609 LogUtil.enterBlock("DialerCall.onRemovedFromCallList");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001610 // Ensure we clean up when this call is removed.
linyuh183cb712017-12-27 17:02:37 -08001611 videoTechManager.dispatchRemovedFromCallList();
wangqif1861052018-04-19 15:06:06 -07001612 if (rttTranscript != null) {
1613 Futures.addCallback(
1614 RttTranscriptUtil.saveRttTranscript(context, rttTranscript),
1615 new DefaultFutureCallback<>(),
1616 MoreExecutors.directExecutor());
1617 // Sets to null so it won't be saved again when called multiple times.
1618 rttTranscript = null;
1619 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001620 }
1621
wangqi4d705e52017-09-28 12:23:35 -07001622 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1623 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001624 }
1625
Android Dialer974fc292018-02-01 16:12:25 -08001626 public void markFeedbackRequested() {
1627 feedbackRequested = true;
1628 }
1629
1630 public boolean isFeedbackRequested() {
1631 return feedbackRequested;
1632 }
1633
Eric Erfanianccca3152017-02-22 16:32:36 -08001634 /**
twyen73a74c32018-03-07 12:12:24 -08001635 * If the in call UI has shown the phone account selection dialog for the call, the {@link
1636 * PreferredAccountRecorder} to record the result from the dialog.
1637 */
1638 @Nullable
1639 public PreferredAccountRecorder getPreferredAccountRecorder() {
1640 return preferredAccountRecorder;
1641 }
1642
1643 public void setPreferredAccountRecorder(PreferredAccountRecorder preferredAccountRecorder) {
1644 this.preferredAccountRecorder = preferredAccountRecorder;
1645 }
1646
erfaniand05d8992018-03-20 19:42:26 -07001647 /** Indicates the call is eligible for SpeakEasy */
1648 public boolean isSpeakEasyEligible() {
erfaniandcc84cd2018-04-17 12:41:05 -07001649 if (!Constraints.isAvailable(context)) {
1650 return false;
1651 }
1652
1653 return !isPotentialEmergencyCallback()
1654 && !isEmergencyCall()
1655 && !isActiveRttCall()
1656 && !isConferenceCall()
1657 && !isVideoCall()
1658 && !isVoiceMailNumber();
erfaniand05d8992018-03-20 19:42:26 -07001659 }
1660
1661 /** Indicates the user has selected SpeakEasy */
1662 public boolean isSpeakEasyCall() {
1663 if (!isSpeakEasyEligible()) {
1664 return false;
1665 }
1666 return isSpeakEasyCall;
1667 }
1668
1669 /** Sets the user preference for SpeakEasy */
1670 public void setIsSpeakEasyCall(boolean isSpeakEasyCall) {
1671 this.isSpeakEasyCall = isSpeakEasyCall;
1672 }
1673
twyen73a74c32018-03-07 12:12:24 -08001674 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001675 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1676 * means there is no result.
1677 */
1678 @IntDef({
1679 CALL_HISTORY_STATUS_UNKNOWN,
1680 CALL_HISTORY_STATUS_PRESENT,
1681 CALL_HISTORY_STATUS_NOT_PRESENT
1682 })
1683 @Retention(RetentionPolicy.SOURCE)
1684 public @interface CallHistoryStatus {}
1685
1686 /* Defines different states of this call */
1687 public static class State {
1688
1689 public static final int INVALID = 0;
1690 public static final int NEW = 1; /* The call is new. */
1691 public static final int IDLE = 2; /* The call is idle. Nothing active */
1692 public static final int ACTIVE = 3; /* There is an active call */
1693 public static final int INCOMING = 4; /* A normal incoming phone call */
1694 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1695 public static final int DIALING = 6; /* An outgoing call during dial phase */
1696 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1697 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1698 public static final int DISCONNECTING = 9; /* A call is being ended. */
1699 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1700 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1701 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1702 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1703 public static final int BLOCKED = 14; /* The number was found on the block list */
1704 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001705 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001706
1707 public static boolean isConnectingOrConnected(int state) {
1708 switch (state) {
1709 case ACTIVE:
1710 case INCOMING:
1711 case CALL_WAITING:
1712 case CONNECTING:
1713 case DIALING:
1714 case PULLING:
1715 case REDIALING:
1716 case ONHOLD:
1717 case CONFERENCED:
1718 return true;
1719 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001720 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001721 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001722 }
1723
1724 public static boolean isDialing(int state) {
1725 return state == DIALING || state == PULLING || state == REDIALING;
1726 }
1727
1728 public static String toString(int state) {
1729 switch (state) {
1730 case INVALID:
1731 return "INVALID";
1732 case NEW:
1733 return "NEW";
1734 case IDLE:
1735 return "IDLE";
1736 case ACTIVE:
1737 return "ACTIVE";
1738 case INCOMING:
1739 return "INCOMING";
1740 case CALL_WAITING:
1741 return "CALL_WAITING";
1742 case DIALING:
1743 return "DIALING";
1744 case PULLING:
1745 return "PULLING";
1746 case REDIALING:
1747 return "REDIALING";
1748 case ONHOLD:
1749 return "ONHOLD";
1750 case DISCONNECTING:
1751 return "DISCONNECTING";
1752 case DISCONNECTED:
1753 return "DISCONNECTED";
1754 case CONFERENCED:
1755 return "CONFERENCED";
1756 case SELECT_PHONE_ACCOUNT:
1757 return "SELECT_PHONE_ACCOUNT";
1758 case CONNECTING:
1759 return "CONNECTING";
1760 case BLOCKED:
1761 return "BLOCKED";
1762 default:
1763 return "UNKNOWN";
1764 }
1765 }
1766 }
1767
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001768 /** Camera direction constants */
1769 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001770 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1771 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1772 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001773 }
1774
1775 /**
1776 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1777 * existing call member variables, but this duplication helps to ensure that none of these logging
1778 * variables will interface with/and affect call logic.
1779 */
1780 public static class LogState {
1781
1782 public DisconnectCause disconnectCause;
1783 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001784 public ContactLookupResult.Type contactLookupResult =
1785 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001786 public CallSpecificAppData callSpecificAppData;
1787 // If this was a conference call, the total number of calls involved in the conference.
1788 public int conferencedCalls = 0;
1789 public long duration = 0;
1790 public boolean isLogged = false;
1791
Eric Erfanian8369df02017-05-03 10:27:13 -07001792 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001793 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001794 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001795 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001796 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001797 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001798 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001799 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001800 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001801 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001802 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001803 return "Voicemail";
1804 default:
1805 return "Not found";
1806 }
1807 }
1808
1809 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1810 if (callSpecificAppData == null) {
1811 return "null";
1812 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001813 switch (callSpecificAppData.getCallInitiationType()) {
1814 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001815 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001816 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001817 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001818 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001819 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001820 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001821 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001822 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001823 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001824 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001825 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001826 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001827 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001828 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001829 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001830 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001831 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001832 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001833 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001834 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001835 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001836 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001837 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001838 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001839 return "Launcher Shortcut";
1840 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001841 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001842 }
1843 }
1844
1845 @Override
1846 public String toString() {
1847 return String.format(
1848 Locale.US,
1849 "["
1850 + "%s, " // DisconnectCause toString already describes the object type
1851 + "isIncoming: %s, "
1852 + "contactLookup: %s, "
1853 + "callInitiation: %s, "
1854 + "duration: %s"
1855 + "]",
1856 disconnectCause,
1857 isIncoming,
1858 lookupToString(contactLookupResult),
1859 initiationToString(callSpecificAppData),
1860 duration);
1861 }
1862 }
1863
roldenburgd7490db2018-01-09 13:51:29 -08001864 /** Coordinates the available VideoTech implementations for a call. */
1865 @VisibleForTesting
1866 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001867 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001868 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001869 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001870 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001871 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001872
roldenburgd7490db2018-01-09 13:51:29 -08001873 @VisibleForTesting
1874 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001875 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001876
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001877 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001878 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001879 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001880
1881 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001882 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001883
linyuh183cb712017-12-27 17:02:37 -08001884 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001885
roldenburgd7490db2018-01-09 13:51:29 -08001886 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001887 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001888 .getRcsVideoShareFactory()
1889 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001890 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001891 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001892 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001893 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001894
1895 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001896 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001897 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001898
1899 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001900 }
1901
roldenburgd7490db2018-01-09 13:51:29 -08001902 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001903 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001904 if (savedTech == emptyVideoTech) {
1905 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001906 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001907 savedTech = tech;
1908 savedTech.becomePrimary();
1909 break;
1910 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001911 }
roldenburg6bd612f2018-01-18 12:57:19 -08001912 } else if (savedTech instanceof DuoVideoTech
1913 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001914 // RCS Video Share will become available after the capability exchange which is slower than
1915 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1916 // not in the middle of any session changes, let it take over.
1917 savedTech = rcsVideoShare;
1918 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001919 }
1920
roldenburgd7490db2018-01-09 13:51:29 -08001921 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001922 }
1923
roldenburgd7490db2018-01-09 13:51:29 -08001924 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001925 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001926 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001927 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001928 }
1929 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001930
1931 void dispatchRemovedFromCallList() {
1932 for (VideoTech videoTech : videoTechs) {
1933 videoTech.onRemovedFromCallList();
1934 }
1935 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001936 }
1937
Eric Erfanianccca3152017-02-22 16:32:36 -08001938 /** Called when canned text responses have been loaded. */
1939 public interface CannedTextResponsesLoadedListener {
1940 void onCannedTextResponsesLoaded(DialerCall call);
1941 }
1942}