blob: d911a4c4fa8823da97f3863386d36e37aca17e31 [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;
wangqi9982f0d2017-10-11 17:46:07 -070067import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070068import com.android.dialer.theme.R;
wangqi9982f0d2017-10-11 17:46:07 -070069import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070070import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080071import com.android.incallui.latencyreport.LatencyReport;
72import com.android.incallui.util.TelecomCallUtil;
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.
97 // TODO(b/35359461): Move it to Telecom in framework.
98 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;
146 private boolean isInUserSpamList;
147 private boolean isInUserWhiteList;
148 private boolean isInGlobalSpamList;
149 private boolean didShowCameraPermission;
150 private String callProviderLabel;
151 private String callbackNumber;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700152 private int mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfanian8369df02017-05-03 10:27:13 -0700153 private EnrichedCallCapabilities mEnrichedCallCapabilities;
154 private Session mEnrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800155
Eric Erfanian2ca43182017-08-31 06:57:16 -0700156 private int answerAndReleaseButtonDisplayedTimes = 0;
157 private boolean releasedByAnsweringSecondCall = false;
158 // Times when a second call is received but AnswerAndRelease button is not shown
159 // since it's not supported.
160 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700161 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700162
wangqi4d705e52017-09-28 12:23:35 -0700163 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
164 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700165 private boolean isVoicemailNumber;
166 private List<PhoneAccountHandle> callCapableAccounts;
167 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700168
Eric Erfanianccca3152017-02-22 16:32:36 -0800169 public static String getNumberFromHandle(Uri handle) {
170 return handle == null ? "" : handle.getSchemeSpecificPart();
171 }
172
173 /**
174 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700175 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800176 */
177 private boolean isRemotelyHeld;
178
Eric Erfanian2ca43182017-08-31 06:57:16 -0700179 /** Indicates whether this call is currently in the process of being merged into a conference. */
180 private boolean isMergeInProcess;
181
Eric Erfanianccca3152017-02-22 16:32:36 -0800182 /**
183 * Indicates whether the phone account associated with this call supports specifying a call
184 * subject.
185 */
186 private boolean mIsCallSubjectSupported;
187
188 private final Call.Callback mTelecomCallCallback =
189 new Call.Callback() {
190 @Override
191 public void onStateChanged(Call call, int newState) {
192 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
193 update();
194 }
195
196 @Override
197 public void onParentChanged(Call call, Call newParent) {
198 LogUtil.v(
199 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
200 update();
201 }
202
203 @Override
204 public void onChildrenChanged(Call call, List<Call> children) {
205 update();
206 }
207
208 @Override
209 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700210 LogUtil.v(
211 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800212 update();
213 }
214
215 @Override
216 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
217 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700218 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800219 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
220 for (CannedTextResponsesLoadedListener listener : mCannedTextResponsesLoadedListeners) {
221 listener.onCannedTextResponsesLoaded(DialerCall.this);
222 }
223 }
224
225 @Override
226 public void onPostDialWait(Call call, String remainingPostDialSequence) {
227 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700228 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800229 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
230 update();
231 }
232
233 @Override
234 public void onVideoCallChanged(Call call, VideoCall videoCall) {
235 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700236 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800237 update();
238 }
239
240 @Override
241 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700242 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700243 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800244 }
245
246 @Override
247 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
248 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700249 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800250 "call %s, conferenceable calls: %d",
251 call,
252 conferenceableCalls.size());
253 update();
254 }
255
256 @Override
257 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
258 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700259 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800260 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
261 switch (event) {
262 // The Previous attempt to Merge two calls together has failed in Telecom. We must
263 // now update the UI to possibly re-enable the Merge button based on the number of
264 // currently conferenceable calls available or Connection Capabilities.
265 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
266 update();
267 break;
268 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
269 notifyWiFiToLteHandover();
270 break;
271 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
272 notifyHandoverToWifiFailed();
273 break;
274 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
275 isRemotelyHeld = true;
276 update();
277 break;
278 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
279 isRemotelyHeld = false;
280 update();
281 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700282 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
283 notifyInternationalCallOnWifi();
284 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700285 case TelephonyManagerCompat.EVENT_MERGE_START:
286 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
287 isMergeInProcess = true;
288 break;
289 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
290 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
291 isMergeInProcess = false;
292 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800293 default:
294 break;
295 }
296 }
297 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700298
Eric Erfanianccca3152017-02-22 16:32:36 -0800299 private long mTimeAddedMs;
300
301 public DialerCall(
302 Context context,
303 DialerCallDelegate dialerCallDelegate,
304 Call telecomCall,
305 LatencyReport latencyReport,
306 boolean registerCallback) {
307 Assert.isNotNull(context);
308 mContext = context;
309 mDialerCallDelegate = dialerCallDelegate;
310 mTelecomCall = telecomCall;
311 mLatencyReport = latencyReport;
312 mId = ID_PREFIX + Integer.toString(sIdCounter++);
313
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700314 // Must be after assigning mTelecomCall
315 mVideoTechManager = new VideoTechManager(this);
316
317 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700318 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
319 mHiddenId = ++sHiddenCounter;
320 } else {
321 mHiddenId = 0;
322 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800323
324 if (registerCallback) {
325 mTelecomCall.registerCallback(mTelecomCallCallback);
326 }
327
328 mTimeAddedMs = System.currentTimeMillis();
329 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700330
331 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800332 }
333
twyendde01c52017-09-22 10:07:31 -0700334 /** Test only constructor to avoid initializing dependencies. */
335 @VisibleForTesting
336 DialerCall(Context context) {
337 mContext = context;
338 mTelecomCall = null;
339 mLatencyReport = null;
340 mId = null;
341 mHiddenId = 0;
342 mDialerCallDelegate = null;
343 mVideoTechManager = null;
344 }
345
Eric Erfanianccca3152017-02-22 16:32:36 -0800346 private static int translateState(int state) {
347 switch (state) {
348 case Call.STATE_NEW:
349 case Call.STATE_CONNECTING:
350 return DialerCall.State.CONNECTING;
351 case Call.STATE_SELECT_PHONE_ACCOUNT:
352 return DialerCall.State.SELECT_PHONE_ACCOUNT;
353 case Call.STATE_DIALING:
354 return DialerCall.State.DIALING;
355 case Call.STATE_PULLING_CALL:
356 return DialerCall.State.PULLING;
357 case Call.STATE_RINGING:
358 return DialerCall.State.INCOMING;
359 case Call.STATE_ACTIVE:
360 return DialerCall.State.ACTIVE;
361 case Call.STATE_HOLDING:
362 return DialerCall.State.ONHOLD;
363 case Call.STATE_DISCONNECTED:
364 return DialerCall.State.DISCONNECTED;
365 case Call.STATE_DISCONNECTING:
366 return DialerCall.State.DISCONNECTING;
367 default:
368 return DialerCall.State.INVALID;
369 }
370 }
371
372 public static boolean areSame(DialerCall call1, DialerCall call2) {
373 if (call1 == null && call2 == null) {
374 return true;
375 } else if (call1 == null || call2 == null) {
376 return false;
377 }
378
379 // otherwise compare call Ids
380 return call1.getId().equals(call2.getId());
381 }
382
383 public static boolean areSameNumber(DialerCall call1, DialerCall call2) {
384 if (call1 == null && call2 == null) {
385 return true;
386 } else if (call1 == null || call2 == null) {
387 return false;
388 }
389
390 // otherwise compare call Numbers
391 return TextUtils.equals(call1.getNumber(), call2.getNumber());
392 }
393
394 public void addListener(DialerCallListener listener) {
395 Assert.isMainThread();
396 mListeners.add(listener);
397 }
398
399 public void removeListener(DialerCallListener listener) {
400 Assert.isMainThread();
401 mListeners.remove(listener);
402 }
403
404 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
405 Assert.isMainThread();
406 mCannedTextResponsesLoadedListeners.add(listener);
407 }
408
409 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
410 Assert.isMainThread();
411 mCannedTextResponsesLoadedListeners.remove(listener);
412 }
413
414 public void notifyWiFiToLteHandover() {
415 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
416 for (DialerCallListener listener : mListeners) {
417 listener.onWiFiToLteHandover();
418 }
419 }
420
421 public void notifyHandoverToWifiFailed() {
422 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
423 for (DialerCallListener listener : mListeners) {
424 listener.onHandoverToWifiFailure();
425 }
426 }
427
Eric Erfanianc857f902017-05-15 14:05:33 -0700428 public void notifyInternationalCallOnWifi() {
429 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
430 for (DialerCallListener dialerCallListener : mListeners) {
431 dialerCallListener.onInternationalCallOnWifi();
432 }
433 }
434
Eric Erfanianccca3152017-02-22 16:32:36 -0800435 /* package-private */ Call getTelecomCall() {
436 return mTelecomCall;
437 }
438
439 public StatusHints getStatusHints() {
440 return mTelecomCall.getDetails().getStatusHints();
441 }
442
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700443 public int getCameraDir() {
444 return mCameraDirection;
445 }
446
447 public void setCameraDir(int cameraDir) {
448 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
449 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
450 mCameraDirection = cameraDir;
451 } else {
452 mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
453 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800454 }
455
Eric Erfanian2ca43182017-08-31 06:57:16 -0700456 public boolean wasParentCall() {
457 return mLogState.conferencedCalls != 0;
458 }
459
wangqi9982f0d2017-10-11 17:46:07 -0700460 public boolean isVoiceMailNumber() {
461 return isVoicemailNumber;
462 }
463
464 public List<PhoneAccountHandle> getCallCapableAccounts() {
465 return callCapableAccounts;
466 }
467
468 public String getCountryIso() {
469 return countryIso;
470 }
471
472 private void updateIsVoiceMailNumber() {
473 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
474 isVoicemailNumber = true;
475 }
476
477 if (!PermissionsUtil.hasPermission(mContext, permission.READ_PHONE_STATE)) {
478 isVoicemailNumber = false;
479 }
480
481 isVoicemailNumber = TelecomUtil.isVoicemailNumber(mContext, getAccountHandle(), getNumber());
482 }
483
Eric Erfanianccca3152017-02-22 16:32:36 -0800484 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700485 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800486 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700487 // Clear any cache here that could potentially change on update.
488 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800489 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700490 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800491 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
492 for (DialerCallListener listener : mListeners) {
493 listener.onDialerCallDisconnect();
494 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700495 EnrichedCallComponent.get(mContext)
496 .getEnrichedCallManager()
497 .unregisterCapabilitiesListener(this);
498 EnrichedCallComponent.get(mContext)
499 .getEnrichedCallManager()
500 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800501 } else {
502 for (DialerCallListener listener : mListeners) {
503 listener.onDialerCallUpdate();
504 }
505 }
506 Trace.endSection();
507 }
508
wangqi9982f0d2017-10-11 17:46:07 -0700509 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700510 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700511 Trace.beginSection("DialerCall.updateFromTelecomCall");
Eric Erfanianccca3152017-02-22 16:32:36 -0800512 LogUtil.v("DialerCall.updateFromTelecomCall", mTelecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700513
514 mVideoTechManager.dispatchCallStateChanged(mTelecomCall.getState());
515
Eric Erfanianccca3152017-02-22 16:32:36 -0800516 final int translatedState = translateState(mTelecomCall.getState());
517 if (mState != State.BLOCKED) {
518 setState(translatedState);
519 setDisconnectCause(mTelecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800520 }
521
522 mChildCallIds.clear();
523 final int numChildCalls = mTelecomCall.getChildren().size();
524 for (int i = 0; i < numChildCalls; i++) {
525 mChildCallIds.add(
526 mDialerCallDelegate
527 .getDialerCallFromTelecomCall(mTelecomCall.getChildren().get(i))
528 .getId());
529 }
530
531 // The number of conferenced calls can change over the course of the call, so use the
532 // maximum number of conferenced child calls as the metric for conference call usage.
533 mLogState.conferencedCalls = Math.max(numChildCalls, mLogState.conferencedCalls);
534
535 updateFromCallExtras(mTelecomCall.getDetails().getExtras());
536
537 // If the handle of the call has changed, update state for the call determining if it is an
538 // emergency call.
539 Uri newHandle = mTelecomCall.getDetails().getHandle();
540 if (!Objects.equals(mHandle, newHandle)) {
541 mHandle = newHandle;
542 updateEmergencyCallState();
543 }
544
wangqi9982f0d2017-10-11 17:46:07 -0700545 TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800546 // If the phone account handle of the call is set, cache capability bit indicating whether
547 // the phone account supports call subjects.
548 PhoneAccountHandle newPhoneAccountHandle = mTelecomCall.getDetails().getAccountHandle();
549 if (!Objects.equals(mPhoneAccountHandle, newPhoneAccountHandle)) {
550 mPhoneAccountHandle = newPhoneAccountHandle;
551
552 if (mPhoneAccountHandle != null) {
wangqi9982f0d2017-10-11 17:46:07 -0700553 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(mPhoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800554 if (phoneAccount != null) {
555 mIsCallSubjectSupported =
556 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
557 }
558 }
559 }
wangqi9982f0d2017-10-11 17:46:07 -0700560 if (PermissionsUtil.hasPermission(mContext, permission.READ_PHONE_STATE)) {
561 updateIsVoiceMailNumber();
562 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
563 countryIso = GeoUtil.getCurrentCountryIso(mContext);
564 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700565 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800566 }
567
568 /**
569 * Tests corruption of the {@code callExtras} bundle by calling {@link
570 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
571 * be thrown and caught by this function.
572 *
573 * @param callExtras the bundle to verify
574 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
575 */
576 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
577 /**
578 * There's currently a bug in Telephony service (b/25613098) that could corrupt the extras
579 * bundle, resulting in a IllegalArgumentException while validating data under {@link
580 * Bundle#containsKey(String)}.
581 */
582 try {
583 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
584 return false;
585 } catch (IllegalArgumentException e) {
586 LogUtil.e(
587 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
588 return true;
589 }
590 }
591
592 protected void updateFromCallExtras(Bundle callExtras) {
593 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
594 /**
595 * If the bundle is corrupted, abandon information update as a work around. These are not
596 * critical for the dialer to function.
597 */
598 return;
599 }
600 // Check for a change in the child address and notify any listeners.
601 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
602 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
603 if (!Objects.equals(childNumber, mChildNumber)) {
604 mChildNumber = childNumber;
605 for (DialerCallListener listener : mListeners) {
606 listener.onDialerCallChildNumberChange();
607 }
608 }
609 }
610
611 // Last forwarded number comes in as an array of strings. We want to choose the
612 // last item in the array. The forwarding numbers arrive independently of when the
613 // call is originally set up, so we need to notify the the UI of the change.
614 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
615 ArrayList<String> lastForwardedNumbers =
616 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
617
618 if (lastForwardedNumbers != null) {
619 String lastForwardedNumber = null;
620 if (!lastForwardedNumbers.isEmpty()) {
621 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
622 }
623
624 if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) {
625 mLastForwardedNumber = lastForwardedNumber;
626 for (DialerCallListener listener : mListeners) {
627 listener.onDialerCallLastForwardedNumberChange();
628 }
629 }
630 }
631 }
632
633 // DialerCall subject is present in the extras at the start of call, so we do not need to
634 // notify any other listeners of this.
635 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
636 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
637 if (!Objects.equals(mCallSubject, callSubject)) {
638 mCallSubject = callSubject;
639 }
640 }
641 }
642
Eric Erfanianccca3152017-02-22 16:32:36 -0800643 public String getId() {
644 return mId;
645 }
646
Eric Erfanianc857f902017-05-15 14:05:33 -0700647 /**
648 * @return name appended with a number if the number is restricted/unknown and the user has
649 * received more than one restricted/unknown call.
650 */
651 @Nullable
652 public String updateNameIfRestricted(@Nullable String name) {
653 if (name != null && isHiddenNumber() && mHiddenId != 0 && sHiddenCounter > 1) {
654 return mContext.getString(R.string.unknown_counter, name, mHiddenId);
655 }
656 return name;
657 }
658
659 public static void clearRestrictedCount() {
660 sHiddenCounter = 0;
661 }
662
663 private boolean isHiddenNumber() {
664 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
665 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
666 }
667
Eric Erfanianccca3152017-02-22 16:32:36 -0800668 public boolean hasShownWiFiToLteHandoverToast() {
669 return hasShownWiFiToLteHandoverToast;
670 }
671
672 public void setHasShownWiFiToLteHandoverToast() {
673 hasShownWiFiToLteHandoverToast = true;
674 }
675
676 public boolean showWifiHandoverAlertAsToast() {
677 return doNotShowDialogForHandoffToWifiFailure;
678 }
679
680 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
681 doNotShowDialogForHandoffToWifiFailure = bool;
682 }
683
684 public long getTimeAddedMs() {
685 return mTimeAddedMs;
686 }
687
688 @Nullable
689 public String getNumber() {
690 return TelecomCallUtil.getNumber(mTelecomCall);
691 }
692
693 public void blockCall() {
694 mTelecomCall.reject(false, null);
695 setState(State.BLOCKED);
696 }
697
698 @Nullable
699 public Uri getHandle() {
700 return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandle();
701 }
702
703 public boolean isEmergencyCall() {
704 return mIsEmergencyCall;
705 }
706
707 public boolean isPotentialEmergencyCallback() {
708 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
709 // is actually in emergency callback mode (ie data is disabled).
710 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
711 return true;
712 }
713 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
714 // as a potential emergency callback.
715 if (getExtras() != null
716 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
717 > 0) {
718 long lastEmergencyCallMillis =
719 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
720 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
721 return true;
722 }
723 }
724 return false;
725 }
726
727 boolean isInEmergencyCallbackWindow(long timestampMillis) {
728 long emergencyCallbackWindowMillis =
729 ConfigProviderBindings.get(mContext)
730 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
731 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
732 }
733
734 public int getState() {
735 if (mTelecomCall != null && mTelecomCall.getParent() != null) {
736 return State.CONFERENCED;
737 } else {
738 return mState;
739 }
740 }
741
yueg265089a2017-10-06 14:35:15 -0700742 public int getNonConferenceState() {
743 return mState;
744 }
745
Eric Erfanianccca3152017-02-22 16:32:36 -0800746 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700747 if (state == State.INCOMING) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800748 mLogState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700749 } else if (state == State.DISCONNECTED) {
750 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800751 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700752 if (mState != state) {
753 mLogState.duration = newDuration;
754 } else {
755 LogUtil.i(
756 "DialerCall.setState",
757 "ignoring state transition from DISCONNECTED to DISCONNECTED."
758 + " Duration would have changed from %s to %s",
759 mLogState.duration,
760 newDuration);
761 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800762 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700763 mState = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800764 }
765
766 public int getNumberPresentation() {
767 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getHandlePresentation();
768 }
769
770 public int getCnapNamePresentation() {
771 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getCallerDisplayNamePresentation();
772 }
773
774 @Nullable
775 public String getCnapName() {
776 return mTelecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
777 }
778
779 public Bundle getIntentExtras() {
780 return mTelecomCall.getDetails().getIntentExtras();
781 }
782
783 @Nullable
784 public Bundle getExtras() {
785 return mTelecomCall == null ? null : mTelecomCall.getDetails().getExtras();
786 }
787
788 /** @return The child number for the call, or {@code null} if none specified. */
789 public String getChildNumber() {
790 return mChildNumber;
791 }
792
793 /** @return The last forwarded number for the call, or {@code null} if none specified. */
794 public String getLastForwardedNumber() {
795 return mLastForwardedNumber;
796 }
797
798 /** @return The call subject, or {@code null} if none specified. */
799 public String getCallSubject() {
800 return mCallSubject;
801 }
802
803 /**
804 * @return {@code true} if the call's phone account supports call subjects, {@code false}
805 * otherwise.
806 */
807 public boolean isCallSubjectSupported() {
808 return mIsCallSubjectSupported;
809 }
810
811 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
812 public DisconnectCause getDisconnectCause() {
813 if (mState == State.DISCONNECTED || mState == State.IDLE) {
814 return mDisconnectCause;
815 }
816
817 return new DisconnectCause(DisconnectCause.UNKNOWN);
818 }
819
820 public void setDisconnectCause(DisconnectCause disconnectCause) {
821 mDisconnectCause = disconnectCause;
822 mLogState.disconnectCause = mDisconnectCause;
823 }
824
825 /** Returns the possible text message responses. */
826 public List<String> getCannedSmsResponses() {
827 return mTelecomCall.getCannedTextResponses();
828 }
829
830 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
831 public boolean can(int capabilities) {
832 int supportedCapabilities = mTelecomCall.getDetails().getCallCapabilities();
833
834 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
835 // We allow you to merge if the capabilities allow it or if it is a call with
836 // conferenceable calls.
837 if (mTelecomCall.getConferenceableCalls().isEmpty()
838 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
839 // Cannot merge calls if there are no calls to merge with.
840 return false;
841 }
842 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
843 }
844 return (capabilities == (capabilities & supportedCapabilities));
845 }
846
847 public boolean hasProperty(int property) {
848 return mTelecomCall.getDetails().hasProperty(property);
849 }
850
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700851 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800852 public String getUniqueCallId() {
853 return uniqueCallId;
854 }
855
856 /** Gets the time when the call first became active. */
857 public long getConnectTimeMillis() {
858 return mTelecomCall.getDetails().getConnectTimeMillis();
859 }
860
861 public boolean isConferenceCall() {
862 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
863 }
864
865 @Nullable
866 public GatewayInfo getGatewayInfo() {
867 return mTelecomCall == null ? null : mTelecomCall.getDetails().getGatewayInfo();
868 }
869
870 @Nullable
871 public PhoneAccountHandle getAccountHandle() {
872 return mTelecomCall == null ? null : mTelecomCall.getDetails().getAccountHandle();
873 }
874
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700875 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -0800876 public VideoCall getVideoCall() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700877 return mTelecomCall == null ? null : mTelecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800878 }
879
880 public List<String> getChildCallIds() {
881 return mChildCallIds;
882 }
883
884 public String getParentId() {
885 Call parentCall = mTelecomCall.getParent();
886 if (parentCall != null) {
887 return mDialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
888 }
889 return null;
890 }
891
892 public int getVideoState() {
893 return mTelecomCall.getDetails().getVideoState();
894 }
895
896 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -0700897 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700898 }
899
900 public boolean hasReceivedVideoUpgradeRequest() {
901 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
902 }
903
904 public boolean hasSentVideoUpgradeRequest() {
905 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -0800906 }
907
908 /**
909 * Determines if the call handle is an emergency number or not and caches the result to avoid
910 * repeated calls to isEmergencyNumber.
911 */
912 private void updateEmergencyCallState() {
913 mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
914 }
915
Eric Erfanianccca3152017-02-22 16:32:36 -0800916 public LogState getLogState() {
917 return mLogState;
918 }
919
920 /**
921 * Determines if the call is an external call.
922 *
923 * <p>An external call is one which does not exist locally for the {@link
924 * android.telecom.ConnectionService} it is associated with.
925 *
926 * <p>External calls are only supported in N and higher.
927 *
928 * @return {@code true} if the call is an external call, {@code false} otherwise.
929 */
930 public boolean isExternalCall() {
931 return VERSION.SDK_INT >= VERSION_CODES.N
932 && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
933 }
934
935 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800936 * Determines if answering this call will cause an ongoing video call to be dropped.
937 *
938 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
939 * otherwise.
940 */
941 public boolean answeringDisconnectsForegroundVideoCall() {
942 Bundle extras = getExtras();
943 if (extras == null
944 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
945 return false;
946 }
947 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
948 }
949
950 private void parseCallSpecificAppData() {
951 if (isExternalCall()) {
952 return;
953 }
954
955 mLogState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
956 if (mLogState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700957
958 mLogState.callSpecificAppData =
959 CallSpecificAppData.newBuilder()
960 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
961 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800962 }
963 if (getState() == State.INCOMING) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700964 mLogState.callSpecificAppData =
965 mLogState
966 .callSpecificAppData
967 .toBuilder()
968 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
969 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800970 }
971 }
972
973 @Override
974 public String toString() {
975 if (mTelecomCall == null) {
976 // This should happen only in testing since otherwise we would never have a null
977 // Telecom call.
978 return String.valueOf(mId);
979 }
980
981 return String.format(
982 Locale.US,
983 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700984 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
Eric Erfanianccca3152017-02-22 16:32:36 -0800985 mId,
986 State.toString(getState()),
987 Details.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
988 Details.propertiesToString(mTelecomCall.getDetails().getCallProperties()),
989 mChildCallIds,
990 getParentId(),
991 this.mTelecomCall.getConferenceableCalls(),
992 VideoProfile.videoStateToString(mTelecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700993 getVideoTech().getSessionModificationState(),
994 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -0800995 }
996
997 public String toSimpleString() {
998 return super.toString();
999 }
1000
1001 @CallHistoryStatus
1002 public int getCallHistoryStatus() {
1003 return mCallHistoryStatus;
1004 }
1005
1006 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
1007 mCallHistoryStatus = callHistoryStatus;
1008 }
1009
1010 public boolean didShowCameraPermission() {
1011 return didShowCameraPermission;
1012 }
1013
1014 public void setDidShowCameraPermission(boolean didShow) {
1015 didShowCameraPermission = didShow;
1016 }
1017
1018 public boolean isInGlobalSpamList() {
1019 return isInGlobalSpamList;
1020 }
1021
1022 public void setIsInGlobalSpamList(boolean inSpamList) {
1023 isInGlobalSpamList = inSpamList;
1024 }
1025
1026 public boolean isInUserSpamList() {
1027 return isInUserSpamList;
1028 }
1029
1030 public void setIsInUserSpamList(boolean inSpamList) {
1031 isInUserSpamList = inSpamList;
1032 }
1033
1034 public boolean isInUserWhiteList() {
1035 return isInUserWhiteList;
1036 }
1037
1038 public void setIsInUserWhiteList(boolean inWhiteList) {
1039 isInUserWhiteList = inWhiteList;
1040 }
1041
1042 public boolean isSpam() {
1043 return mIsSpam;
1044 }
1045
1046 public void setSpam(boolean isSpam) {
1047 mIsSpam = isSpam;
1048 }
1049
1050 public boolean isBlocked() {
1051 return mIsBlocked;
1052 }
1053
1054 public void setBlockedStatus(boolean isBlocked) {
1055 mIsBlocked = isBlocked;
1056 }
1057
1058 public boolean isRemotelyHeld() {
1059 return isRemotelyHeld;
1060 }
1061
Eric Erfanian2ca43182017-08-31 06:57:16 -07001062 public boolean isMergeInProcess() {
1063 return isMergeInProcess;
1064 }
1065
Eric Erfanianccca3152017-02-22 16:32:36 -08001066 public boolean isIncoming() {
1067 return mLogState.isIncoming;
1068 }
1069
Eric Erfanian2ca43182017-08-31 06:57:16 -07001070 public boolean isAssistedDialed() {
1071 if (getIntentExtras() != null) {
1072 return getIntentExtras().getBoolean(TelephonyManagerCompat.IS_ASSISTED_DIALED, false);
1073 }
1074 return false;
1075 }
1076
erfaniand0f207f2017-10-11 12:23:29 -07001077 public TransformationInfo getAssistedDialingExtras() {
1078 if (isAssistedDialed()) {
1079 return TransformationInfo.newInstanceFromBundle(
1080 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
1081 }
1082 return null;
1083 }
1084
Eric Erfanianccca3152017-02-22 16:32:36 -08001085 public LatencyReport getLatencyReport() {
1086 return mLatencyReport;
1087 }
1088
Eric Erfanian2ca43182017-08-31 06:57:16 -07001089 public int getAnswerAndReleaseButtonDisplayedTimes() {
1090 return answerAndReleaseButtonDisplayedTimes;
1091 }
1092
1093 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1094 answerAndReleaseButtonDisplayedTimes++;
1095 }
1096
1097 public boolean getReleasedByAnsweringSecondCall() {
1098 return releasedByAnsweringSecondCall;
1099 }
1100
1101 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1102 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1103 }
1104
1105 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1106 return secondCallWithoutAnswerAndReleasedButtonTimes;
1107 }
1108
1109 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1110 secondCallWithoutAnswerAndReleasedButtonTimes++;
1111 }
1112
Eric Erfanian8369df02017-05-03 10:27:13 -07001113 @Nullable
1114 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
1115 return mEnrichedCallCapabilities;
1116 }
1117
1118 public void setEnrichedCallCapabilities(
1119 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
1120 this.mEnrichedCallCapabilities = mEnrichedCallCapabilities;
1121 }
1122
1123 @Nullable
1124 public Session getEnrichedCallSession() {
1125 return mEnrichedCallSession;
1126 }
1127
1128 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
1129 this.mEnrichedCallSession = mEnrichedCallSession;
1130 }
1131
Eric Erfanianccca3152017-02-22 16:32:36 -08001132 public void unregisterCallback() {
1133 mTelecomCall.unregisterCallback(mTelecomCallCallback);
1134 }
1135
Eric Erfanianccca3152017-02-22 16:32:36 -08001136 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1137 LogUtil.i(
1138 "DialerCall.phoneAccountSelected",
1139 "accountHandle: %s, setDefault: %b",
1140 accountHandle,
1141 setDefault);
1142 mTelecomCall.phoneAccountSelected(accountHandle, setDefault);
1143 }
1144
1145 public void disconnect() {
1146 LogUtil.i("DialerCall.disconnect", "");
1147 setState(DialerCall.State.DISCONNECTING);
1148 for (DialerCallListener listener : mListeners) {
1149 listener.onDialerCallUpdate();
1150 }
1151 mTelecomCall.disconnect();
1152 }
1153
1154 public void hold() {
1155 LogUtil.i("DialerCall.hold", "");
1156 mTelecomCall.hold();
1157 }
1158
1159 public void unhold() {
1160 LogUtil.i("DialerCall.unhold", "");
1161 mTelecomCall.unhold();
1162 }
1163
1164 public void splitFromConference() {
1165 LogUtil.i("DialerCall.splitFromConference", "");
1166 mTelecomCall.splitFromConference();
1167 }
1168
1169 public void answer(int videoState) {
1170 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
1171 mTelecomCall.answer(videoState);
1172 }
1173
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001174 public void answer() {
1175 answer(mTelecomCall.getDetails().getVideoState());
1176 }
1177
Eric Erfanianccca3152017-02-22 16:32:36 -08001178 public void reject(boolean rejectWithMessage, String message) {
1179 LogUtil.i("DialerCall.reject", "");
1180 mTelecomCall.reject(rejectWithMessage, message);
1181 }
1182
1183 /** Return the string label to represent the call provider */
1184 public String getCallProviderLabel() {
1185 if (callProviderLabel == null) {
1186 PhoneAccount account = getPhoneAccount();
1187 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001188 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001189 callProviderLabel = account.getLabel().toString();
1190 }
1191 }
1192 if (callProviderLabel == null) {
1193 callProviderLabel = "";
1194 }
1195 }
1196 return callProviderLabel;
1197 }
1198
1199 private PhoneAccount getPhoneAccount() {
1200 PhoneAccountHandle accountHandle = getAccountHandle();
1201 if (accountHandle == null) {
1202 return null;
1203 }
1204 return mContext.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
1205 }
1206
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001207 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001208 if (videoTech == null) {
1209 videoTech = mVideoTechManager.getVideoTech();
1210
1211 // Only store the first video tech type found to be available during the life of the call.
1212 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1213 // Update the video tech.
1214 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1215 }
1216 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001217 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001218 }
1219
Eric Erfanianccca3152017-02-22 16:32:36 -08001220 public String getCallbackNumber() {
1221 if (callbackNumber == null) {
1222 // Show the emergency callback number if either:
1223 // 1. This is an emergency call.
1224 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1225 // number.
1226 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1227
1228 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001229 callbackNumber =
1230 mContext.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001231 }
1232
Eric Erfanianccca3152017-02-22 16:32:36 -08001233 if (callbackNumber == null) {
1234 callbackNumber = "";
1235 }
1236 }
1237 return callbackNumber;
1238 }
1239
wangqi97539352017-09-25 11:15:16 -07001240 public String getSimCountryIso() {
1241 String simCountryIso =
1242 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(
1243 mContext, getAccountHandle())
1244 .getSimCountryIso();
1245 if (!TextUtils.isEmpty(simCountryIso)) {
1246 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1247 }
1248 return simCountryIso;
1249 }
1250
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001251 @Override
1252 public void onVideoTechStateChanged() {
1253 update();
1254 }
1255
1256 @Override
1257 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001258 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001259 for (DialerCallListener listener : mListeners) {
1260 listener.onDialerCallSessionModificationStateChange();
1261 }
wangqi9982f0d2017-10-11 17:46:07 -07001262 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001263 }
1264
1265 @Override
1266 public void onCameraDimensionsChanged(int width, int height) {
1267 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1268 }
1269
1270 @Override
1271 public void onPeerDimensionsChanged(int width, int height) {
1272 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1273 }
1274
1275 @Override
1276 public void onVideoUpgradeRequestReceived() {
1277 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1278
1279 for (DialerCallListener listener : mListeners) {
1280 listener.onDialerCallUpgradeToVideo();
1281 }
1282
1283 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001284
1285 Logger.get(mContext)
1286 .logCallImpression(
1287 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1288 }
1289
1290 @Override
1291 public void onUpgradedToVideo(boolean switchToSpeaker) {
1292 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1293
1294 if (!switchToSpeaker) {
1295 return;
1296 }
1297
1298 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1299
1300 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1301 LogUtil.e(
1302 "DialerCall.onUpgradedToVideo",
1303 "toggling speakerphone not allowed when bluetooth supported.");
1304 return;
1305 }
1306
1307 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1308 return;
1309 }
1310
1311 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001312 }
1313
Eric Erfanian2ca43182017-08-31 06:57:16 -07001314 @Override
1315 public void onCapabilitiesUpdated() {
1316 if (getNumber() == null) {
1317 return;
1318 }
1319 EnrichedCallCapabilities capabilities =
1320 EnrichedCallComponent.get(mContext).getEnrichedCallManager().getCapabilities(getNumber());
1321 if (capabilities != null) {
1322 setEnrichedCallCapabilities(capabilities);
1323 update();
1324 }
1325 }
1326
1327 @Override
1328 public void onEnrichedCallStateChanged() {
1329 updateEnrichedCallSession();
1330 }
1331
1332 @Override
1333 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
1334 Logger.get(mContext).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001335 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1336 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
1337 Logger.get(mContext)
1338 .logCallImpression(
1339 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1340 getUniqueCallId(),
1341 getTimeAddedMs());
1342 }
1343 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001344 }
1345
1346 private void updateEnrichedCallSession() {
1347 if (getNumber() == null) {
1348 return;
1349 }
1350 if (getEnrichedCallSession() != null) {
1351 // State changes to existing sessions are currently handled by the UI components (which have
1352 // their own listeners). Someday instead we could remove those and just call update() here and
1353 // have the usual onDialerCallUpdate update the UI.
1354 dispatchOnEnrichedCallSessionUpdate();
1355 return;
1356 }
1357
1358 EnrichedCallManager manager = EnrichedCallComponent.get(mContext).getEnrichedCallManager();
1359
1360 Filter filter =
1361 isIncoming()
1362 ? manager.createIncomingCallComposerFilter()
1363 : manager.createOutgoingCallComposerFilter();
1364
1365 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1366 if (session == null) {
1367 return;
1368 }
1369
1370 session.setUniqueDialerCallId(getUniqueCallId());
1371 setEnrichedCallSession(session);
1372
1373 LogUtil.i(
1374 "DialerCall.updateEnrichedCallSession",
1375 "setting session %d's dialer id to %s",
1376 session.getSessionId(),
1377 getUniqueCallId());
1378
1379 dispatchOnEnrichedCallSessionUpdate();
1380 }
1381
1382 private void dispatchOnEnrichedCallSessionUpdate() {
1383 for (DialerCallListener listener : mListeners) {
1384 listener.onEnrichedCallSessionUpdate();
1385 }
1386 }
1387
1388 void onRemovedFromCallList() {
1389 // Ensure we clean up when this call is removed.
1390 mVideoTechManager.dispatchRemovedFromCallList();
1391 }
1392
wangqi4d705e52017-09-28 12:23:35 -07001393 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1394 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001395 }
1396
Eric Erfanianccca3152017-02-22 16:32:36 -08001397 /**
1398 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1399 * means there is no result.
1400 */
1401 @IntDef({
1402 CALL_HISTORY_STATUS_UNKNOWN,
1403 CALL_HISTORY_STATUS_PRESENT,
1404 CALL_HISTORY_STATUS_NOT_PRESENT
1405 })
1406 @Retention(RetentionPolicy.SOURCE)
1407 public @interface CallHistoryStatus {}
1408
1409 /* Defines different states of this call */
1410 public static class State {
1411
1412 public static final int INVALID = 0;
1413 public static final int NEW = 1; /* The call is new. */
1414 public static final int IDLE = 2; /* The call is idle. Nothing active */
1415 public static final int ACTIVE = 3; /* There is an active call */
1416 public static final int INCOMING = 4; /* A normal incoming phone call */
1417 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1418 public static final int DIALING = 6; /* An outgoing call during dial phase */
1419 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1420 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1421 public static final int DISCONNECTING = 9; /* A call is being ended. */
1422 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1423 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1424 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1425 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1426 public static final int BLOCKED = 14; /* The number was found on the block list */
1427 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001428 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001429
1430 public static boolean isConnectingOrConnected(int state) {
1431 switch (state) {
1432 case ACTIVE:
1433 case INCOMING:
1434 case CALL_WAITING:
1435 case CONNECTING:
1436 case DIALING:
1437 case PULLING:
1438 case REDIALING:
1439 case ONHOLD:
1440 case CONFERENCED:
1441 return true;
1442 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001443 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001444 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001445 }
1446
1447 public static boolean isDialing(int state) {
1448 return state == DIALING || state == PULLING || state == REDIALING;
1449 }
1450
1451 public static String toString(int state) {
1452 switch (state) {
1453 case INVALID:
1454 return "INVALID";
1455 case NEW:
1456 return "NEW";
1457 case IDLE:
1458 return "IDLE";
1459 case ACTIVE:
1460 return "ACTIVE";
1461 case INCOMING:
1462 return "INCOMING";
1463 case CALL_WAITING:
1464 return "CALL_WAITING";
1465 case DIALING:
1466 return "DIALING";
1467 case PULLING:
1468 return "PULLING";
1469 case REDIALING:
1470 return "REDIALING";
1471 case ONHOLD:
1472 return "ONHOLD";
1473 case DISCONNECTING:
1474 return "DISCONNECTING";
1475 case DISCONNECTED:
1476 return "DISCONNECTED";
1477 case CONFERENCED:
1478 return "CONFERENCED";
1479 case SELECT_PHONE_ACCOUNT:
1480 return "SELECT_PHONE_ACCOUNT";
1481 case CONNECTING:
1482 return "CONNECTING";
1483 case BLOCKED:
1484 return "BLOCKED";
1485 default:
1486 return "UNKNOWN";
1487 }
1488 }
1489 }
1490
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001491 /** Camera direction constants */
1492 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001493 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1494 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1495 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001496 }
1497
1498 /**
1499 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1500 * existing call member variables, but this duplication helps to ensure that none of these logging
1501 * variables will interface with/and affect call logic.
1502 */
1503 public static class LogState {
1504
1505 public DisconnectCause disconnectCause;
1506 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001507 public ContactLookupResult.Type contactLookupResult =
1508 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001509 public CallSpecificAppData callSpecificAppData;
1510 // If this was a conference call, the total number of calls involved in the conference.
1511 public int conferencedCalls = 0;
1512 public long duration = 0;
1513 public boolean isLogged = false;
1514
Eric Erfanian8369df02017-05-03 10:27:13 -07001515 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001516 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001517 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001518 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001519 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001520 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001521 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001522 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001523 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001524 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001525 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001526 return "Voicemail";
1527 default:
1528 return "Not found";
1529 }
1530 }
1531
1532 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1533 if (callSpecificAppData == null) {
1534 return "null";
1535 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001536 switch (callSpecificAppData.getCallInitiationType()) {
1537 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001538 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001539 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001540 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001541 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001542 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001543 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001544 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001545 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001546 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001547 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001548 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001549 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001550 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001551 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001552 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001553 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001554 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001555 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001556 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001557 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001558 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001559 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001560 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001561 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001562 return "Launcher Shortcut";
1563 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001564 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001565 }
1566 }
1567
1568 @Override
1569 public String toString() {
1570 return String.format(
1571 Locale.US,
1572 "["
1573 + "%s, " // DisconnectCause toString already describes the object type
1574 + "isIncoming: %s, "
1575 + "contactLookup: %s, "
1576 + "callInitiation: %s, "
1577 + "duration: %s"
1578 + "]",
1579 disconnectCause,
1580 isIncoming,
1581 lookupToString(contactLookupResult),
1582 initiationToString(callSpecificAppData),
1583 duration);
1584 }
1585 }
1586
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001587 private static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001588 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001589 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
Eric Erfanian90508232017-03-24 09:31:16 -07001590 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001591 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001592
1593 VideoTechManager(DialerCall call) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001594 this.context = call.mContext;
1595
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001596 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001597 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001598 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001599
1600 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001601 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001602
wangqi4d705e52017-09-28 12:23:35 -07001603 videoTechs.add(new ImsVideoTech(Logger.get(call.mContext), call, call.mTelecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001604
1605 VideoTech rcsVideoTech =
1606 EnrichedCallComponent.get(call.mContext)
1607 .getRcsVideoShareFactory()
1608 .newRcsVideoShare(
1609 EnrichedCallComponent.get(call.mContext).getEnrichedCallManager(),
1610 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001611 phoneNumber);
Eric Erfanian90508232017-03-24 09:31:16 -07001612 if (rcsVideoTech != null) {
1613 videoTechs.add(rcsVideoTech);
1614 }
Eric Erfaniand8046e52017-04-06 09:41:50 -07001615
1616 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001617 new DuoVideoTech(
1618 DuoComponent.get(call.mContext).getDuo(), call, call.mTelecomCall, phoneNumber));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001619 }
1620
roldenburg7bb96232017-10-09 10:32:05 -07001621 VideoTech getVideoTech() {
1622 if (savedTech != null) {
1623 return savedTech;
1624 }
1625
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001626 for (VideoTech tech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001627 if (tech.isAvailable(context)) {
roldenburg7bb96232017-10-09 10:32:05 -07001628 // Remember the first VideoTech that becomes available and always use it
1629 savedTech = tech;
1630 savedTech.becomePrimary();
1631 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001632 }
1633 }
1634
1635 return emptyVideoTech;
1636 }
1637
1638 void dispatchCallStateChanged(int newState) {
1639 for (VideoTech videoTech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001640 videoTech.onCallStateChanged(context, newState);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001641 }
1642 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001643
1644 void dispatchRemovedFromCallList() {
1645 for (VideoTech videoTech : videoTechs) {
1646 videoTech.onRemovedFromCallList();
1647 }
1648 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001649 }
1650
Eric Erfanianccca3152017-02-22 16:32:36 -08001651 /** Called when canned text responses have been loaded. */
1652 public interface CannedTextResponsesLoadedListener {
1653 void onCannedTextResponsesLoaded(DialerCall call);
1654 }
1655}