blob: 8120249048e149f19a0170448f17398a8a1e368c [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;
Eric Erfanianccca3152017-02-22 16:32:36 -080020import android.content.Context;
21import android.hardware.camera2.CameraCharacteristics;
22import android.net.Uri;
23import android.os.Build.VERSION;
24import android.os.Build.VERSION_CODES;
25import android.os.Bundle;
26import android.os.Trace;
27import android.support.annotation.IntDef;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070028import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080029import android.support.annotation.Nullable;
twyendde01c52017-09-22 10:07:31 -070030import android.support.annotation.VisibleForTesting;
Eric Erfanianccca3152017-02-22 16:32:36 -080031import android.telecom.Call;
32import android.telecom.Call.Details;
Eric Erfanian8369df02017-05-03 10:27:13 -070033import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080034import android.telecom.Connection;
35import android.telecom.DisconnectCause;
36import android.telecom.GatewayInfo;
37import android.telecom.InCallService.VideoCall;
38import android.telecom.PhoneAccount;
39import android.telecom.PhoneAccountHandle;
40import android.telecom.StatusHints;
41import android.telecom.TelecomManager;
42import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080043import android.text.TextUtils;
44import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080045import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
erfaniand0f207f2017-10-11 12:23:29 -070046import com.android.dialer.assisteddialing.TransformationInfo;
Eric Erfanian8369df02017-05-03 10:27:13 -070047import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080048import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070049import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080050import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080051import com.android.dialer.common.LogUtil;
Eric Erfanian2ca43182017-08-31 06:57:16 -070052import com.android.dialer.compat.telephony.TelephonyManagerCompat;
53import com.android.dialer.configprovider.ConfigProviderBindings;
roldenburg4f026392017-10-13 18:42:20 -070054import com.android.dialer.duo.DuoComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070055import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070056import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070057import com.android.dialer.enrichedcall.EnrichedCallManager;
58import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
59import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
60import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070061import com.android.dialer.enrichedcall.Session;
wangqi9982f0d2017-10-11 17:46:07 -070062import com.android.dialer.location.GeoUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070063import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070064import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070065import com.android.dialer.logging.DialerImpression;
66import com.android.dialer.logging.Logger;
twyena4745bd2017-12-12 18:40:11 -080067import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070068import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070069import com.android.dialer.theme.R;
wangqi9982f0d2017-10-11 17:46:07 -070070import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070071import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080072import com.android.incallui.latencyreport.LatencyReport;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070073import com.android.incallui.videotech.VideoTech;
74import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070075import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070076import com.android.incallui.videotech.empty.EmptyVideoTech;
77import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070078import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080079import java.lang.annotation.Retention;
80import java.lang.annotation.RetentionPolicy;
81import java.util.ArrayList;
82import java.util.List;
83import java.util.Locale;
84import java.util.Objects;
85import java.util.UUID;
86import java.util.concurrent.CopyOnWriteArrayList;
87import java.util.concurrent.TimeUnit;
88
89/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -070090public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -080091
92 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
93 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
94 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070095
96 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -070097 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -070098 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
99
Eric Erfanianccca3152017-02-22 16:32:36 -0800100 private static final String ID_PREFIX = "DialerCall_";
101 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
102 "emergency_callback_window_millis";
103 private static int sIdCounter = 0;
104
105 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700106 * A counter used to append to restricted/private/hidden calls so that users can identify them in
107 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
108 * are no live calls.
109 */
110 private static int sHiddenCounter;
111
112 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800113 * The unique call ID for every call. This will help us to identify each call and allow us the
114 * ability to stitch impressions to calls if needed.
115 */
116 private final String uniqueCallId = UUID.randomUUID().toString();
117
118 private final Call mTelecomCall;
119 private final LatencyReport mLatencyReport;
120 private final String mId;
Eric Erfanianc857f902017-05-15 14:05:33 -0700121 private final int mHiddenId;
Eric Erfanianccca3152017-02-22 16:32:36 -0800122 private final List<String> mChildCallIds = new ArrayList<>();
Eric Erfanianccca3152017-02-22 16:32:36 -0800123 private final LogState mLogState = new LogState();
124 private final Context mContext;
125 private final DialerCallDelegate mDialerCallDelegate;
126 private final List<DialerCallListener> mListeners = new CopyOnWriteArrayList<>();
127 private final List<CannedTextResponsesLoadedListener> mCannedTextResponsesLoadedListeners =
128 new CopyOnWriteArrayList<>();
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700129 private final VideoTechManager mVideoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800130
131 private boolean mIsEmergencyCall;
132 private Uri mHandle;
133 private int mState = State.INVALID;
134 private DisconnectCause mDisconnectCause;
135
136 private boolean hasShownWiFiToLteHandoverToast;
137 private boolean doNotShowDialogForHandoffToWifiFailure;
138
Eric Erfanianccca3152017-02-22 16:32:36 -0800139 private String mChildNumber;
140 private String mLastForwardedNumber;
141 private String mCallSubject;
142 private PhoneAccountHandle mPhoneAccountHandle;
143 @CallHistoryStatus private int mCallHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
144 private boolean mIsSpam;
145 private boolean mIsBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700146
147 @Nullable private Boolean isInUserSpamList;
148
149 @Nullable private Boolean isInUserWhiteList;
150
151 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800152 private boolean didShowCameraPermission;
153 private String callProviderLabel;
154 private String callbackNumber;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700155 private int mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfanian8369df02017-05-03 10:27:13 -0700156 private EnrichedCallCapabilities mEnrichedCallCapabilities;
157 private Session mEnrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800158
Eric Erfanian2ca43182017-08-31 06:57:16 -0700159 private int answerAndReleaseButtonDisplayedTimes = 0;
160 private boolean releasedByAnsweringSecondCall = false;
161 // Times when a second call is received but AnswerAndRelease button is not shown
162 // since it's not supported.
163 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700164 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700165
wangqi4d705e52017-09-28 12:23:35 -0700166 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
167 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700168 private boolean isVoicemailNumber;
169 private List<PhoneAccountHandle> callCapableAccounts;
170 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700171
Eric Erfanianccca3152017-02-22 16:32:36 -0800172 public static String getNumberFromHandle(Uri handle) {
173 return handle == null ? "" : handle.getSchemeSpecificPart();
174 }
175
176 /**
177 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700178 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800179 */
180 private boolean isRemotelyHeld;
181
Eric Erfanian2ca43182017-08-31 06:57:16 -0700182 /** Indicates whether this call is currently in the process of being merged into a conference. */
183 private boolean isMergeInProcess;
184
Eric Erfanianccca3152017-02-22 16:32:36 -0800185 /**
186 * Indicates whether the phone account associated with this call supports specifying a call
187 * subject.
188 */
189 private boolean mIsCallSubjectSupported;
190
191 private final Call.Callback mTelecomCallCallback =
192 new Call.Callback() {
193 @Override
194 public void onStateChanged(Call call, int newState) {
195 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
196 update();
197 }
198
199 @Override
200 public void onParentChanged(Call call, Call newParent) {
201 LogUtil.v(
202 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
203 update();
204 }
205
206 @Override
207 public void onChildrenChanged(Call call, List<Call> children) {
208 update();
209 }
210
211 @Override
212 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700213 LogUtil.v(
214 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800215 update();
216 }
217
218 @Override
219 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
220 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700221 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800222 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
223 for (CannedTextResponsesLoadedListener listener : mCannedTextResponsesLoadedListeners) {
224 listener.onCannedTextResponsesLoaded(DialerCall.this);
225 }
226 }
227
228 @Override
229 public void onPostDialWait(Call call, String remainingPostDialSequence) {
230 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700231 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800232 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
233 update();
234 }
235
236 @Override
237 public void onVideoCallChanged(Call call, VideoCall videoCall) {
238 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700239 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800240 update();
241 }
242
243 @Override
244 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700245 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700246 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800247 }
248
249 @Override
250 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
251 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700252 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800253 "call %s, conferenceable calls: %d",
254 call,
255 conferenceableCalls.size());
256 update();
257 }
258
259 @Override
260 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
261 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700262 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800263 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
264 switch (event) {
265 // The Previous attempt to Merge two calls together has failed in Telecom. We must
266 // now update the UI to possibly re-enable the Merge button based on the number of
267 // currently conferenceable calls available or Connection Capabilities.
268 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
269 update();
270 break;
271 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
272 notifyWiFiToLteHandover();
273 break;
274 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
275 notifyHandoverToWifiFailed();
276 break;
277 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
278 isRemotelyHeld = true;
279 update();
280 break;
281 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
282 isRemotelyHeld = false;
283 update();
284 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700285 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
286 notifyInternationalCallOnWifi();
287 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700288 case TelephonyManagerCompat.EVENT_MERGE_START:
289 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
290 isMergeInProcess = true;
291 break;
292 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
293 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
294 isMergeInProcess = false;
295 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800296 default:
297 break;
298 }
299 }
300 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700301
Eric Erfanianccca3152017-02-22 16:32:36 -0800302 private long mTimeAddedMs;
303
304 public DialerCall(
305 Context context,
306 DialerCallDelegate dialerCallDelegate,
307 Call telecomCall,
308 LatencyReport latencyReport,
309 boolean registerCallback) {
310 Assert.isNotNull(context);
311 mContext = context;
312 mDialerCallDelegate = dialerCallDelegate;
313 mTelecomCall = telecomCall;
314 mLatencyReport = latencyReport;
315 mId = ID_PREFIX + Integer.toString(sIdCounter++);
316
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700317 // Must be after assigning mTelecomCall
318 mVideoTechManager = new VideoTechManager(this);
319
320 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700321 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
322 mHiddenId = ++sHiddenCounter;
323 } else {
324 mHiddenId = 0;
325 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800326
327 if (registerCallback) {
328 mTelecomCall.registerCallback(mTelecomCallCallback);
329 }
330
331 mTimeAddedMs = System.currentTimeMillis();
332 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700333
334 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800335 }
336
twyendde01c52017-09-22 10:07:31 -0700337 /** Test only constructor to avoid initializing dependencies. */
338 @VisibleForTesting
339 DialerCall(Context context) {
340 mContext = context;
341 mTelecomCall = null;
342 mLatencyReport = null;
343 mId = null;
344 mHiddenId = 0;
345 mDialerCallDelegate = null;
346 mVideoTechManager = null;
347 }
348
Eric Erfanianccca3152017-02-22 16:32:36 -0800349 private static int translateState(int state) {
350 switch (state) {
351 case Call.STATE_NEW:
352 case Call.STATE_CONNECTING:
353 return DialerCall.State.CONNECTING;
354 case Call.STATE_SELECT_PHONE_ACCOUNT:
355 return DialerCall.State.SELECT_PHONE_ACCOUNT;
356 case Call.STATE_DIALING:
357 return DialerCall.State.DIALING;
358 case Call.STATE_PULLING_CALL:
359 return DialerCall.State.PULLING;
360 case Call.STATE_RINGING:
361 return DialerCall.State.INCOMING;
362 case Call.STATE_ACTIVE:
363 return DialerCall.State.ACTIVE;
364 case Call.STATE_HOLDING:
365 return DialerCall.State.ONHOLD;
366 case Call.STATE_DISCONNECTED:
367 return DialerCall.State.DISCONNECTED;
368 case Call.STATE_DISCONNECTING:
369 return DialerCall.State.DISCONNECTING;
370 default:
371 return DialerCall.State.INVALID;
372 }
373 }
374
375 public static boolean areSame(DialerCall call1, DialerCall call2) {
376 if (call1 == null && call2 == null) {
377 return true;
378 } else if (call1 == null || call2 == null) {
379 return false;
380 }
381
382 // otherwise compare call Ids
383 return call1.getId().equals(call2.getId());
384 }
385
386 public static boolean areSameNumber(DialerCall call1, DialerCall call2) {
387 if (call1 == null && call2 == null) {
388 return true;
389 } else if (call1 == null || call2 == null) {
390 return false;
391 }
392
393 // otherwise compare call Numbers
394 return TextUtils.equals(call1.getNumber(), call2.getNumber());
395 }
396
397 public void addListener(DialerCallListener listener) {
398 Assert.isMainThread();
399 mListeners.add(listener);
400 }
401
402 public void removeListener(DialerCallListener listener) {
403 Assert.isMainThread();
404 mListeners.remove(listener);
405 }
406
407 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
408 Assert.isMainThread();
409 mCannedTextResponsesLoadedListeners.add(listener);
410 }
411
412 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
413 Assert.isMainThread();
414 mCannedTextResponsesLoadedListeners.remove(listener);
415 }
416
417 public void notifyWiFiToLteHandover() {
418 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
419 for (DialerCallListener listener : mListeners) {
420 listener.onWiFiToLteHandover();
421 }
422 }
423
424 public void notifyHandoverToWifiFailed() {
425 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
426 for (DialerCallListener listener : mListeners) {
427 listener.onHandoverToWifiFailure();
428 }
429 }
430
Eric Erfanianc857f902017-05-15 14:05:33 -0700431 public void notifyInternationalCallOnWifi() {
432 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
433 for (DialerCallListener dialerCallListener : mListeners) {
434 dialerCallListener.onInternationalCallOnWifi();
435 }
436 }
437
Eric Erfanianccca3152017-02-22 16:32:36 -0800438 /* package-private */ Call getTelecomCall() {
439 return mTelecomCall;
440 }
441
442 public StatusHints getStatusHints() {
443 return mTelecomCall.getDetails().getStatusHints();
444 }
445
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700446 public int getCameraDir() {
447 return mCameraDirection;
448 }
449
450 public void setCameraDir(int cameraDir) {
451 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
452 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
453 mCameraDirection = cameraDir;
454 } else {
455 mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
456 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800457 }
458
Eric Erfanian2ca43182017-08-31 06:57:16 -0700459 public boolean wasParentCall() {
460 return mLogState.conferencedCalls != 0;
461 }
462
wangqi9982f0d2017-10-11 17:46:07 -0700463 public boolean isVoiceMailNumber() {
464 return isVoicemailNumber;
465 }
466
467 public List<PhoneAccountHandle> getCallCapableAccounts() {
468 return callCapableAccounts;
469 }
470
471 public String getCountryIso() {
472 return countryIso;
473 }
474
475 private void updateIsVoiceMailNumber() {
476 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
477 isVoicemailNumber = true;
478 }
479
480 if (!PermissionsUtil.hasPermission(mContext, permission.READ_PHONE_STATE)) {
481 isVoicemailNumber = false;
482 }
483
484 isVoicemailNumber = TelecomUtil.isVoicemailNumber(mContext, getAccountHandle(), getNumber());
485 }
486
Eric Erfanianccca3152017-02-22 16:32:36 -0800487 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700488 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800489 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700490 // Clear any cache here that could potentially change on update.
491 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800492 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700493 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800494 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
495 for (DialerCallListener listener : mListeners) {
496 listener.onDialerCallDisconnect();
497 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700498 EnrichedCallComponent.get(mContext)
499 .getEnrichedCallManager()
500 .unregisterCapabilitiesListener(this);
501 EnrichedCallComponent.get(mContext)
502 .getEnrichedCallManager()
503 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800504 } else {
505 for (DialerCallListener listener : mListeners) {
506 listener.onDialerCallUpdate();
507 }
508 }
509 Trace.endSection();
510 }
511
wangqi9982f0d2017-10-11 17:46:07 -0700512 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700513 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700514 Trace.beginSection("DialerCall.updateFromTelecomCall");
Eric Erfanianccca3152017-02-22 16:32:36 -0800515 LogUtil.v("DialerCall.updateFromTelecomCall", mTelecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700516
517 mVideoTechManager.dispatchCallStateChanged(mTelecomCall.getState());
518
Eric Erfanianccca3152017-02-22 16:32:36 -0800519 final int translatedState = translateState(mTelecomCall.getState());
520 if (mState != State.BLOCKED) {
521 setState(translatedState);
522 setDisconnectCause(mTelecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800523 }
524
525 mChildCallIds.clear();
526 final int numChildCalls = mTelecomCall.getChildren().size();
527 for (int i = 0; i < numChildCalls; i++) {
528 mChildCallIds.add(
529 mDialerCallDelegate
530 .getDialerCallFromTelecomCall(mTelecomCall.getChildren().get(i))
531 .getId());
532 }
533
534 // The number of conferenced calls can change over the course of the call, so use the
535 // maximum number of conferenced child calls as the metric for conference call usage.
536 mLogState.conferencedCalls = Math.max(numChildCalls, mLogState.conferencedCalls);
537
538 updateFromCallExtras(mTelecomCall.getDetails().getExtras());
539
540 // If the handle of the call has changed, update state for the call determining if it is an
541 // emergency call.
542 Uri newHandle = mTelecomCall.getDetails().getHandle();
543 if (!Objects.equals(mHandle, newHandle)) {
544 mHandle = newHandle;
545 updateEmergencyCallState();
546 }
547
wangqi9982f0d2017-10-11 17:46:07 -0700548 TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800549 // If the phone account handle of the call is set, cache capability bit indicating whether
550 // the phone account supports call subjects.
551 PhoneAccountHandle newPhoneAccountHandle = mTelecomCall.getDetails().getAccountHandle();
552 if (!Objects.equals(mPhoneAccountHandle, newPhoneAccountHandle)) {
553 mPhoneAccountHandle = newPhoneAccountHandle;
554
555 if (mPhoneAccountHandle != null) {
wangqi9982f0d2017-10-11 17:46:07 -0700556 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(mPhoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800557 if (phoneAccount != null) {
558 mIsCallSubjectSupported =
559 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
560 }
561 }
562 }
wangqi9982f0d2017-10-11 17:46:07 -0700563 if (PermissionsUtil.hasPermission(mContext, permission.READ_PHONE_STATE)) {
564 updateIsVoiceMailNumber();
565 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
566 countryIso = GeoUtil.getCurrentCountryIso(mContext);
567 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700568 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800569 }
570
571 /**
572 * Tests corruption of the {@code callExtras} bundle by calling {@link
573 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
574 * be thrown and caught by this function.
575 *
576 * @param callExtras the bundle to verify
577 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
578 */
579 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
580 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700581 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800582 * bundle, resulting in a IllegalArgumentException while validating data under {@link
583 * Bundle#containsKey(String)}.
584 */
585 try {
586 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
587 return false;
588 } catch (IllegalArgumentException e) {
589 LogUtil.e(
590 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
591 return true;
592 }
593 }
594
595 protected void updateFromCallExtras(Bundle callExtras) {
596 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
597 /**
598 * If the bundle is corrupted, abandon information update as a work around. These are not
599 * critical for the dialer to function.
600 */
601 return;
602 }
603 // Check for a change in the child address and notify any listeners.
604 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
605 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
606 if (!Objects.equals(childNumber, mChildNumber)) {
607 mChildNumber = childNumber;
608 for (DialerCallListener listener : mListeners) {
609 listener.onDialerCallChildNumberChange();
610 }
611 }
612 }
613
614 // Last forwarded number comes in as an array of strings. We want to choose the
615 // last item in the array. The forwarding numbers arrive independently of when the
616 // call is originally set up, so we need to notify the the UI of the change.
617 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
618 ArrayList<String> lastForwardedNumbers =
619 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
620
621 if (lastForwardedNumbers != null) {
622 String lastForwardedNumber = null;
623 if (!lastForwardedNumbers.isEmpty()) {
624 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
625 }
626
627 if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) {
628 mLastForwardedNumber = lastForwardedNumber;
629 for (DialerCallListener listener : mListeners) {
630 listener.onDialerCallLastForwardedNumberChange();
631 }
632 }
633 }
634 }
635
636 // DialerCall subject is present in the extras at the start of call, so we do not need to
637 // notify any other listeners of this.
638 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
639 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
640 if (!Objects.equals(mCallSubject, callSubject)) {
641 mCallSubject = callSubject;
642 }
643 }
644 }
645
Eric Erfanianccca3152017-02-22 16:32:36 -0800646 public String getId() {
647 return mId;
648 }
649
Eric Erfanianc857f902017-05-15 14:05:33 -0700650 /**
651 * @return name appended with a number if the number is restricted/unknown and the user has
652 * received more than one restricted/unknown call.
653 */
654 @Nullable
655 public String updateNameIfRestricted(@Nullable String name) {
656 if (name != null && isHiddenNumber() && mHiddenId != 0 && sHiddenCounter > 1) {
657 return mContext.getString(R.string.unknown_counter, name, mHiddenId);
658 }
659 return name;
660 }
661
662 public static void clearRestrictedCount() {
663 sHiddenCounter = 0;
664 }
665
666 private boolean isHiddenNumber() {
667 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
668 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
669 }
670
Eric Erfanianccca3152017-02-22 16:32:36 -0800671 public boolean hasShownWiFiToLteHandoverToast() {
672 return hasShownWiFiToLteHandoverToast;
673 }
674
675 public void setHasShownWiFiToLteHandoverToast() {
676 hasShownWiFiToLteHandoverToast = true;
677 }
678
679 public boolean showWifiHandoverAlertAsToast() {
680 return doNotShowDialogForHandoffToWifiFailure;
681 }
682
683 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
684 doNotShowDialogForHandoffToWifiFailure = bool;
685 }
686
687 public long getTimeAddedMs() {
688 return mTimeAddedMs;
689 }
690
691 @Nullable
692 public String getNumber() {
693 return TelecomCallUtil.getNumber(mTelecomCall);
694 }
695
696 public void blockCall() {
697 mTelecomCall.reject(false, null);
698 setState(State.BLOCKED);
699 }
700
701 @Nullable
702 public Uri getHandle() {
703 return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandle();
704 }
705
706 public boolean isEmergencyCall() {
707 return mIsEmergencyCall;
708 }
709
710 public boolean isPotentialEmergencyCallback() {
711 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
712 // is actually in emergency callback mode (ie data is disabled).
713 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
714 return true;
715 }
716 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
717 // as a potential emergency callback.
718 if (getExtras() != null
719 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
720 > 0) {
721 long lastEmergencyCallMillis =
722 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
723 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
724 return true;
725 }
726 }
727 return false;
728 }
729
730 boolean isInEmergencyCallbackWindow(long timestampMillis) {
731 long emergencyCallbackWindowMillis =
732 ConfigProviderBindings.get(mContext)
733 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
734 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
735 }
736
737 public int getState() {
738 if (mTelecomCall != null && mTelecomCall.getParent() != null) {
739 return State.CONFERENCED;
740 } else {
741 return mState;
742 }
743 }
744
yueg265089a2017-10-06 14:35:15 -0700745 public int getNonConferenceState() {
746 return mState;
747 }
748
Eric Erfanianccca3152017-02-22 16:32:36 -0800749 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700750 if (state == State.INCOMING) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800751 mLogState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700752 } else if (state == State.DISCONNECTED) {
753 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800754 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700755 if (mState != state) {
756 mLogState.duration = newDuration;
757 } else {
758 LogUtil.i(
759 "DialerCall.setState",
760 "ignoring state transition from DISCONNECTED to DISCONNECTED."
761 + " Duration would have changed from %s to %s",
762 mLogState.duration,
763 newDuration);
764 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800765 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700766 mState = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800767 }
768
769 public int getNumberPresentation() {
770 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getHandlePresentation();
771 }
772
773 public int getCnapNamePresentation() {
774 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getCallerDisplayNamePresentation();
775 }
776
777 @Nullable
778 public String getCnapName() {
779 return mTelecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
780 }
781
782 public Bundle getIntentExtras() {
783 return mTelecomCall.getDetails().getIntentExtras();
784 }
785
786 @Nullable
787 public Bundle getExtras() {
788 return mTelecomCall == null ? null : mTelecomCall.getDetails().getExtras();
789 }
790
791 /** @return The child number for the call, or {@code null} if none specified. */
792 public String getChildNumber() {
793 return mChildNumber;
794 }
795
796 /** @return The last forwarded number for the call, or {@code null} if none specified. */
797 public String getLastForwardedNumber() {
798 return mLastForwardedNumber;
799 }
800
801 /** @return The call subject, or {@code null} if none specified. */
802 public String getCallSubject() {
803 return mCallSubject;
804 }
805
806 /**
807 * @return {@code true} if the call's phone account supports call subjects, {@code false}
808 * otherwise.
809 */
810 public boolean isCallSubjectSupported() {
811 return mIsCallSubjectSupported;
812 }
813
814 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
815 public DisconnectCause getDisconnectCause() {
816 if (mState == State.DISCONNECTED || mState == State.IDLE) {
817 return mDisconnectCause;
818 }
819
820 return new DisconnectCause(DisconnectCause.UNKNOWN);
821 }
822
823 public void setDisconnectCause(DisconnectCause disconnectCause) {
824 mDisconnectCause = disconnectCause;
825 mLogState.disconnectCause = mDisconnectCause;
826 }
827
828 /** Returns the possible text message responses. */
829 public List<String> getCannedSmsResponses() {
830 return mTelecomCall.getCannedTextResponses();
831 }
832
833 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
834 public boolean can(int capabilities) {
835 int supportedCapabilities = mTelecomCall.getDetails().getCallCapabilities();
836
837 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
838 // We allow you to merge if the capabilities allow it or if it is a call with
839 // conferenceable calls.
840 if (mTelecomCall.getConferenceableCalls().isEmpty()
841 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
842 // Cannot merge calls if there are no calls to merge with.
843 return false;
844 }
845 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
846 }
847 return (capabilities == (capabilities & supportedCapabilities));
848 }
849
850 public boolean hasProperty(int property) {
851 return mTelecomCall.getDetails().hasProperty(property);
852 }
853
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700854 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800855 public String getUniqueCallId() {
856 return uniqueCallId;
857 }
858
859 /** Gets the time when the call first became active. */
860 public long getConnectTimeMillis() {
861 return mTelecomCall.getDetails().getConnectTimeMillis();
862 }
863
864 public boolean isConferenceCall() {
865 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
866 }
867
868 @Nullable
869 public GatewayInfo getGatewayInfo() {
870 return mTelecomCall == null ? null : mTelecomCall.getDetails().getGatewayInfo();
871 }
872
873 @Nullable
874 public PhoneAccountHandle getAccountHandle() {
875 return mTelecomCall == null ? null : mTelecomCall.getDetails().getAccountHandle();
876 }
877
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700878 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -0800879 public VideoCall getVideoCall() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700880 return mTelecomCall == null ? null : mTelecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800881 }
882
883 public List<String> getChildCallIds() {
884 return mChildCallIds;
885 }
886
887 public String getParentId() {
888 Call parentCall = mTelecomCall.getParent();
889 if (parentCall != null) {
890 return mDialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
891 }
892 return null;
893 }
894
895 public int getVideoState() {
896 return mTelecomCall.getDetails().getVideoState();
897 }
898
899 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -0700900 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700901 }
902
903 public boolean hasReceivedVideoUpgradeRequest() {
904 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
905 }
906
907 public boolean hasSentVideoUpgradeRequest() {
908 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -0800909 }
910
911 /**
912 * Determines if the call handle is an emergency number or not and caches the result to avoid
913 * repeated calls to isEmergencyNumber.
914 */
915 private void updateEmergencyCallState() {
916 mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
917 }
918
Eric Erfanianccca3152017-02-22 16:32:36 -0800919 public LogState getLogState() {
920 return mLogState;
921 }
922
923 /**
924 * Determines if the call is an external call.
925 *
926 * <p>An external call is one which does not exist locally for the {@link
927 * android.telecom.ConnectionService} it is associated with.
928 *
929 * <p>External calls are only supported in N and higher.
930 *
931 * @return {@code true} if the call is an external call, {@code false} otherwise.
932 */
933 public boolean isExternalCall() {
934 return VERSION.SDK_INT >= VERSION_CODES.N
935 && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
936 }
937
938 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800939 * Determines if answering this call will cause an ongoing video call to be dropped.
940 *
941 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
942 * otherwise.
943 */
944 public boolean answeringDisconnectsForegroundVideoCall() {
945 Bundle extras = getExtras();
946 if (extras == null
947 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
948 return false;
949 }
950 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
951 }
952
953 private void parseCallSpecificAppData() {
954 if (isExternalCall()) {
955 return;
956 }
957
958 mLogState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
959 if (mLogState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700960
961 mLogState.callSpecificAppData =
962 CallSpecificAppData.newBuilder()
963 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
964 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800965 }
966 if (getState() == State.INCOMING) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700967 mLogState.callSpecificAppData =
968 mLogState
969 .callSpecificAppData
970 .toBuilder()
971 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
972 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800973 }
974 }
975
976 @Override
977 public String toString() {
978 if (mTelecomCall == null) {
979 // This should happen only in testing since otherwise we would never have a null
980 // Telecom call.
981 return String.valueOf(mId);
982 }
983
984 return String.format(
985 Locale.US,
986 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700987 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
Eric Erfanianccca3152017-02-22 16:32:36 -0800988 mId,
989 State.toString(getState()),
990 Details.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
991 Details.propertiesToString(mTelecomCall.getDetails().getCallProperties()),
992 mChildCallIds,
993 getParentId(),
994 this.mTelecomCall.getConferenceableCalls(),
995 VideoProfile.videoStateToString(mTelecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700996 getVideoTech().getSessionModificationState(),
997 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -0800998 }
999
1000 public String toSimpleString() {
1001 return super.toString();
1002 }
1003
1004 @CallHistoryStatus
1005 public int getCallHistoryStatus() {
1006 return mCallHistoryStatus;
1007 }
1008
1009 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
1010 mCallHistoryStatus = callHistoryStatus;
1011 }
1012
1013 public boolean didShowCameraPermission() {
1014 return didShowCameraPermission;
1015 }
1016
1017 public void setDidShowCameraPermission(boolean didShow) {
1018 didShowCameraPermission = didShow;
1019 }
1020
Eric Erfanian938468d2017-10-24 14:05:52 -07001021 @Nullable
1022 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001023 return isInGlobalSpamList;
1024 }
1025
1026 public void setIsInGlobalSpamList(boolean inSpamList) {
1027 isInGlobalSpamList = inSpamList;
1028 }
1029
Eric Erfanian938468d2017-10-24 14:05:52 -07001030 @Nullable
1031 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001032 return isInUserSpamList;
1033 }
1034
1035 public void setIsInUserSpamList(boolean inSpamList) {
1036 isInUserSpamList = inSpamList;
1037 }
1038
Eric Erfanian938468d2017-10-24 14:05:52 -07001039 @Nullable
1040 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001041 return isInUserWhiteList;
1042 }
1043
1044 public void setIsInUserWhiteList(boolean inWhiteList) {
1045 isInUserWhiteList = inWhiteList;
1046 }
1047
1048 public boolean isSpam() {
1049 return mIsSpam;
1050 }
1051
1052 public void setSpam(boolean isSpam) {
1053 mIsSpam = isSpam;
1054 }
1055
1056 public boolean isBlocked() {
1057 return mIsBlocked;
1058 }
1059
1060 public void setBlockedStatus(boolean isBlocked) {
1061 mIsBlocked = isBlocked;
1062 }
1063
1064 public boolean isRemotelyHeld() {
1065 return isRemotelyHeld;
1066 }
1067
Eric Erfanian2ca43182017-08-31 06:57:16 -07001068 public boolean isMergeInProcess() {
1069 return isMergeInProcess;
1070 }
1071
Eric Erfanianccca3152017-02-22 16:32:36 -08001072 public boolean isIncoming() {
1073 return mLogState.isIncoming;
1074 }
1075
Eric Erfanian2ca43182017-08-31 06:57:16 -07001076 public boolean isAssistedDialed() {
1077 if (getIntentExtras() != null) {
1078 return getIntentExtras().getBoolean(TelephonyManagerCompat.IS_ASSISTED_DIALED, false);
1079 }
1080 return false;
1081 }
1082
erfaniand0f207f2017-10-11 12:23:29 -07001083 public TransformationInfo getAssistedDialingExtras() {
1084 if (isAssistedDialed()) {
1085 return TransformationInfo.newInstanceFromBundle(
1086 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
1087 }
1088 return null;
1089 }
1090
Eric Erfanianccca3152017-02-22 16:32:36 -08001091 public LatencyReport getLatencyReport() {
1092 return mLatencyReport;
1093 }
1094
Eric Erfanian2ca43182017-08-31 06:57:16 -07001095 public int getAnswerAndReleaseButtonDisplayedTimes() {
1096 return answerAndReleaseButtonDisplayedTimes;
1097 }
1098
1099 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1100 answerAndReleaseButtonDisplayedTimes++;
1101 }
1102
1103 public boolean getReleasedByAnsweringSecondCall() {
1104 return releasedByAnsweringSecondCall;
1105 }
1106
1107 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1108 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1109 }
1110
1111 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1112 return secondCallWithoutAnswerAndReleasedButtonTimes;
1113 }
1114
1115 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1116 secondCallWithoutAnswerAndReleasedButtonTimes++;
1117 }
1118
Eric Erfanian8369df02017-05-03 10:27:13 -07001119 @Nullable
1120 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
1121 return mEnrichedCallCapabilities;
1122 }
1123
1124 public void setEnrichedCallCapabilities(
1125 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
1126 this.mEnrichedCallCapabilities = mEnrichedCallCapabilities;
1127 }
1128
1129 @Nullable
1130 public Session getEnrichedCallSession() {
1131 return mEnrichedCallSession;
1132 }
1133
1134 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
1135 this.mEnrichedCallSession = mEnrichedCallSession;
1136 }
1137
Eric Erfanianccca3152017-02-22 16:32:36 -08001138 public void unregisterCallback() {
1139 mTelecomCall.unregisterCallback(mTelecomCallCallback);
1140 }
1141
Eric Erfanianccca3152017-02-22 16:32:36 -08001142 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1143 LogUtil.i(
1144 "DialerCall.phoneAccountSelected",
1145 "accountHandle: %s, setDefault: %b",
1146 accountHandle,
1147 setDefault);
1148 mTelecomCall.phoneAccountSelected(accountHandle, setDefault);
1149 }
1150
1151 public void disconnect() {
1152 LogUtil.i("DialerCall.disconnect", "");
1153 setState(DialerCall.State.DISCONNECTING);
1154 for (DialerCallListener listener : mListeners) {
1155 listener.onDialerCallUpdate();
1156 }
1157 mTelecomCall.disconnect();
1158 }
1159
1160 public void hold() {
1161 LogUtil.i("DialerCall.hold", "");
1162 mTelecomCall.hold();
1163 }
1164
1165 public void unhold() {
1166 LogUtil.i("DialerCall.unhold", "");
1167 mTelecomCall.unhold();
1168 }
1169
1170 public void splitFromConference() {
1171 LogUtil.i("DialerCall.splitFromConference", "");
1172 mTelecomCall.splitFromConference();
1173 }
1174
1175 public void answer(int videoState) {
1176 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
1177 mTelecomCall.answer(videoState);
1178 }
1179
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001180 public void answer() {
1181 answer(mTelecomCall.getDetails().getVideoState());
1182 }
1183
Eric Erfanianccca3152017-02-22 16:32:36 -08001184 public void reject(boolean rejectWithMessage, String message) {
1185 LogUtil.i("DialerCall.reject", "");
1186 mTelecomCall.reject(rejectWithMessage, message);
1187 }
1188
1189 /** Return the string label to represent the call provider */
1190 public String getCallProviderLabel() {
1191 if (callProviderLabel == null) {
1192 PhoneAccount account = getPhoneAccount();
1193 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001194 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001195 callProviderLabel = account.getLabel().toString();
1196 }
1197 }
1198 if (callProviderLabel == null) {
1199 callProviderLabel = "";
1200 }
1201 }
1202 return callProviderLabel;
1203 }
1204
1205 private PhoneAccount getPhoneAccount() {
1206 PhoneAccountHandle accountHandle = getAccountHandle();
1207 if (accountHandle == null) {
1208 return null;
1209 }
1210 return mContext.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
1211 }
1212
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001213 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001214 if (videoTech == null) {
1215 videoTech = mVideoTechManager.getVideoTech();
1216
1217 // Only store the first video tech type found to be available during the life of the call.
1218 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1219 // Update the video tech.
1220 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1221 }
1222 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001223 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001224 }
1225
Eric Erfanianccca3152017-02-22 16:32:36 -08001226 public String getCallbackNumber() {
1227 if (callbackNumber == null) {
1228 // Show the emergency callback number if either:
1229 // 1. This is an emergency call.
1230 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1231 // number.
1232 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1233
1234 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001235 callbackNumber =
1236 mContext.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001237 }
1238
Eric Erfanianccca3152017-02-22 16:32:36 -08001239 if (callbackNumber == null) {
1240 callbackNumber = "";
1241 }
1242 }
1243 return callbackNumber;
1244 }
1245
wangqi97539352017-09-25 11:15:16 -07001246 public String getSimCountryIso() {
1247 String simCountryIso =
1248 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(
1249 mContext, getAccountHandle())
1250 .getSimCountryIso();
1251 if (!TextUtils.isEmpty(simCountryIso)) {
1252 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1253 }
1254 return simCountryIso;
1255 }
1256
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001257 @Override
1258 public void onVideoTechStateChanged() {
1259 update();
1260 }
1261
1262 @Override
1263 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001264 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001265 for (DialerCallListener listener : mListeners) {
1266 listener.onDialerCallSessionModificationStateChange();
1267 }
wangqi9982f0d2017-10-11 17:46:07 -07001268 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001269 }
1270
1271 @Override
1272 public void onCameraDimensionsChanged(int width, int height) {
1273 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1274 }
1275
1276 @Override
1277 public void onPeerDimensionsChanged(int width, int height) {
1278 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1279 }
1280
1281 @Override
1282 public void onVideoUpgradeRequestReceived() {
1283 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1284
1285 for (DialerCallListener listener : mListeners) {
1286 listener.onDialerCallUpgradeToVideo();
1287 }
1288
1289 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001290
1291 Logger.get(mContext)
1292 .logCallImpression(
1293 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1294 }
1295
1296 @Override
1297 public void onUpgradedToVideo(boolean switchToSpeaker) {
1298 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1299
1300 if (!switchToSpeaker) {
1301 return;
1302 }
1303
1304 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1305
1306 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1307 LogUtil.e(
1308 "DialerCall.onUpgradedToVideo",
1309 "toggling speakerphone not allowed when bluetooth supported.");
1310 return;
1311 }
1312
1313 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1314 return;
1315 }
1316
1317 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001318 }
1319
Eric Erfanian2ca43182017-08-31 06:57:16 -07001320 @Override
1321 public void onCapabilitiesUpdated() {
1322 if (getNumber() == null) {
1323 return;
1324 }
1325 EnrichedCallCapabilities capabilities =
1326 EnrichedCallComponent.get(mContext).getEnrichedCallManager().getCapabilities(getNumber());
1327 if (capabilities != null) {
1328 setEnrichedCallCapabilities(capabilities);
1329 update();
1330 }
1331 }
1332
1333 @Override
1334 public void onEnrichedCallStateChanged() {
1335 updateEnrichedCallSession();
1336 }
1337
1338 @Override
1339 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
1340 Logger.get(mContext).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001341 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1342 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
1343 Logger.get(mContext)
1344 .logCallImpression(
1345 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1346 getUniqueCallId(),
1347 getTimeAddedMs());
1348 }
1349 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001350 }
1351
1352 private void updateEnrichedCallSession() {
1353 if (getNumber() == null) {
1354 return;
1355 }
1356 if (getEnrichedCallSession() != null) {
1357 // State changes to existing sessions are currently handled by the UI components (which have
1358 // their own listeners). Someday instead we could remove those and just call update() here and
1359 // have the usual onDialerCallUpdate update the UI.
1360 dispatchOnEnrichedCallSessionUpdate();
1361 return;
1362 }
1363
1364 EnrichedCallManager manager = EnrichedCallComponent.get(mContext).getEnrichedCallManager();
1365
1366 Filter filter =
1367 isIncoming()
1368 ? manager.createIncomingCallComposerFilter()
1369 : manager.createOutgoingCallComposerFilter();
1370
1371 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1372 if (session == null) {
1373 return;
1374 }
1375
1376 session.setUniqueDialerCallId(getUniqueCallId());
1377 setEnrichedCallSession(session);
1378
1379 LogUtil.i(
1380 "DialerCall.updateEnrichedCallSession",
1381 "setting session %d's dialer id to %s",
1382 session.getSessionId(),
1383 getUniqueCallId());
1384
1385 dispatchOnEnrichedCallSessionUpdate();
1386 }
1387
1388 private void dispatchOnEnrichedCallSessionUpdate() {
1389 for (DialerCallListener listener : mListeners) {
1390 listener.onEnrichedCallSessionUpdate();
1391 }
1392 }
1393
1394 void onRemovedFromCallList() {
1395 // Ensure we clean up when this call is removed.
1396 mVideoTechManager.dispatchRemovedFromCallList();
1397 }
1398
wangqi4d705e52017-09-28 12:23:35 -07001399 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1400 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001401 }
1402
Eric Erfanianccca3152017-02-22 16:32:36 -08001403 /**
1404 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1405 * means there is no result.
1406 */
1407 @IntDef({
1408 CALL_HISTORY_STATUS_UNKNOWN,
1409 CALL_HISTORY_STATUS_PRESENT,
1410 CALL_HISTORY_STATUS_NOT_PRESENT
1411 })
1412 @Retention(RetentionPolicy.SOURCE)
1413 public @interface CallHistoryStatus {}
1414
1415 /* Defines different states of this call */
1416 public static class State {
1417
1418 public static final int INVALID = 0;
1419 public static final int NEW = 1; /* The call is new. */
1420 public static final int IDLE = 2; /* The call is idle. Nothing active */
1421 public static final int ACTIVE = 3; /* There is an active call */
1422 public static final int INCOMING = 4; /* A normal incoming phone call */
1423 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1424 public static final int DIALING = 6; /* An outgoing call during dial phase */
1425 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1426 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1427 public static final int DISCONNECTING = 9; /* A call is being ended. */
1428 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1429 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1430 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1431 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1432 public static final int BLOCKED = 14; /* The number was found on the block list */
1433 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001434 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001435
1436 public static boolean isConnectingOrConnected(int state) {
1437 switch (state) {
1438 case ACTIVE:
1439 case INCOMING:
1440 case CALL_WAITING:
1441 case CONNECTING:
1442 case DIALING:
1443 case PULLING:
1444 case REDIALING:
1445 case ONHOLD:
1446 case CONFERENCED:
1447 return true;
1448 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001449 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001450 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001451 }
1452
1453 public static boolean isDialing(int state) {
1454 return state == DIALING || state == PULLING || state == REDIALING;
1455 }
1456
1457 public static String toString(int state) {
1458 switch (state) {
1459 case INVALID:
1460 return "INVALID";
1461 case NEW:
1462 return "NEW";
1463 case IDLE:
1464 return "IDLE";
1465 case ACTIVE:
1466 return "ACTIVE";
1467 case INCOMING:
1468 return "INCOMING";
1469 case CALL_WAITING:
1470 return "CALL_WAITING";
1471 case DIALING:
1472 return "DIALING";
1473 case PULLING:
1474 return "PULLING";
1475 case REDIALING:
1476 return "REDIALING";
1477 case ONHOLD:
1478 return "ONHOLD";
1479 case DISCONNECTING:
1480 return "DISCONNECTING";
1481 case DISCONNECTED:
1482 return "DISCONNECTED";
1483 case CONFERENCED:
1484 return "CONFERENCED";
1485 case SELECT_PHONE_ACCOUNT:
1486 return "SELECT_PHONE_ACCOUNT";
1487 case CONNECTING:
1488 return "CONNECTING";
1489 case BLOCKED:
1490 return "BLOCKED";
1491 default:
1492 return "UNKNOWN";
1493 }
1494 }
1495 }
1496
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001497 /** Camera direction constants */
1498 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001499 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1500 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1501 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001502 }
1503
1504 /**
1505 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1506 * existing call member variables, but this duplication helps to ensure that none of these logging
1507 * variables will interface with/and affect call logic.
1508 */
1509 public static class LogState {
1510
1511 public DisconnectCause disconnectCause;
1512 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001513 public ContactLookupResult.Type contactLookupResult =
1514 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001515 public CallSpecificAppData callSpecificAppData;
1516 // If this was a conference call, the total number of calls involved in the conference.
1517 public int conferencedCalls = 0;
1518 public long duration = 0;
1519 public boolean isLogged = false;
1520
Eric Erfanian8369df02017-05-03 10:27:13 -07001521 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001522 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001523 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001524 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001525 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001526 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001527 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001528 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001529 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001530 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001531 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001532 return "Voicemail";
1533 default:
1534 return "Not found";
1535 }
1536 }
1537
1538 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1539 if (callSpecificAppData == null) {
1540 return "null";
1541 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001542 switch (callSpecificAppData.getCallInitiationType()) {
1543 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001544 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001545 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001546 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001547 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001548 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001549 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001550 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001551 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001552 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001553 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001554 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001555 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001556 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001557 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001558 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001559 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001560 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001561 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001562 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001563 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001564 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001565 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001566 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001567 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001568 return "Launcher Shortcut";
1569 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001570 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001571 }
1572 }
1573
1574 @Override
1575 public String toString() {
1576 return String.format(
1577 Locale.US,
1578 "["
1579 + "%s, " // DisconnectCause toString already describes the object type
1580 + "isIncoming: %s, "
1581 + "contactLookup: %s, "
1582 + "callInitiation: %s, "
1583 + "duration: %s"
1584 + "]",
1585 disconnectCause,
1586 isIncoming,
1587 lookupToString(contactLookupResult),
1588 initiationToString(callSpecificAppData),
1589 duration);
1590 }
1591 }
1592
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001593 private static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001594 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001595 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
Eric Erfanian90508232017-03-24 09:31:16 -07001596 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001597 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001598
1599 VideoTechManager(DialerCall call) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001600 this.context = call.mContext;
1601
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001602 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001603 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001604 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001605
1606 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001607 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001608
wangqi4d705e52017-09-28 12:23:35 -07001609 videoTechs.add(new ImsVideoTech(Logger.get(call.mContext), call, call.mTelecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001610
1611 VideoTech rcsVideoTech =
1612 EnrichedCallComponent.get(call.mContext)
1613 .getRcsVideoShareFactory()
1614 .newRcsVideoShare(
1615 EnrichedCallComponent.get(call.mContext).getEnrichedCallManager(),
1616 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001617 phoneNumber);
Eric Erfanian90508232017-03-24 09:31:16 -07001618 if (rcsVideoTech != null) {
1619 videoTechs.add(rcsVideoTech);
1620 }
Eric Erfaniand8046e52017-04-06 09:41:50 -07001621
1622 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001623 new DuoVideoTech(
1624 DuoComponent.get(call.mContext).getDuo(), call, call.mTelecomCall, phoneNumber));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001625 }
1626
roldenburg7bb96232017-10-09 10:32:05 -07001627 VideoTech getVideoTech() {
1628 if (savedTech != null) {
1629 return savedTech;
1630 }
1631
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001632 for (VideoTech tech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001633 if (tech.isAvailable(context)) {
roldenburg7bb96232017-10-09 10:32:05 -07001634 // Remember the first VideoTech that becomes available and always use it
1635 savedTech = tech;
1636 savedTech.becomePrimary();
1637 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001638 }
1639 }
1640
1641 return emptyVideoTech;
1642 }
1643
1644 void dispatchCallStateChanged(int newState) {
1645 for (VideoTech videoTech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001646 videoTech.onCallStateChanged(context, newState);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001647 }
1648 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001649
1650 void dispatchRemovedFromCallList() {
1651 for (VideoTech videoTech : videoTechs) {
1652 videoTech.onRemovedFromCallList();
1653 }
1654 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001655 }
1656
Eric Erfanianccca3152017-02-22 16:32:36 -08001657 /** Called when canned text responses have been loaded. */
1658 public interface CannedTextResponsesLoadedListener {
1659 void onCannedTextResponsesLoaded(DialerCall call);
1660 }
1661}