blob: 6829203ffdffd893dae1ca04abc646b7f8f87407 [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
19import android.content.Context;
20import android.hardware.camera2.CameraCharacteristics;
21import android.net.Uri;
22import android.os.Build.VERSION;
23import android.os.Build.VERSION_CODES;
24import android.os.Bundle;
25import android.os.Trace;
26import android.support.annotation.IntDef;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070027import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080028import android.support.annotation.Nullable;
twyendde01c52017-09-22 10:07:31 -070029import android.support.annotation.VisibleForTesting;
Eric Erfanianccca3152017-02-22 16:32:36 -080030import android.telecom.Call;
31import android.telecom.Call.Details;
Eric Erfanian8369df02017-05-03 10:27:13 -070032import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080033import android.telecom.Connection;
34import android.telecom.DisconnectCause;
35import android.telecom.GatewayInfo;
36import android.telecom.InCallService.VideoCall;
37import android.telecom.PhoneAccount;
38import android.telecom.PhoneAccountHandle;
39import android.telecom.StatusHints;
40import android.telecom.TelecomManager;
41import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080042import android.text.TextUtils;
43import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080044import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
erfaniand0f207f2017-10-11 12:23:29 -070045import com.android.dialer.assisteddialing.TransformationInfo;
Eric Erfanian8369df02017-05-03 10:27:13 -070046import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080047import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070048import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080049import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080050import com.android.dialer.common.LogUtil;
Eric Erfanian2ca43182017-08-31 06:57:16 -070051import com.android.dialer.compat.telephony.TelephonyManagerCompat;
52import com.android.dialer.configprovider.ConfigProviderBindings;
Eric Erfanian8369df02017-05-03 10:27:13 -070053import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070054import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070055import com.android.dialer.enrichedcall.EnrichedCallManager;
56import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
57import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
58import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070059import com.android.dialer.enrichedcall.Session;
Eric Erfaniand8046e52017-04-06 09:41:50 -070060import com.android.dialer.lightbringer.LightbringerComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070061import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070062import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070063import com.android.dialer.logging.DialerImpression;
64import com.android.dialer.logging.Logger;
Eric Erfanianc857f902017-05-15 14:05:33 -070065import com.android.dialer.theme.R;
Eric Erfanian8369df02017-05-03 10:27:13 -070066import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080067import com.android.incallui.latencyreport.LatencyReport;
68import com.android.incallui.util.TelecomCallUtil;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070069import com.android.incallui.videotech.VideoTech;
70import com.android.incallui.videotech.VideoTech.VideoTechListener;
71import com.android.incallui.videotech.empty.EmptyVideoTech;
72import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfaniand8046e52017-04-06 09:41:50 -070073import com.android.incallui.videotech.lightbringer.LightbringerTech;
Eric Erfanian90508232017-03-24 09:31:16 -070074import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080075import java.lang.annotation.Retention;
76import java.lang.annotation.RetentionPolicy;
77import java.util.ArrayList;
78import java.util.List;
79import java.util.Locale;
80import java.util.Objects;
81import java.util.UUID;
82import java.util.concurrent.CopyOnWriteArrayList;
83import java.util.concurrent.TimeUnit;
84
85/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -070086public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -080087
88 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
89 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
90 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070091
92 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
93 // TODO(b/35359461): Move it to Telecom in framework.
94 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
95
Eric Erfanianccca3152017-02-22 16:32:36 -080096 private static final String ID_PREFIX = "DialerCall_";
97 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
98 "emergency_callback_window_millis";
99 private static int sIdCounter = 0;
100
101 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700102 * A counter used to append to restricted/private/hidden calls so that users can identify them in
103 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
104 * are no live calls.
105 */
106 private static int sHiddenCounter;
107
108 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800109 * The unique call ID for every call. This will help us to identify each call and allow us the
110 * ability to stitch impressions to calls if needed.
111 */
112 private final String uniqueCallId = UUID.randomUUID().toString();
113
114 private final Call mTelecomCall;
115 private final LatencyReport mLatencyReport;
116 private final String mId;
Eric Erfanianc857f902017-05-15 14:05:33 -0700117 private final int mHiddenId;
Eric Erfanianccca3152017-02-22 16:32:36 -0800118 private final List<String> mChildCallIds = new ArrayList<>();
Eric Erfanianccca3152017-02-22 16:32:36 -0800119 private final LogState mLogState = new LogState();
120 private final Context mContext;
121 private final DialerCallDelegate mDialerCallDelegate;
122 private final List<DialerCallListener> mListeners = new CopyOnWriteArrayList<>();
123 private final List<CannedTextResponsesLoadedListener> mCannedTextResponsesLoadedListeners =
124 new CopyOnWriteArrayList<>();
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700125 private final VideoTechManager mVideoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800126
127 private boolean mIsEmergencyCall;
128 private Uri mHandle;
129 private int mState = State.INVALID;
130 private DisconnectCause mDisconnectCause;
131
132 private boolean hasShownWiFiToLteHandoverToast;
133 private boolean doNotShowDialogForHandoffToWifiFailure;
134
Eric Erfanianccca3152017-02-22 16:32:36 -0800135 private String mChildNumber;
136 private String mLastForwardedNumber;
137 private String mCallSubject;
138 private PhoneAccountHandle mPhoneAccountHandle;
139 @CallHistoryStatus private int mCallHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
140 private boolean mIsSpam;
141 private boolean mIsBlocked;
142 private boolean isInUserSpamList;
143 private boolean isInUserWhiteList;
144 private boolean isInGlobalSpamList;
145 private boolean didShowCameraPermission;
146 private String callProviderLabel;
147 private String callbackNumber;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700148 private int mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfanian8369df02017-05-03 10:27:13 -0700149 private EnrichedCallCapabilities mEnrichedCallCapabilities;
150 private Session mEnrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800151
Eric Erfanian2ca43182017-08-31 06:57:16 -0700152 private int answerAndReleaseButtonDisplayedTimes = 0;
153 private boolean releasedByAnsweringSecondCall = false;
154 // Times when a second call is received but AnswerAndRelease button is not shown
155 // since it's not supported.
156 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700157 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700158
wangqi4d705e52017-09-28 12:23:35 -0700159 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
160 com.android.dialer.logging.VideoTech.Type.NONE;
yueg457b3972017-09-18 15:11:47 -0700161
Eric Erfanianccca3152017-02-22 16:32:36 -0800162 public static String getNumberFromHandle(Uri handle) {
163 return handle == null ? "" : handle.getSchemeSpecificPart();
164 }
165
166 /**
167 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700168 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800169 */
170 private boolean isRemotelyHeld;
171
Eric Erfanian2ca43182017-08-31 06:57:16 -0700172 /** Indicates whether this call is currently in the process of being merged into a conference. */
173 private boolean isMergeInProcess;
174
Eric Erfanianccca3152017-02-22 16:32:36 -0800175 /**
176 * Indicates whether the phone account associated with this call supports specifying a call
177 * subject.
178 */
179 private boolean mIsCallSubjectSupported;
180
181 private final Call.Callback mTelecomCallCallback =
182 new Call.Callback() {
183 @Override
184 public void onStateChanged(Call call, int newState) {
185 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
186 update();
187 }
188
189 @Override
190 public void onParentChanged(Call call, Call newParent) {
191 LogUtil.v(
192 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
193 update();
194 }
195
196 @Override
197 public void onChildrenChanged(Call call, List<Call> children) {
198 update();
199 }
200
201 @Override
202 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700203 LogUtil.v(
204 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800205 update();
206 }
207
208 @Override
209 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
210 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700211 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800212 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
213 for (CannedTextResponsesLoadedListener listener : mCannedTextResponsesLoadedListeners) {
214 listener.onCannedTextResponsesLoaded(DialerCall.this);
215 }
216 }
217
218 @Override
219 public void onPostDialWait(Call call, String remainingPostDialSequence) {
220 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700221 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800222 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
223 update();
224 }
225
226 @Override
227 public void onVideoCallChanged(Call call, VideoCall videoCall) {
228 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700229 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800230 update();
231 }
232
233 @Override
234 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700235 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700236 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800237 }
238
239 @Override
240 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
241 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700242 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800243 "call %s, conferenceable calls: %d",
244 call,
245 conferenceableCalls.size());
246 update();
247 }
248
249 @Override
250 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
251 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700252 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800253 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
254 switch (event) {
255 // The Previous attempt to Merge two calls together has failed in Telecom. We must
256 // now update the UI to possibly re-enable the Merge button based on the number of
257 // currently conferenceable calls available or Connection Capabilities.
258 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
259 update();
260 break;
261 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
262 notifyWiFiToLteHandover();
263 break;
264 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
265 notifyHandoverToWifiFailed();
266 break;
267 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
268 isRemotelyHeld = true;
269 update();
270 break;
271 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
272 isRemotelyHeld = false;
273 update();
274 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700275 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
276 notifyInternationalCallOnWifi();
277 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700278 case TelephonyManagerCompat.EVENT_MERGE_START:
279 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
280 isMergeInProcess = true;
281 break;
282 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
283 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
284 isMergeInProcess = false;
285 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800286 default:
287 break;
288 }
289 }
290 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700291
Eric Erfanianccca3152017-02-22 16:32:36 -0800292 private long mTimeAddedMs;
293
294 public DialerCall(
295 Context context,
296 DialerCallDelegate dialerCallDelegate,
297 Call telecomCall,
298 LatencyReport latencyReport,
299 boolean registerCallback) {
300 Assert.isNotNull(context);
301 mContext = context;
302 mDialerCallDelegate = dialerCallDelegate;
303 mTelecomCall = telecomCall;
304 mLatencyReport = latencyReport;
305 mId = ID_PREFIX + Integer.toString(sIdCounter++);
306
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700307 // Must be after assigning mTelecomCall
308 mVideoTechManager = new VideoTechManager(this);
309
310 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700311 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
312 mHiddenId = ++sHiddenCounter;
313 } else {
314 mHiddenId = 0;
315 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800316
317 if (registerCallback) {
318 mTelecomCall.registerCallback(mTelecomCallCallback);
319 }
320
321 mTimeAddedMs = System.currentTimeMillis();
322 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700323
324 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800325 }
326
twyendde01c52017-09-22 10:07:31 -0700327 /** Test only constructor to avoid initializing dependencies. */
328 @VisibleForTesting
329 DialerCall(Context context) {
330 mContext = context;
331 mTelecomCall = null;
332 mLatencyReport = null;
333 mId = null;
334 mHiddenId = 0;
335 mDialerCallDelegate = null;
336 mVideoTechManager = null;
337 }
338
Eric Erfanianccca3152017-02-22 16:32:36 -0800339 private static int translateState(int state) {
340 switch (state) {
341 case Call.STATE_NEW:
342 case Call.STATE_CONNECTING:
343 return DialerCall.State.CONNECTING;
344 case Call.STATE_SELECT_PHONE_ACCOUNT:
345 return DialerCall.State.SELECT_PHONE_ACCOUNT;
346 case Call.STATE_DIALING:
347 return DialerCall.State.DIALING;
348 case Call.STATE_PULLING_CALL:
349 return DialerCall.State.PULLING;
350 case Call.STATE_RINGING:
351 return DialerCall.State.INCOMING;
352 case Call.STATE_ACTIVE:
353 return DialerCall.State.ACTIVE;
354 case Call.STATE_HOLDING:
355 return DialerCall.State.ONHOLD;
356 case Call.STATE_DISCONNECTED:
357 return DialerCall.State.DISCONNECTED;
358 case Call.STATE_DISCONNECTING:
359 return DialerCall.State.DISCONNECTING;
360 default:
361 return DialerCall.State.INVALID;
362 }
363 }
364
365 public static boolean areSame(DialerCall call1, DialerCall call2) {
366 if (call1 == null && call2 == null) {
367 return true;
368 } else if (call1 == null || call2 == null) {
369 return false;
370 }
371
372 // otherwise compare call Ids
373 return call1.getId().equals(call2.getId());
374 }
375
376 public static boolean areSameNumber(DialerCall call1, DialerCall call2) {
377 if (call1 == null && call2 == null) {
378 return true;
379 } else if (call1 == null || call2 == null) {
380 return false;
381 }
382
383 // otherwise compare call Numbers
384 return TextUtils.equals(call1.getNumber(), call2.getNumber());
385 }
386
387 public void addListener(DialerCallListener listener) {
388 Assert.isMainThread();
389 mListeners.add(listener);
390 }
391
392 public void removeListener(DialerCallListener listener) {
393 Assert.isMainThread();
394 mListeners.remove(listener);
395 }
396
397 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
398 Assert.isMainThread();
399 mCannedTextResponsesLoadedListeners.add(listener);
400 }
401
402 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
403 Assert.isMainThread();
404 mCannedTextResponsesLoadedListeners.remove(listener);
405 }
406
407 public void notifyWiFiToLteHandover() {
408 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
409 for (DialerCallListener listener : mListeners) {
410 listener.onWiFiToLteHandover();
411 }
412 }
413
414 public void notifyHandoverToWifiFailed() {
415 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
416 for (DialerCallListener listener : mListeners) {
417 listener.onHandoverToWifiFailure();
418 }
419 }
420
Eric Erfanianc857f902017-05-15 14:05:33 -0700421 public void notifyInternationalCallOnWifi() {
422 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
423 for (DialerCallListener dialerCallListener : mListeners) {
424 dialerCallListener.onInternationalCallOnWifi();
425 }
426 }
427
Eric Erfanianccca3152017-02-22 16:32:36 -0800428 /* package-private */ Call getTelecomCall() {
429 return mTelecomCall;
430 }
431
432 public StatusHints getStatusHints() {
433 return mTelecomCall.getDetails().getStatusHints();
434 }
435
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700436 public int getCameraDir() {
437 return mCameraDirection;
438 }
439
440 public void setCameraDir(int cameraDir) {
441 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
442 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
443 mCameraDirection = cameraDir;
444 } else {
445 mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
446 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800447 }
448
Eric Erfanian2ca43182017-08-31 06:57:16 -0700449 public boolean wasParentCall() {
450 return mLogState.conferencedCalls != 0;
451 }
452
Eric Erfanianccca3152017-02-22 16:32:36 -0800453 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700454 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800455 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700456 // Clear any cache here that could potentially change on update.
457 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800458 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700459 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800460 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
461 for (DialerCallListener listener : mListeners) {
462 listener.onDialerCallDisconnect();
463 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700464 EnrichedCallComponent.get(mContext)
465 .getEnrichedCallManager()
466 .unregisterCapabilitiesListener(this);
467 EnrichedCallComponent.get(mContext)
468 .getEnrichedCallManager()
469 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800470 } else {
471 for (DialerCallListener listener : mListeners) {
472 listener.onDialerCallUpdate();
473 }
474 }
475 Trace.endSection();
476 }
477
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700478 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700479 Trace.beginSection("DialerCall.updateFromTelecomCall");
Eric Erfanianccca3152017-02-22 16:32:36 -0800480 LogUtil.v("DialerCall.updateFromTelecomCall", mTelecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700481
482 mVideoTechManager.dispatchCallStateChanged(mTelecomCall.getState());
483
Eric Erfanianccca3152017-02-22 16:32:36 -0800484 final int translatedState = translateState(mTelecomCall.getState());
485 if (mState != State.BLOCKED) {
486 setState(translatedState);
487 setDisconnectCause(mTelecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800488 }
489
490 mChildCallIds.clear();
491 final int numChildCalls = mTelecomCall.getChildren().size();
492 for (int i = 0; i < numChildCalls; i++) {
493 mChildCallIds.add(
494 mDialerCallDelegate
495 .getDialerCallFromTelecomCall(mTelecomCall.getChildren().get(i))
496 .getId());
497 }
498
499 // The number of conferenced calls can change over the course of the call, so use the
500 // maximum number of conferenced child calls as the metric for conference call usage.
501 mLogState.conferencedCalls = Math.max(numChildCalls, mLogState.conferencedCalls);
502
503 updateFromCallExtras(mTelecomCall.getDetails().getExtras());
504
505 // If the handle of the call has changed, update state for the call determining if it is an
506 // emergency call.
507 Uri newHandle = mTelecomCall.getDetails().getHandle();
508 if (!Objects.equals(mHandle, newHandle)) {
509 mHandle = newHandle;
510 updateEmergencyCallState();
511 }
512
513 // If the phone account handle of the call is set, cache capability bit indicating whether
514 // the phone account supports call subjects.
515 PhoneAccountHandle newPhoneAccountHandle = mTelecomCall.getDetails().getAccountHandle();
516 if (!Objects.equals(mPhoneAccountHandle, newPhoneAccountHandle)) {
517 mPhoneAccountHandle = newPhoneAccountHandle;
518
519 if (mPhoneAccountHandle != null) {
520 PhoneAccount phoneAccount =
521 mContext.getSystemService(TelecomManager.class).getPhoneAccount(mPhoneAccountHandle);
522 if (phoneAccount != null) {
523 mIsCallSubjectSupported =
524 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
525 }
526 }
527 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700528 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800529 }
530
531 /**
532 * Tests corruption of the {@code callExtras} bundle by calling {@link
533 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
534 * be thrown and caught by this function.
535 *
536 * @param callExtras the bundle to verify
537 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
538 */
539 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
540 /**
541 * There's currently a bug in Telephony service (b/25613098) that could corrupt the extras
542 * bundle, resulting in a IllegalArgumentException while validating data under {@link
543 * Bundle#containsKey(String)}.
544 */
545 try {
546 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
547 return false;
548 } catch (IllegalArgumentException e) {
549 LogUtil.e(
550 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
551 return true;
552 }
553 }
554
555 protected void updateFromCallExtras(Bundle callExtras) {
556 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
557 /**
558 * If the bundle is corrupted, abandon information update as a work around. These are not
559 * critical for the dialer to function.
560 */
561 return;
562 }
563 // Check for a change in the child address and notify any listeners.
564 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
565 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
566 if (!Objects.equals(childNumber, mChildNumber)) {
567 mChildNumber = childNumber;
568 for (DialerCallListener listener : mListeners) {
569 listener.onDialerCallChildNumberChange();
570 }
571 }
572 }
573
574 // Last forwarded number comes in as an array of strings. We want to choose the
575 // last item in the array. The forwarding numbers arrive independently of when the
576 // call is originally set up, so we need to notify the the UI of the change.
577 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
578 ArrayList<String> lastForwardedNumbers =
579 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
580
581 if (lastForwardedNumbers != null) {
582 String lastForwardedNumber = null;
583 if (!lastForwardedNumbers.isEmpty()) {
584 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
585 }
586
587 if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) {
588 mLastForwardedNumber = lastForwardedNumber;
589 for (DialerCallListener listener : mListeners) {
590 listener.onDialerCallLastForwardedNumberChange();
591 }
592 }
593 }
594 }
595
596 // DialerCall subject is present in the extras at the start of call, so we do not need to
597 // notify any other listeners of this.
598 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
599 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
600 if (!Objects.equals(mCallSubject, callSubject)) {
601 mCallSubject = callSubject;
602 }
603 }
604 }
605
Eric Erfanianccca3152017-02-22 16:32:36 -0800606 public String getId() {
607 return mId;
608 }
609
Eric Erfanianc857f902017-05-15 14:05:33 -0700610 /**
611 * @return name appended with a number if the number is restricted/unknown and the user has
612 * received more than one restricted/unknown call.
613 */
614 @Nullable
615 public String updateNameIfRestricted(@Nullable String name) {
616 if (name != null && isHiddenNumber() && mHiddenId != 0 && sHiddenCounter > 1) {
617 return mContext.getString(R.string.unknown_counter, name, mHiddenId);
618 }
619 return name;
620 }
621
622 public static void clearRestrictedCount() {
623 sHiddenCounter = 0;
624 }
625
626 private boolean isHiddenNumber() {
627 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
628 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
629 }
630
Eric Erfanianccca3152017-02-22 16:32:36 -0800631 public boolean hasShownWiFiToLteHandoverToast() {
632 return hasShownWiFiToLteHandoverToast;
633 }
634
635 public void setHasShownWiFiToLteHandoverToast() {
636 hasShownWiFiToLteHandoverToast = true;
637 }
638
639 public boolean showWifiHandoverAlertAsToast() {
640 return doNotShowDialogForHandoffToWifiFailure;
641 }
642
643 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
644 doNotShowDialogForHandoffToWifiFailure = bool;
645 }
646
647 public long getTimeAddedMs() {
648 return mTimeAddedMs;
649 }
650
651 @Nullable
652 public String getNumber() {
653 return TelecomCallUtil.getNumber(mTelecomCall);
654 }
655
656 public void blockCall() {
657 mTelecomCall.reject(false, null);
658 setState(State.BLOCKED);
659 }
660
661 @Nullable
662 public Uri getHandle() {
663 return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandle();
664 }
665
666 public boolean isEmergencyCall() {
667 return mIsEmergencyCall;
668 }
669
670 public boolean isPotentialEmergencyCallback() {
671 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
672 // is actually in emergency callback mode (ie data is disabled).
673 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
674 return true;
675 }
676 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
677 // as a potential emergency callback.
678 if (getExtras() != null
679 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
680 > 0) {
681 long lastEmergencyCallMillis =
682 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
683 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
684 return true;
685 }
686 }
687 return false;
688 }
689
690 boolean isInEmergencyCallbackWindow(long timestampMillis) {
691 long emergencyCallbackWindowMillis =
692 ConfigProviderBindings.get(mContext)
693 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
694 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
695 }
696
697 public int getState() {
698 if (mTelecomCall != null && mTelecomCall.getParent() != null) {
699 return State.CONFERENCED;
700 } else {
701 return mState;
702 }
703 }
704
yueg265089a2017-10-06 14:35:15 -0700705 public int getNonConferenceState() {
706 return mState;
707 }
708
Eric Erfanianccca3152017-02-22 16:32:36 -0800709 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700710 if (state == State.INCOMING) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800711 mLogState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700712 } else if (state == State.DISCONNECTED) {
713 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800714 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700715 if (mState != state) {
716 mLogState.duration = newDuration;
717 } else {
718 LogUtil.i(
719 "DialerCall.setState",
720 "ignoring state transition from DISCONNECTED to DISCONNECTED."
721 + " Duration would have changed from %s to %s",
722 mLogState.duration,
723 newDuration);
724 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800725 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700726 mState = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800727 }
728
729 public int getNumberPresentation() {
730 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getHandlePresentation();
731 }
732
733 public int getCnapNamePresentation() {
734 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getCallerDisplayNamePresentation();
735 }
736
737 @Nullable
738 public String getCnapName() {
739 return mTelecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
740 }
741
742 public Bundle getIntentExtras() {
743 return mTelecomCall.getDetails().getIntentExtras();
744 }
745
746 @Nullable
747 public Bundle getExtras() {
748 return mTelecomCall == null ? null : mTelecomCall.getDetails().getExtras();
749 }
750
751 /** @return The child number for the call, or {@code null} if none specified. */
752 public String getChildNumber() {
753 return mChildNumber;
754 }
755
756 /** @return The last forwarded number for the call, or {@code null} if none specified. */
757 public String getLastForwardedNumber() {
758 return mLastForwardedNumber;
759 }
760
761 /** @return The call subject, or {@code null} if none specified. */
762 public String getCallSubject() {
763 return mCallSubject;
764 }
765
766 /**
767 * @return {@code true} if the call's phone account supports call subjects, {@code false}
768 * otherwise.
769 */
770 public boolean isCallSubjectSupported() {
771 return mIsCallSubjectSupported;
772 }
773
774 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
775 public DisconnectCause getDisconnectCause() {
776 if (mState == State.DISCONNECTED || mState == State.IDLE) {
777 return mDisconnectCause;
778 }
779
780 return new DisconnectCause(DisconnectCause.UNKNOWN);
781 }
782
783 public void setDisconnectCause(DisconnectCause disconnectCause) {
784 mDisconnectCause = disconnectCause;
785 mLogState.disconnectCause = mDisconnectCause;
786 }
787
788 /** Returns the possible text message responses. */
789 public List<String> getCannedSmsResponses() {
790 return mTelecomCall.getCannedTextResponses();
791 }
792
793 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
794 public boolean can(int capabilities) {
795 int supportedCapabilities = mTelecomCall.getDetails().getCallCapabilities();
796
797 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
798 // We allow you to merge if the capabilities allow it or if it is a call with
799 // conferenceable calls.
800 if (mTelecomCall.getConferenceableCalls().isEmpty()
801 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
802 // Cannot merge calls if there are no calls to merge with.
803 return false;
804 }
805 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
806 }
807 return (capabilities == (capabilities & supportedCapabilities));
808 }
809
810 public boolean hasProperty(int property) {
811 return mTelecomCall.getDetails().hasProperty(property);
812 }
813
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700814 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800815 public String getUniqueCallId() {
816 return uniqueCallId;
817 }
818
819 /** Gets the time when the call first became active. */
820 public long getConnectTimeMillis() {
821 return mTelecomCall.getDetails().getConnectTimeMillis();
822 }
823
824 public boolean isConferenceCall() {
825 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
826 }
827
828 @Nullable
829 public GatewayInfo getGatewayInfo() {
830 return mTelecomCall == null ? null : mTelecomCall.getDetails().getGatewayInfo();
831 }
832
833 @Nullable
834 public PhoneAccountHandle getAccountHandle() {
835 return mTelecomCall == null ? null : mTelecomCall.getDetails().getAccountHandle();
836 }
837
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700838 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -0800839 public VideoCall getVideoCall() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700840 return mTelecomCall == null ? null : mTelecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800841 }
842
843 public List<String> getChildCallIds() {
844 return mChildCallIds;
845 }
846
847 public String getParentId() {
848 Call parentCall = mTelecomCall.getParent();
849 if (parentCall != null) {
850 return mDialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
851 }
852 return null;
853 }
854
855 public int getVideoState() {
856 return mTelecomCall.getDetails().getVideoState();
857 }
858
859 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -0700860 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700861 }
862
863 public boolean hasReceivedVideoUpgradeRequest() {
864 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
865 }
866
867 public boolean hasSentVideoUpgradeRequest() {
868 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -0800869 }
870
871 /**
872 * Determines if the call handle is an emergency number or not and caches the result to avoid
873 * repeated calls to isEmergencyNumber.
874 */
875 private void updateEmergencyCallState() {
876 mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
877 }
878
Eric Erfanianccca3152017-02-22 16:32:36 -0800879 public LogState getLogState() {
880 return mLogState;
881 }
882
883 /**
884 * Determines if the call is an external call.
885 *
886 * <p>An external call is one which does not exist locally for the {@link
887 * android.telecom.ConnectionService} it is associated with.
888 *
889 * <p>External calls are only supported in N and higher.
890 *
891 * @return {@code true} if the call is an external call, {@code false} otherwise.
892 */
893 public boolean isExternalCall() {
894 return VERSION.SDK_INT >= VERSION_CODES.N
895 && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
896 }
897
898 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800899 * Determines if answering this call will cause an ongoing video call to be dropped.
900 *
901 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
902 * otherwise.
903 */
904 public boolean answeringDisconnectsForegroundVideoCall() {
905 Bundle extras = getExtras();
906 if (extras == null
907 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
908 return false;
909 }
910 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
911 }
912
913 private void parseCallSpecificAppData() {
914 if (isExternalCall()) {
915 return;
916 }
917
918 mLogState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
919 if (mLogState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700920
921 mLogState.callSpecificAppData =
922 CallSpecificAppData.newBuilder()
923 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
924 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800925 }
926 if (getState() == State.INCOMING) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700927 mLogState.callSpecificAppData =
928 mLogState
929 .callSpecificAppData
930 .toBuilder()
931 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
932 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800933 }
934 }
935
936 @Override
937 public String toString() {
938 if (mTelecomCall == null) {
939 // This should happen only in testing since otherwise we would never have a null
940 // Telecom call.
941 return String.valueOf(mId);
942 }
943
944 return String.format(
945 Locale.US,
946 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700947 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
Eric Erfanianccca3152017-02-22 16:32:36 -0800948 mId,
949 State.toString(getState()),
950 Details.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
951 Details.propertiesToString(mTelecomCall.getDetails().getCallProperties()),
952 mChildCallIds,
953 getParentId(),
954 this.mTelecomCall.getConferenceableCalls(),
955 VideoProfile.videoStateToString(mTelecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700956 getVideoTech().getSessionModificationState(),
957 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -0800958 }
959
960 public String toSimpleString() {
961 return super.toString();
962 }
963
964 @CallHistoryStatus
965 public int getCallHistoryStatus() {
966 return mCallHistoryStatus;
967 }
968
969 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
970 mCallHistoryStatus = callHistoryStatus;
971 }
972
973 public boolean didShowCameraPermission() {
974 return didShowCameraPermission;
975 }
976
977 public void setDidShowCameraPermission(boolean didShow) {
978 didShowCameraPermission = didShow;
979 }
980
981 public boolean isInGlobalSpamList() {
982 return isInGlobalSpamList;
983 }
984
985 public void setIsInGlobalSpamList(boolean inSpamList) {
986 isInGlobalSpamList = inSpamList;
987 }
988
989 public boolean isInUserSpamList() {
990 return isInUserSpamList;
991 }
992
993 public void setIsInUserSpamList(boolean inSpamList) {
994 isInUserSpamList = inSpamList;
995 }
996
997 public boolean isInUserWhiteList() {
998 return isInUserWhiteList;
999 }
1000
1001 public void setIsInUserWhiteList(boolean inWhiteList) {
1002 isInUserWhiteList = inWhiteList;
1003 }
1004
1005 public boolean isSpam() {
1006 return mIsSpam;
1007 }
1008
1009 public void setSpam(boolean isSpam) {
1010 mIsSpam = isSpam;
1011 }
1012
1013 public boolean isBlocked() {
1014 return mIsBlocked;
1015 }
1016
1017 public void setBlockedStatus(boolean isBlocked) {
1018 mIsBlocked = isBlocked;
1019 }
1020
1021 public boolean isRemotelyHeld() {
1022 return isRemotelyHeld;
1023 }
1024
Eric Erfanian2ca43182017-08-31 06:57:16 -07001025 public boolean isMergeInProcess() {
1026 return isMergeInProcess;
1027 }
1028
Eric Erfanianccca3152017-02-22 16:32:36 -08001029 public boolean isIncoming() {
1030 return mLogState.isIncoming;
1031 }
1032
Eric Erfanian2ca43182017-08-31 06:57:16 -07001033 public boolean isAssistedDialed() {
1034 if (getIntentExtras() != null) {
1035 return getIntentExtras().getBoolean(TelephonyManagerCompat.IS_ASSISTED_DIALED, false);
1036 }
1037 return false;
1038 }
1039
erfaniand0f207f2017-10-11 12:23:29 -07001040 public TransformationInfo getAssistedDialingExtras() {
1041 if (isAssistedDialed()) {
1042 return TransformationInfo.newInstanceFromBundle(
1043 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
1044 }
1045 return null;
1046 }
1047
Eric Erfanianccca3152017-02-22 16:32:36 -08001048 public LatencyReport getLatencyReport() {
1049 return mLatencyReport;
1050 }
1051
Eric Erfanian2ca43182017-08-31 06:57:16 -07001052 public int getAnswerAndReleaseButtonDisplayedTimes() {
1053 return answerAndReleaseButtonDisplayedTimes;
1054 }
1055
1056 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1057 answerAndReleaseButtonDisplayedTimes++;
1058 }
1059
1060 public boolean getReleasedByAnsweringSecondCall() {
1061 return releasedByAnsweringSecondCall;
1062 }
1063
1064 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1065 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1066 }
1067
1068 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1069 return secondCallWithoutAnswerAndReleasedButtonTimes;
1070 }
1071
1072 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1073 secondCallWithoutAnswerAndReleasedButtonTimes++;
1074 }
1075
Eric Erfanian8369df02017-05-03 10:27:13 -07001076 @Nullable
1077 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
1078 return mEnrichedCallCapabilities;
1079 }
1080
1081 public void setEnrichedCallCapabilities(
1082 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
1083 this.mEnrichedCallCapabilities = mEnrichedCallCapabilities;
1084 }
1085
1086 @Nullable
1087 public Session getEnrichedCallSession() {
1088 return mEnrichedCallSession;
1089 }
1090
1091 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
1092 this.mEnrichedCallSession = mEnrichedCallSession;
1093 }
1094
Eric Erfanianccca3152017-02-22 16:32:36 -08001095 public void unregisterCallback() {
1096 mTelecomCall.unregisterCallback(mTelecomCallCallback);
1097 }
1098
Eric Erfanianccca3152017-02-22 16:32:36 -08001099 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1100 LogUtil.i(
1101 "DialerCall.phoneAccountSelected",
1102 "accountHandle: %s, setDefault: %b",
1103 accountHandle,
1104 setDefault);
1105 mTelecomCall.phoneAccountSelected(accountHandle, setDefault);
1106 }
1107
1108 public void disconnect() {
1109 LogUtil.i("DialerCall.disconnect", "");
1110 setState(DialerCall.State.DISCONNECTING);
1111 for (DialerCallListener listener : mListeners) {
1112 listener.onDialerCallUpdate();
1113 }
1114 mTelecomCall.disconnect();
1115 }
1116
1117 public void hold() {
1118 LogUtil.i("DialerCall.hold", "");
1119 mTelecomCall.hold();
1120 }
1121
1122 public void unhold() {
1123 LogUtil.i("DialerCall.unhold", "");
1124 mTelecomCall.unhold();
1125 }
1126
1127 public void splitFromConference() {
1128 LogUtil.i("DialerCall.splitFromConference", "");
1129 mTelecomCall.splitFromConference();
1130 }
1131
1132 public void answer(int videoState) {
1133 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
1134 mTelecomCall.answer(videoState);
1135 }
1136
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001137 public void answer() {
1138 answer(mTelecomCall.getDetails().getVideoState());
1139 }
1140
Eric Erfanianccca3152017-02-22 16:32:36 -08001141 public void reject(boolean rejectWithMessage, String message) {
1142 LogUtil.i("DialerCall.reject", "");
1143 mTelecomCall.reject(rejectWithMessage, message);
1144 }
1145
1146 /** Return the string label to represent the call provider */
1147 public String getCallProviderLabel() {
1148 if (callProviderLabel == null) {
1149 PhoneAccount account = getPhoneAccount();
1150 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
1151 List<PhoneAccountHandle> accounts =
1152 mContext.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts();
1153 if (accounts != null && accounts.size() > 1) {
1154 callProviderLabel = account.getLabel().toString();
1155 }
1156 }
1157 if (callProviderLabel == null) {
1158 callProviderLabel = "";
1159 }
1160 }
1161 return callProviderLabel;
1162 }
1163
1164 private PhoneAccount getPhoneAccount() {
1165 PhoneAccountHandle accountHandle = getAccountHandle();
1166 if (accountHandle == null) {
1167 return null;
1168 }
1169 return mContext.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
1170 }
1171
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001172 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001173 if (videoTech == null) {
1174 videoTech = mVideoTechManager.getVideoTech();
1175
1176 // Only store the first video tech type found to be available during the life of the call.
1177 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1178 // Update the video tech.
1179 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1180 }
1181 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001182 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001183 }
1184
Eric Erfanianccca3152017-02-22 16:32:36 -08001185 public String getCallbackNumber() {
1186 if (callbackNumber == null) {
1187 // Show the emergency callback number if either:
1188 // 1. This is an emergency call.
1189 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1190 // number.
1191 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1192
1193 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001194 callbackNumber =
1195 mContext.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001196 }
1197
Eric Erfanianccca3152017-02-22 16:32:36 -08001198 if (callbackNumber == null) {
1199 callbackNumber = "";
1200 }
1201 }
1202 return callbackNumber;
1203 }
1204
wangqi97539352017-09-25 11:15:16 -07001205 public String getSimCountryIso() {
1206 String simCountryIso =
1207 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(
1208 mContext, getAccountHandle())
1209 .getSimCountryIso();
1210 if (!TextUtils.isEmpty(simCountryIso)) {
1211 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1212 }
1213 return simCountryIso;
1214 }
1215
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001216 @Override
1217 public void onVideoTechStateChanged() {
1218 update();
1219 }
1220
1221 @Override
1222 public void onSessionModificationStateChanged() {
1223 for (DialerCallListener listener : mListeners) {
1224 listener.onDialerCallSessionModificationStateChange();
1225 }
1226 }
1227
1228 @Override
1229 public void onCameraDimensionsChanged(int width, int height) {
1230 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1231 }
1232
1233 @Override
1234 public void onPeerDimensionsChanged(int width, int height) {
1235 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1236 }
1237
1238 @Override
1239 public void onVideoUpgradeRequestReceived() {
1240 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1241
1242 for (DialerCallListener listener : mListeners) {
1243 listener.onDialerCallUpgradeToVideo();
1244 }
1245
1246 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001247
1248 Logger.get(mContext)
1249 .logCallImpression(
1250 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1251 }
1252
1253 @Override
1254 public void onUpgradedToVideo(boolean switchToSpeaker) {
1255 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1256
1257 if (!switchToSpeaker) {
1258 return;
1259 }
1260
1261 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1262
1263 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1264 LogUtil.e(
1265 "DialerCall.onUpgradedToVideo",
1266 "toggling speakerphone not allowed when bluetooth supported.");
1267 return;
1268 }
1269
1270 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1271 return;
1272 }
1273
1274 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001275 }
1276
Eric Erfanian2ca43182017-08-31 06:57:16 -07001277 @Override
1278 public void onCapabilitiesUpdated() {
1279 if (getNumber() == null) {
1280 return;
1281 }
1282 EnrichedCallCapabilities capabilities =
1283 EnrichedCallComponent.get(mContext).getEnrichedCallManager().getCapabilities(getNumber());
1284 if (capabilities != null) {
1285 setEnrichedCallCapabilities(capabilities);
1286 update();
1287 }
1288 }
1289
1290 @Override
1291 public void onEnrichedCallStateChanged() {
1292 updateEnrichedCallSession();
1293 }
1294
1295 @Override
1296 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
1297 Logger.get(mContext).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001298 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1299 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
1300 Logger.get(mContext)
1301 .logCallImpression(
1302 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1303 getUniqueCallId(),
1304 getTimeAddedMs());
1305 }
1306 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001307 }
1308
1309 private void updateEnrichedCallSession() {
1310 if (getNumber() == null) {
1311 return;
1312 }
1313 if (getEnrichedCallSession() != null) {
1314 // State changes to existing sessions are currently handled by the UI components (which have
1315 // their own listeners). Someday instead we could remove those and just call update() here and
1316 // have the usual onDialerCallUpdate update the UI.
1317 dispatchOnEnrichedCallSessionUpdate();
1318 return;
1319 }
1320
1321 EnrichedCallManager manager = EnrichedCallComponent.get(mContext).getEnrichedCallManager();
1322
1323 Filter filter =
1324 isIncoming()
1325 ? manager.createIncomingCallComposerFilter()
1326 : manager.createOutgoingCallComposerFilter();
1327
1328 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1329 if (session == null) {
1330 return;
1331 }
1332
1333 session.setUniqueDialerCallId(getUniqueCallId());
1334 setEnrichedCallSession(session);
1335
1336 LogUtil.i(
1337 "DialerCall.updateEnrichedCallSession",
1338 "setting session %d's dialer id to %s",
1339 session.getSessionId(),
1340 getUniqueCallId());
1341
1342 dispatchOnEnrichedCallSessionUpdate();
1343 }
1344
1345 private void dispatchOnEnrichedCallSessionUpdate() {
1346 for (DialerCallListener listener : mListeners) {
1347 listener.onEnrichedCallSessionUpdate();
1348 }
1349 }
1350
1351 void onRemovedFromCallList() {
1352 // Ensure we clean up when this call is removed.
1353 mVideoTechManager.dispatchRemovedFromCallList();
1354 }
1355
wangqi4d705e52017-09-28 12:23:35 -07001356 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1357 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001358 }
1359
Eric Erfanianccca3152017-02-22 16:32:36 -08001360 /**
1361 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1362 * means there is no result.
1363 */
1364 @IntDef({
1365 CALL_HISTORY_STATUS_UNKNOWN,
1366 CALL_HISTORY_STATUS_PRESENT,
1367 CALL_HISTORY_STATUS_NOT_PRESENT
1368 })
1369 @Retention(RetentionPolicy.SOURCE)
1370 public @interface CallHistoryStatus {}
1371
1372 /* Defines different states of this call */
1373 public static class State {
1374
1375 public static final int INVALID = 0;
1376 public static final int NEW = 1; /* The call is new. */
1377 public static final int IDLE = 2; /* The call is idle. Nothing active */
1378 public static final int ACTIVE = 3; /* There is an active call */
1379 public static final int INCOMING = 4; /* A normal incoming phone call */
1380 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1381 public static final int DIALING = 6; /* An outgoing call during dial phase */
1382 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1383 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1384 public static final int DISCONNECTING = 9; /* A call is being ended. */
1385 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1386 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1387 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1388 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1389 public static final int BLOCKED = 14; /* The number was found on the block list */
1390 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001391 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001392
1393 public static boolean isConnectingOrConnected(int state) {
1394 switch (state) {
1395 case ACTIVE:
1396 case INCOMING:
1397 case CALL_WAITING:
1398 case CONNECTING:
1399 case DIALING:
1400 case PULLING:
1401 case REDIALING:
1402 case ONHOLD:
1403 case CONFERENCED:
1404 return true;
1405 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001406 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001407 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001408 }
1409
1410 public static boolean isDialing(int state) {
1411 return state == DIALING || state == PULLING || state == REDIALING;
1412 }
1413
1414 public static String toString(int state) {
1415 switch (state) {
1416 case INVALID:
1417 return "INVALID";
1418 case NEW:
1419 return "NEW";
1420 case IDLE:
1421 return "IDLE";
1422 case ACTIVE:
1423 return "ACTIVE";
1424 case INCOMING:
1425 return "INCOMING";
1426 case CALL_WAITING:
1427 return "CALL_WAITING";
1428 case DIALING:
1429 return "DIALING";
1430 case PULLING:
1431 return "PULLING";
1432 case REDIALING:
1433 return "REDIALING";
1434 case ONHOLD:
1435 return "ONHOLD";
1436 case DISCONNECTING:
1437 return "DISCONNECTING";
1438 case DISCONNECTED:
1439 return "DISCONNECTED";
1440 case CONFERENCED:
1441 return "CONFERENCED";
1442 case SELECT_PHONE_ACCOUNT:
1443 return "SELECT_PHONE_ACCOUNT";
1444 case CONNECTING:
1445 return "CONNECTING";
1446 case BLOCKED:
1447 return "BLOCKED";
1448 default:
1449 return "UNKNOWN";
1450 }
1451 }
1452 }
1453
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001454 /** Camera direction constants */
1455 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001456 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1457 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1458 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001459 }
1460
1461 /**
1462 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1463 * existing call member variables, but this duplication helps to ensure that none of these logging
1464 * variables will interface with/and affect call logic.
1465 */
1466 public static class LogState {
1467
1468 public DisconnectCause disconnectCause;
1469 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001470 public ContactLookupResult.Type contactLookupResult =
1471 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001472 public CallSpecificAppData callSpecificAppData;
1473 // If this was a conference call, the total number of calls involved in the conference.
1474 public int conferencedCalls = 0;
1475 public long duration = 0;
1476 public boolean isLogged = false;
1477
Eric Erfanian8369df02017-05-03 10:27:13 -07001478 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001479 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001480 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001481 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001482 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001483 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001484 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001485 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001486 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001487 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001488 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001489 return "Voicemail";
1490 default:
1491 return "Not found";
1492 }
1493 }
1494
1495 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1496 if (callSpecificAppData == null) {
1497 return "null";
1498 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001499 switch (callSpecificAppData.getCallInitiationType()) {
1500 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001501 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001502 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001503 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001504 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001505 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001506 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001507 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001508 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001509 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001510 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001511 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001512 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001513 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001514 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001515 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001516 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001517 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001518 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001519 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001520 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001521 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001522 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001523 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001524 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001525 return "Launcher Shortcut";
1526 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001527 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001528 }
1529 }
1530
1531 @Override
1532 public String toString() {
1533 return String.format(
1534 Locale.US,
1535 "["
1536 + "%s, " // DisconnectCause toString already describes the object type
1537 + "isIncoming: %s, "
1538 + "contactLookup: %s, "
1539 + "callInitiation: %s, "
1540 + "duration: %s"
1541 + "]",
1542 disconnectCause,
1543 isIncoming,
1544 lookupToString(contactLookupResult),
1545 initiationToString(callSpecificAppData),
1546 duration);
1547 }
1548 }
1549
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001550 private static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001551 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001552 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
Eric Erfanian90508232017-03-24 09:31:16 -07001553 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001554 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001555
1556 VideoTechManager(DialerCall call) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001557 this.context = call.mContext;
1558
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001559 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001560 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001561 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001562
1563 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001564 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001565
wangqi4d705e52017-09-28 12:23:35 -07001566 videoTechs.add(new ImsVideoTech(Logger.get(call.mContext), call, call.mTelecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001567
1568 VideoTech rcsVideoTech =
1569 EnrichedCallComponent.get(call.mContext)
1570 .getRcsVideoShareFactory()
1571 .newRcsVideoShare(
1572 EnrichedCallComponent.get(call.mContext).getEnrichedCallManager(),
1573 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001574 phoneNumber);
Eric Erfanian90508232017-03-24 09:31:16 -07001575 if (rcsVideoTech != null) {
1576 videoTechs.add(rcsVideoTech);
1577 }
Eric Erfaniand8046e52017-04-06 09:41:50 -07001578
1579 videoTechs.add(
1580 new LightbringerTech(
Eric Erfanian2ca43182017-08-31 06:57:16 -07001581 LightbringerComponent.get(call.mContext).getLightbringer(),
1582 call,
1583 call.mTelecomCall,
1584 phoneNumber));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001585 }
1586
roldenburg7bb96232017-10-09 10:32:05 -07001587 VideoTech getVideoTech() {
1588 if (savedTech != null) {
1589 return savedTech;
1590 }
1591
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001592 for (VideoTech tech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001593 if (tech.isAvailable(context)) {
roldenburg7bb96232017-10-09 10:32:05 -07001594 // Remember the first VideoTech that becomes available and always use it
1595 savedTech = tech;
1596 savedTech.becomePrimary();
1597 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001598 }
1599 }
1600
1601 return emptyVideoTech;
1602 }
1603
1604 void dispatchCallStateChanged(int newState) {
1605 for (VideoTech videoTech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001606 videoTech.onCallStateChanged(context, newState);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001607 }
1608 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001609
1610 void dispatchRemovedFromCallList() {
1611 for (VideoTech videoTech : videoTechs) {
1612 videoTech.onRemovedFromCallList();
1613 }
1614 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001615 }
1616
Eric Erfanianccca3152017-02-22 16:32:36 -08001617 /** Called when canned text responses have been loaded. */
1618 public interface CannedTextResponsesLoadedListener {
1619 void onCannedTextResponsesLoaded(DialerCall call);
1620 }
1621}