blob: cbe7c57a67b1a4872e9a672bbc819b276b16ab54 [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;
erfanian2cf2c342017-12-21 12:01:33 -080023import android.os.Build;
Eric Erfanianccca3152017-02-22 16:32:36 -080024import android.os.Build.VERSION;
25import android.os.Build.VERSION_CODES;
26import android.os.Bundle;
27import android.os.Trace;
28import android.support.annotation.IntDef;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070029import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080030import android.support.annotation.Nullable;
twyendde01c52017-09-22 10:07:31 -070031import android.support.annotation.VisibleForTesting;
wangqif4ba3452018-01-09 11:26:29 -080032import android.support.v4.os.BuildCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080033import android.telecom.Call;
34import android.telecom.Call.Details;
wangqi219b8702018-02-13 09:34:41 -080035import android.telecom.Call.RttCall;
Eric Erfanian8369df02017-05-03 10:27:13 -070036import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080037import android.telecom.Connection;
38import android.telecom.DisconnectCause;
39import android.telecom.GatewayInfo;
40import android.telecom.InCallService.VideoCall;
41import android.telecom.PhoneAccount;
42import android.telecom.PhoneAccountHandle;
43import android.telecom.StatusHints;
44import android.telecom.TelecomManager;
45import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080046import android.text.TextUtils;
47import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080048import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
erfanian2cf2c342017-12-21 12:01:33 -080049import com.android.dialer.assisteddialing.ConcreteCreator;
erfaniand0f207f2017-10-11 12:23:29 -070050import com.android.dialer.assisteddialing.TransformationInfo;
Eric Erfanian8369df02017-05-03 10:27:13 -070051import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080052import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070053import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080054import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080055import com.android.dialer.common.LogUtil;
Eric Erfanian2ca43182017-08-31 06:57:16 -070056import com.android.dialer.compat.telephony.TelephonyManagerCompat;
57import com.android.dialer.configprovider.ConfigProviderBindings;
roldenburg4f026392017-10-13 18:42:20 -070058import com.android.dialer.duo.DuoComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070059import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070060import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070061import com.android.dialer.enrichedcall.EnrichedCallManager;
62import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
63import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
64import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070065import com.android.dialer.enrichedcall.Session;
wangqi9982f0d2017-10-11 17:46:07 -070066import com.android.dialer.location.GeoUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070067import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070068import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070069import com.android.dialer.logging.DialerImpression;
70import com.android.dialer.logging.Logger;
twyena4745bd2017-12-12 18:40:11 -080071import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070072import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070073import com.android.dialer.theme.R;
wangqi9982f0d2017-10-11 17:46:07 -070074import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070075import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080076import com.android.incallui.latencyreport.LatencyReport;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070077import com.android.incallui.videotech.VideoTech;
78import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070079import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070080import com.android.incallui.videotech.empty.EmptyVideoTech;
81import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070082import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080083import java.lang.annotation.Retention;
84import java.lang.annotation.RetentionPolicy;
85import java.util.ArrayList;
86import java.util.List;
87import java.util.Locale;
88import java.util.Objects;
89import java.util.UUID;
90import java.util.concurrent.CopyOnWriteArrayList;
91import java.util.concurrent.TimeUnit;
92
93/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -070094public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -080095
96 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
97 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
98 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070099
100 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -0700101 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700102 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
103
Eric Erfanianccca3152017-02-22 16:32:36 -0800104 private static final String ID_PREFIX = "DialerCall_";
105 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
106 "emergency_callback_window_millis";
linyuh183cb712017-12-27 17:02:37 -0800107 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800108
109 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700110 * A counter used to append to restricted/private/hidden calls so that users can identify them in
111 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
112 * are no live calls.
113 */
linyuh183cb712017-12-27 17:02:37 -0800114 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700115
116 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800117 * The unique call ID for every call. This will help us to identify each call and allow us the
118 * ability to stitch impressions to calls if needed.
119 */
120 private final String uniqueCallId = UUID.randomUUID().toString();
121
linyuh183cb712017-12-27 17:02:37 -0800122 private final Call telecomCall;
123 private final LatencyReport latencyReport;
124 private final String id;
125 private final int hiddenId;
126 private final List<String> childCallIds = new ArrayList<>();
127 private final LogState logState = new LogState();
128 private final Context context;
129 private final DialerCallDelegate dialerCallDelegate;
130 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
131 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800132 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800133 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800134
linyuh183cb712017-12-27 17:02:37 -0800135 private boolean isEmergencyCall;
136 private Uri handle;
137 private int state = State.INVALID;
138 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800139
140 private boolean hasShownWiFiToLteHandoverToast;
141 private boolean doNotShowDialogForHandoffToWifiFailure;
142
linyuh183cb712017-12-27 17:02:37 -0800143 private String childNumber;
144 private String lastForwardedNumber;
wangqif4ba3452018-01-09 11:26:29 -0800145 private boolean isCallForwarded;
linyuh183cb712017-12-27 17:02:37 -0800146 private String callSubject;
147 private PhoneAccountHandle phoneAccountHandle;
148 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
149 private boolean isSpam;
150 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700151
152 @Nullable private Boolean isInUserSpamList;
153
154 @Nullable private Boolean isInUserWhiteList;
155
156 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800157 private boolean didShowCameraPermission;
158 private String callProviderLabel;
159 private String callbackNumber;
linyuh183cb712017-12-27 17:02:37 -0800160 private int cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
161 private EnrichedCallCapabilities enrichedCallCapabilities;
162 private Session enrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800163
Eric Erfanian2ca43182017-08-31 06:57:16 -0700164 private int answerAndReleaseButtonDisplayedTimes = 0;
165 private boolean releasedByAnsweringSecondCall = false;
166 // Times when a second call is received but AnswerAndRelease button is not shown
167 // since it's not supported.
168 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700169 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700170
wangqi4d705e52017-09-28 12:23:35 -0700171 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
172 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700173 private boolean isVoicemailNumber;
174 private List<PhoneAccountHandle> callCapableAccounts;
175 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700176
Android Dialer974fc292018-02-01 16:12:25 -0800177 private volatile boolean feedbackRequested = false;
178
Eric Erfanianccca3152017-02-22 16:32:36 -0800179 public static String getNumberFromHandle(Uri handle) {
180 return handle == null ? "" : handle.getSchemeSpecificPart();
181 }
182
183 /**
184 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700185 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800186 */
187 private boolean isRemotelyHeld;
188
Eric Erfanian2ca43182017-08-31 06:57:16 -0700189 /** Indicates whether this call is currently in the process of being merged into a conference. */
190 private boolean isMergeInProcess;
191
Eric Erfanianccca3152017-02-22 16:32:36 -0800192 /**
193 * Indicates whether the phone account associated with this call supports specifying a call
194 * subject.
195 */
linyuh183cb712017-12-27 17:02:37 -0800196 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800197
linyuh183cb712017-12-27 17:02:37 -0800198 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800199 new Call.Callback() {
200 @Override
201 public void onStateChanged(Call call, int newState) {
202 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
203 update();
204 }
205
206 @Override
207 public void onParentChanged(Call call, Call newParent) {
208 LogUtil.v(
209 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
210 update();
211 }
212
213 @Override
214 public void onChildrenChanged(Call call, List<Call> children) {
215 update();
216 }
217
218 @Override
219 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700220 LogUtil.v(
221 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800222 update();
223 }
224
225 @Override
226 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
227 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700228 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800229 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800230 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800231 listener.onCannedTextResponsesLoaded(DialerCall.this);
232 }
233 }
234
235 @Override
236 public void onPostDialWait(Call call, String remainingPostDialSequence) {
237 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700238 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800239 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
240 update();
241 }
242
243 @Override
244 public void onVideoCallChanged(Call call, VideoCall videoCall) {
245 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700246 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800247 update();
248 }
249
250 @Override
251 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700252 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700253 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800254 }
255
256 @Override
257 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
258 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700259 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800260 "call %s, conferenceable calls: %d",
261 call,
262 conferenceableCalls.size());
263 update();
264 }
265
266 @Override
wangqi219b8702018-02-13 09:34:41 -0800267 public void onRttModeChanged(Call call, int mode) {
268 LogUtil.v("TelecomCallCallback.onRttModeChanged", "mode=%d", mode);
269 }
270
271 @Override
272 public void onRttRequest(Call call, int id) {
273 LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id);
274 }
275
276 @Override
277 public void onRttInitiationFailure(Call call, int reason) {
278 LogUtil.v("TelecomCallCallback.onRttInitiationFailure", "reason=%d", reason);
279 update();
280 }
281
282 @Override
283 public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {
284 LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled);
285 update();
286 }
287
288 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800289 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
290 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700291 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800292 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
293 switch (event) {
294 // The Previous attempt to Merge two calls together has failed in Telecom. We must
295 // now update the UI to possibly re-enable the Merge button based on the number of
296 // currently conferenceable calls available or Connection Capabilities.
297 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
298 update();
299 break;
300 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
301 notifyWiFiToLteHandover();
302 break;
303 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
304 notifyHandoverToWifiFailed();
305 break;
306 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
307 isRemotelyHeld = true;
308 update();
309 break;
310 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
311 isRemotelyHeld = false;
312 update();
313 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700314 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
315 notifyInternationalCallOnWifi();
316 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700317 case TelephonyManagerCompat.EVENT_MERGE_START:
318 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
319 isMergeInProcess = true;
320 break;
321 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
322 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
323 isMergeInProcess = false;
324 break;
wangqif4ba3452018-01-09 11:26:29 -0800325 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
326 // Only handle this event for P+ since it's unreliable pre-P.
327 if (BuildCompat.isAtLeastP()) {
328 isCallForwarded = true;
329 update();
330 }
331 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800332 default:
333 break;
334 }
335 }
336 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700337
linyuh183cb712017-12-27 17:02:37 -0800338 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800339
340 public DialerCall(
341 Context context,
342 DialerCallDelegate dialerCallDelegate,
343 Call telecomCall,
344 LatencyReport latencyReport,
345 boolean registerCallback) {
346 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800347 this.context = context;
348 this.dialerCallDelegate = dialerCallDelegate;
349 this.telecomCall = telecomCall;
350 this.latencyReport = latencyReport;
351 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800352
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700353 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800354 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700355
356 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700357 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800358 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700359 } else {
linyuh183cb712017-12-27 17:02:37 -0800360 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700361 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800362
363 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800364 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800365 }
366
linyuh183cb712017-12-27 17:02:37 -0800367 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800368 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700369
370 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800371 }
372
twyendde01c52017-09-22 10:07:31 -0700373 /** Test only constructor to avoid initializing dependencies. */
374 @VisibleForTesting
375 DialerCall(Context context) {
linyuh183cb712017-12-27 17:02:37 -0800376 this.context = context;
377 telecomCall = null;
378 latencyReport = null;
379 id = null;
380 hiddenId = 0;
381 dialerCallDelegate = null;
382 videoTechManager = null;
twyendde01c52017-09-22 10:07:31 -0700383 }
384
Eric Erfanianccca3152017-02-22 16:32:36 -0800385 private static int translateState(int state) {
386 switch (state) {
387 case Call.STATE_NEW:
388 case Call.STATE_CONNECTING:
389 return DialerCall.State.CONNECTING;
390 case Call.STATE_SELECT_PHONE_ACCOUNT:
391 return DialerCall.State.SELECT_PHONE_ACCOUNT;
392 case Call.STATE_DIALING:
393 return DialerCall.State.DIALING;
394 case Call.STATE_PULLING_CALL:
395 return DialerCall.State.PULLING;
396 case Call.STATE_RINGING:
397 return DialerCall.State.INCOMING;
398 case Call.STATE_ACTIVE:
399 return DialerCall.State.ACTIVE;
400 case Call.STATE_HOLDING:
401 return DialerCall.State.ONHOLD;
402 case Call.STATE_DISCONNECTED:
403 return DialerCall.State.DISCONNECTED;
404 case Call.STATE_DISCONNECTING:
405 return DialerCall.State.DISCONNECTING;
406 default:
407 return DialerCall.State.INVALID;
408 }
409 }
410
411 public static boolean areSame(DialerCall call1, DialerCall call2) {
412 if (call1 == null && call2 == null) {
413 return true;
414 } else if (call1 == null || call2 == null) {
415 return false;
416 }
417
418 // otherwise compare call Ids
419 return call1.getId().equals(call2.getId());
420 }
421
Eric Erfanianccca3152017-02-22 16:32:36 -0800422 public void addListener(DialerCallListener listener) {
423 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800424 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800425 }
426
427 public void removeListener(DialerCallListener listener) {
428 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800429 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800430 }
431
432 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
433 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800434 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800435 }
436
437 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
438 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800439 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800440 }
441
442 public void notifyWiFiToLteHandover() {
443 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800444 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800445 listener.onWiFiToLteHandover();
446 }
447 }
448
449 public void notifyHandoverToWifiFailed() {
450 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800451 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800452 listener.onHandoverToWifiFailure();
453 }
454 }
455
Eric Erfanianc857f902017-05-15 14:05:33 -0700456 public void notifyInternationalCallOnWifi() {
457 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800458 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700459 dialerCallListener.onInternationalCallOnWifi();
460 }
461 }
462
Eric Erfanianccca3152017-02-22 16:32:36 -0800463 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800464 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800465 }
Android Dialer974fc292018-02-01 16:12:25 -0800466
Eric Erfanianccca3152017-02-22 16:32:36 -0800467 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800468 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800469 }
470
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700471 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800472 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700473 }
474
475 public void setCameraDir(int cameraDir) {
476 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
477 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800478 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700479 } else {
linyuh183cb712017-12-27 17:02:37 -0800480 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700481 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800482 }
483
Eric Erfanian2ca43182017-08-31 06:57:16 -0700484 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800485 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700486 }
487
wangqi9982f0d2017-10-11 17:46:07 -0700488 public boolean isVoiceMailNumber() {
489 return isVoicemailNumber;
490 }
491
492 public List<PhoneAccountHandle> getCallCapableAccounts() {
493 return callCapableAccounts;
494 }
495
496 public String getCountryIso() {
497 return countryIso;
498 }
499
500 private void updateIsVoiceMailNumber() {
501 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
502 isVoicemailNumber = true;
503 }
504
linyuh183cb712017-12-27 17:02:37 -0800505 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700506 isVoicemailNumber = false;
507 }
508
linyuh183cb712017-12-27 17:02:37 -0800509 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700510 }
511
Eric Erfanianccca3152017-02-22 16:32:36 -0800512 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700513 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800514 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700515 // Clear any cache here that could potentially change on update.
516 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800517 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700518 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800519 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800520 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800521 listener.onDialerCallDisconnect();
522 }
linyuh183cb712017-12-27 17:02:37 -0800523 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700524 .getEnrichedCallManager()
525 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800526 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700527 .getEnrichedCallManager()
528 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800529 } else {
linyuh183cb712017-12-27 17:02:37 -0800530 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800531 listener.onDialerCallUpdate();
532 }
533 }
534 Trace.endSection();
535 }
536
wangqi9982f0d2017-10-11 17:46:07 -0700537 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700538 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700539 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800540 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700541
roldenburg6bd612f2018-01-18 12:57:19 -0800542 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700543
linyuh183cb712017-12-27 17:02:37 -0800544 final int translatedState = translateState(telecomCall.getState());
545 if (state != State.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800546 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800547 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800548 }
549
linyuh183cb712017-12-27 17:02:37 -0800550 childCallIds.clear();
551 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800552 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800553 childCallIds.add(
554 dialerCallDelegate
555 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800556 .getId());
557 }
558
559 // The number of conferenced calls can change over the course of the call, so use the
560 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800561 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800562
linyuh183cb712017-12-27 17:02:37 -0800563 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800564
565 // If the handle of the call has changed, update state for the call determining if it is an
566 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800567 Uri newHandle = telecomCall.getDetails().getHandle();
568 if (!Objects.equals(handle, newHandle)) {
569 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800570 updateEmergencyCallState();
571 }
572
linyuh183cb712017-12-27 17:02:37 -0800573 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800574 // If the phone account handle of the call is set, cache capability bit indicating whether
575 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800576 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
577 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
578 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800579
linyuh183cb712017-12-27 17:02:37 -0800580 if (phoneAccountHandle != null) {
581 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800582 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800583 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800584 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
585 }
586 }
587 }
linyuh183cb712017-12-27 17:02:37 -0800588 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700589 updateIsVoiceMailNumber();
590 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800591 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700592 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700593 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800594 }
595
596 /**
597 * Tests corruption of the {@code callExtras} bundle by calling {@link
598 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
599 * be thrown and caught by this function.
600 *
601 * @param callExtras the bundle to verify
602 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
603 */
604 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
605 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700606 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800607 * bundle, resulting in a IllegalArgumentException while validating data under {@link
608 * Bundle#containsKey(String)}.
609 */
610 try {
611 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
612 return false;
613 } catch (IllegalArgumentException e) {
614 LogUtil.e(
615 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
616 return true;
617 }
618 }
619
620 protected void updateFromCallExtras(Bundle callExtras) {
621 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
622 /**
623 * If the bundle is corrupted, abandon information update as a work around. These are not
624 * critical for the dialer to function.
625 */
626 return;
627 }
628 // Check for a change in the child address and notify any listeners.
629 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
630 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800631 if (!Objects.equals(childNumber, this.childNumber)) {
632 this.childNumber = childNumber;
633 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800634 listener.onDialerCallChildNumberChange();
635 }
636 }
637 }
638
639 // Last forwarded number comes in as an array of strings. We want to choose the
640 // last item in the array. The forwarding numbers arrive independently of when the
641 // call is originally set up, so we need to notify the the UI of the change.
642 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
643 ArrayList<String> lastForwardedNumbers =
644 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
645
646 if (lastForwardedNumbers != null) {
647 String lastForwardedNumber = null;
648 if (!lastForwardedNumbers.isEmpty()) {
649 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
650 }
651
linyuh183cb712017-12-27 17:02:37 -0800652 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
653 this.lastForwardedNumber = lastForwardedNumber;
654 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800655 listener.onDialerCallLastForwardedNumberChange();
656 }
657 }
658 }
659 }
660
661 // DialerCall subject is present in the extras at the start of call, so we do not need to
662 // notify any other listeners of this.
663 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
664 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800665 if (!Objects.equals(this.callSubject, callSubject)) {
666 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800667 }
668 }
669 }
670
Eric Erfanianccca3152017-02-22 16:32:36 -0800671 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800672 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800673 }
674
Eric Erfanianc857f902017-05-15 14:05:33 -0700675 /**
676 * @return name appended with a number if the number is restricted/unknown and the user has
677 * received more than one restricted/unknown call.
678 */
679 @Nullable
680 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800681 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
682 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700683 }
684 return name;
685 }
686
687 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800688 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700689 }
690
691 private boolean isHiddenNumber() {
692 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
693 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
694 }
695
Eric Erfanianccca3152017-02-22 16:32:36 -0800696 public boolean hasShownWiFiToLteHandoverToast() {
697 return hasShownWiFiToLteHandoverToast;
698 }
699
700 public void setHasShownWiFiToLteHandoverToast() {
701 hasShownWiFiToLteHandoverToast = true;
702 }
703
704 public boolean showWifiHandoverAlertAsToast() {
705 return doNotShowDialogForHandoffToWifiFailure;
706 }
707
708 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
709 doNotShowDialogForHandoffToWifiFailure = bool;
710 }
711
712 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800713 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800714 }
715
716 @Nullable
717 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800718 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800719 }
720
721 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800722 telecomCall.reject(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800723 setState(State.BLOCKED);
724 }
725
726 @Nullable
727 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800728 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800729 }
730
731 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800732 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800733 }
734
735 public boolean isPotentialEmergencyCallback() {
736 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
737 // is actually in emergency callback mode (ie data is disabled).
738 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
739 return true;
740 }
741 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
742 // as a potential emergency callback.
743 if (getExtras() != null
744 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
745 > 0) {
746 long lastEmergencyCallMillis =
747 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
748 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
749 return true;
750 }
751 }
752 return false;
753 }
754
755 boolean isInEmergencyCallbackWindow(long timestampMillis) {
756 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800757 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800758 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
759 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
760 }
761
762 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800763 if (telecomCall != null && telecomCall.getParent() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800764 return State.CONFERENCED;
765 } else {
linyuh183cb712017-12-27 17:02:37 -0800766 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800767 }
768 }
769
yueg265089a2017-10-06 14:35:15 -0700770 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800771 return state;
yueg265089a2017-10-06 14:35:15 -0700772 }
773
Eric Erfanianccca3152017-02-22 16:32:36 -0800774 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700775 if (state == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800776 logState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700777 } else if (state == State.DISCONNECTED) {
778 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800779 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
linyuh183cb712017-12-27 17:02:37 -0800780 if (this.state != state) {
781 logState.duration = newDuration;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700782 } else {
783 LogUtil.i(
784 "DialerCall.setState",
785 "ignoring state transition from DISCONNECTED to DISCONNECTED."
786 + " Duration would have changed from %s to %s",
linyuh183cb712017-12-27 17:02:37 -0800787 logState.duration,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700788 newDuration);
789 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800790 }
linyuh183cb712017-12-27 17:02:37 -0800791 this.state = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800792 }
793
794 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800795 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800796 }
797
798 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800799 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800800 }
801
802 @Nullable
803 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800804 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800805 }
806
807 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800808 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800809 }
810
811 @Nullable
812 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800813 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800814 }
815
816 /** @return The child number for the call, or {@code null} if none specified. */
817 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800818 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800819 }
820
821 /** @return The last forwarded number for the call, or {@code null} if none specified. */
822 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800823 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800824 }
825
wangqif4ba3452018-01-09 11:26:29 -0800826 public boolean isCallForwarded() {
827 return isCallForwarded;
828 }
829
Eric Erfanianccca3152017-02-22 16:32:36 -0800830 /** @return The call subject, or {@code null} if none specified. */
831 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800832 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800833 }
834
835 /**
836 * @return {@code true} if the call's phone account supports call subjects, {@code false}
837 * otherwise.
838 */
839 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800840 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800841 }
842
843 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
844 public DisconnectCause getDisconnectCause() {
linyuh183cb712017-12-27 17:02:37 -0800845 if (state == State.DISCONNECTED || state == State.IDLE) {
846 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800847 }
848
849 return new DisconnectCause(DisconnectCause.UNKNOWN);
850 }
851
852 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800853 this.disconnectCause = disconnectCause;
854 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800855 }
856
857 /** Returns the possible text message responses. */
858 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800859 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800860 }
861
862 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
863 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800864 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800865
866 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
867 // We allow you to merge if the capabilities allow it or if it is a call with
868 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800869 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800870 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
871 // Cannot merge calls if there are no calls to merge with.
872 return false;
873 }
874 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
875 }
876 return (capabilities == (capabilities & supportedCapabilities));
877 }
878
879 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800880 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800881 }
882
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700883 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800884 public String getUniqueCallId() {
885 return uniqueCallId;
886 }
887
888 /** Gets the time when the call first became active. */
889 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800890 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800891 }
892
893 public boolean isConferenceCall() {
894 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
895 }
896
897 @Nullable
898 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -0800899 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -0800900 }
901
902 @Nullable
903 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -0800904 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800905 }
906
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700907 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -0800908 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -0800909 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800910 }
911
912 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -0800913 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -0800914 }
915
916 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -0800917 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -0800918 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -0800919 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -0800920 }
921 return null;
922 }
923
924 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -0800925 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -0800926 }
927
928 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -0700929 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700930 }
931
wangqi219b8702018-02-13 09:34:41 -0800932 public boolean isRttCall() {
933 if (BuildCompat.isAtLeastP()) {
934 return getTelecomCall().isRttActive();
935 } else {
936 return false;
937 }
938 }
939
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700940 public boolean hasReceivedVideoUpgradeRequest() {
941 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
942 }
943
944 public boolean hasSentVideoUpgradeRequest() {
945 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -0800946 }
947
wangqi219b8702018-02-13 09:34:41 -0800948 public boolean hasSentRttUpgradeRequest() {
949 // TODO(wangqi): Implement this.
950 return false;
951 }
952
Eric Erfanianccca3152017-02-22 16:32:36 -0800953 /**
954 * Determines if the call handle is an emergency number or not and caches the result to avoid
955 * repeated calls to isEmergencyNumber.
956 */
957 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -0800958 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800959 }
960
Eric Erfanianccca3152017-02-22 16:32:36 -0800961 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -0800962 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -0800963 }
964
965 /**
966 * Determines if the call is an external call.
967 *
968 * <p>An external call is one which does not exist locally for the {@link
969 * android.telecom.ConnectionService} it is associated with.
970 *
971 * <p>External calls are only supported in N and higher.
972 *
973 * @return {@code true} if the call is an external call, {@code false} otherwise.
974 */
975 public boolean isExternalCall() {
976 return VERSION.SDK_INT >= VERSION_CODES.N
977 && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
978 }
979
980 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800981 * Determines if answering this call will cause an ongoing video call to be dropped.
982 *
983 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
984 * otherwise.
985 */
986 public boolean answeringDisconnectsForegroundVideoCall() {
987 Bundle extras = getExtras();
988 if (extras == null
989 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
990 return false;
991 }
992 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
993 }
994
995 private void parseCallSpecificAppData() {
996 if (isExternalCall()) {
997 return;
998 }
999
linyuh183cb712017-12-27 17:02:37 -08001000 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
1001 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001002
linyuh183cb712017-12-27 17:02:37 -08001003 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -07001004 CallSpecificAppData.newBuilder()
1005 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
1006 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001007 }
1008 if (getState() == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -08001009 logState.callSpecificAppData =
1010 logState
Eric Erfanian8369df02017-05-03 10:27:13 -07001011 .callSpecificAppData
1012 .toBuilder()
1013 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
1014 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001015 }
1016 }
1017
1018 @Override
1019 public String toString() {
linyuh183cb712017-12-27 17:02:37 -08001020 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001021 // This should happen only in testing since otherwise we would never have a null
1022 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -08001023 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -08001024 }
1025
1026 return String.format(
1027 Locale.US,
1028 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001029 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001030 id,
Eric Erfanianccca3152017-02-22 16:32:36 -08001031 State.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001032 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1033 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1034 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001035 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001036 this.telecomCall.getConferenceableCalls(),
1037 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001038 getVideoTech().getSessionModificationState(),
1039 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001040 }
1041
1042 public String toSimpleString() {
1043 return super.toString();
1044 }
1045
1046 @CallHistoryStatus
1047 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001048 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001049 }
1050
1051 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001052 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001053 }
1054
1055 public boolean didShowCameraPermission() {
1056 return didShowCameraPermission;
1057 }
1058
1059 public void setDidShowCameraPermission(boolean didShow) {
1060 didShowCameraPermission = didShow;
1061 }
1062
Eric Erfanian938468d2017-10-24 14:05:52 -07001063 @Nullable
1064 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001065 return isInGlobalSpamList;
1066 }
1067
1068 public void setIsInGlobalSpamList(boolean inSpamList) {
1069 isInGlobalSpamList = inSpamList;
1070 }
1071
Eric Erfanian938468d2017-10-24 14:05:52 -07001072 @Nullable
1073 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001074 return isInUserSpamList;
1075 }
1076
1077 public void setIsInUserSpamList(boolean inSpamList) {
1078 isInUserSpamList = inSpamList;
1079 }
1080
Eric Erfanian938468d2017-10-24 14:05:52 -07001081 @Nullable
1082 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001083 return isInUserWhiteList;
1084 }
1085
1086 public void setIsInUserWhiteList(boolean inWhiteList) {
1087 isInUserWhiteList = inWhiteList;
1088 }
1089
1090 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001091 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001092 }
1093
1094 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001095 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001096 }
1097
1098 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001099 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001100 }
1101
1102 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001103 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001104 }
1105
1106 public boolean isRemotelyHeld() {
1107 return isRemotelyHeld;
1108 }
1109
Eric Erfanian2ca43182017-08-31 06:57:16 -07001110 public boolean isMergeInProcess() {
1111 return isMergeInProcess;
1112 }
1113
Eric Erfanianccca3152017-02-22 16:32:36 -08001114 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001115 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001116 }
1117
erfanian2cf2c342017-12-21 12:01:33 -08001118 /**
1119 * Try and determine if the call used assisted dialing.
1120 *
1121 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1122 * implmentation is complete in P+.
1123 *
1124 * @return a boolean indicating assisted dialing may have been performed
1125 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001126 public boolean isAssistedDialed() {
1127 if (getIntentExtras() != null) {
erfanian2cf2c342017-12-21 12:01:33 -08001128 // O_MR1 and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
1129 // was used. The Dialer client is responsible for performing assisted dialing before
1130 // placing the outgoing call.
1131 //
1132 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1133 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1134 && getAssistedDialingExtras() != null
1135 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1136 return true;
1137 }
1138 }
1139
1140 // Starting in P+ USE_ASSISTED_DIALING indicates that the client requested the platform
1141 // perform assisted dialing. PROPERTY_ASSISTED_DIALING_USED indicates assisted dialing took
1142 // place.
1143 if (hasProperty(TelephonyManagerCompat.PROPERTY_ASSISTED_DIALING_USED)
erfanianf2556612018-01-23 09:55:59 -08001144 && BuildCompat.isAtLeastP()) {
erfanian2cf2c342017-12-21 12:01:33 -08001145 return true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001146 }
1147 return false;
1148 }
1149
erfanian2cf2c342017-12-21 12:01:33 -08001150 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001151 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001152 if (getIntentExtras() == null) {
1153 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001154 }
erfanian2cf2c342017-12-21 12:01:33 -08001155
erfanianf2556612018-01-23 09:55:59 -08001156 if (BuildCompat.isAtLeastP()) {
1157 if (getExtras() == null) {
1158 return null;
1159 }
1160
1161 if (getExtras()
1162 .getParcelable(TelephonyManagerCompat.EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO)
1163 == null) {
1164 return null;
1165 }
1166
1167 // TODO(erfanian): Use the framework transformation info when we can link against it
1168 return null;
1169 }
1170
erfanian2cf2c342017-12-21 12:01:33 -08001171 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1172 return null;
1173 }
1174
erfanianf2556612018-01-23 09:55:59 -08001175 // Used in N-OMR1
erfanian2cf2c342017-12-21 12:01:33 -08001176 return TransformationInfo.newInstanceFromBundle(
1177 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001178 }
1179
Eric Erfanianccca3152017-02-22 16:32:36 -08001180 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001181 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001182 }
1183
Eric Erfanian2ca43182017-08-31 06:57:16 -07001184 public int getAnswerAndReleaseButtonDisplayedTimes() {
1185 return answerAndReleaseButtonDisplayedTimes;
1186 }
1187
1188 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1189 answerAndReleaseButtonDisplayedTimes++;
1190 }
1191
1192 public boolean getReleasedByAnsweringSecondCall() {
1193 return releasedByAnsweringSecondCall;
1194 }
1195
1196 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1197 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1198 }
1199
1200 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1201 return secondCallWithoutAnswerAndReleasedButtonTimes;
1202 }
1203
1204 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1205 secondCallWithoutAnswerAndReleasedButtonTimes++;
1206 }
1207
Eric Erfanian8369df02017-05-03 10:27:13 -07001208 @Nullable
1209 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001210 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001211 }
1212
1213 public void setEnrichedCallCapabilities(
1214 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001215 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001216 }
1217
1218 @Nullable
1219 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001220 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001221 }
1222
1223 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001224 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001225 }
1226
Eric Erfanianccca3152017-02-22 16:32:36 -08001227 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001228 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001229 }
1230
Eric Erfanianccca3152017-02-22 16:32:36 -08001231 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1232 LogUtil.i(
1233 "DialerCall.phoneAccountSelected",
1234 "accountHandle: %s, setDefault: %b",
1235 accountHandle,
1236 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001237 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001238 }
1239
1240 public void disconnect() {
1241 LogUtil.i("DialerCall.disconnect", "");
1242 setState(DialerCall.State.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001243 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001244 listener.onDialerCallUpdate();
1245 }
linyuh183cb712017-12-27 17:02:37 -08001246 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001247 }
1248
1249 public void hold() {
1250 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001251 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001252 }
1253
1254 public void unhold() {
1255 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001256 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001257 }
1258
1259 public void splitFromConference() {
1260 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001261 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001262 }
1263
1264 public void answer(int videoState) {
1265 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001266 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001267 }
1268
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001269 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001270 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001271 }
1272
Eric Erfanianccca3152017-02-22 16:32:36 -08001273 public void reject(boolean rejectWithMessage, String message) {
1274 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001275 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001276 }
1277
1278 /** Return the string label to represent the call provider */
1279 public String getCallProviderLabel() {
1280 if (callProviderLabel == null) {
1281 PhoneAccount account = getPhoneAccount();
1282 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001283 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001284 callProviderLabel = account.getLabel().toString();
1285 }
1286 }
1287 if (callProviderLabel == null) {
1288 callProviderLabel = "";
1289 }
1290 }
1291 return callProviderLabel;
1292 }
1293
1294 private PhoneAccount getPhoneAccount() {
1295 PhoneAccountHandle accountHandle = getAccountHandle();
1296 if (accountHandle == null) {
1297 return null;
1298 }
linyuh183cb712017-12-27 17:02:37 -08001299 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001300 }
1301
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001302 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001303 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001304 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001305
1306 // Only store the first video tech type found to be available during the life of the call.
1307 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1308 // Update the video tech.
1309 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1310 }
1311 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001312 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001313 }
1314
Eric Erfanianccca3152017-02-22 16:32:36 -08001315 public String getCallbackNumber() {
1316 if (callbackNumber == null) {
1317 // Show the emergency callback number if either:
1318 // 1. This is an emergency call.
1319 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1320 // number.
1321 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1322
1323 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001324 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001325 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001326 }
1327
Eric Erfanianccca3152017-02-22 16:32:36 -08001328 if (callbackNumber == null) {
1329 callbackNumber = "";
1330 }
1331 }
1332 return callbackNumber;
1333 }
1334
wangqi97539352017-09-25 11:15:16 -07001335 public String getSimCountryIso() {
1336 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001337 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001338 .getSimCountryIso();
1339 if (!TextUtils.isEmpty(simCountryIso)) {
1340 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1341 }
1342 return simCountryIso;
1343 }
1344
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001345 @Override
1346 public void onVideoTechStateChanged() {
1347 update();
1348 }
1349
1350 @Override
1351 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001352 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001353 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001354 listener.onDialerCallSessionModificationStateChange();
1355 }
wangqi9982f0d2017-10-11 17:46:07 -07001356 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001357 }
1358
1359 @Override
1360 public void onCameraDimensionsChanged(int width, int height) {
1361 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1362 }
1363
1364 @Override
1365 public void onPeerDimensionsChanged(int width, int height) {
1366 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1367 }
1368
1369 @Override
1370 public void onVideoUpgradeRequestReceived() {
1371 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1372
linyuh183cb712017-12-27 17:02:37 -08001373 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001374 listener.onDialerCallUpgradeToVideo();
1375 }
1376
1377 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001378
linyuh183cb712017-12-27 17:02:37 -08001379 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001380 .logCallImpression(
1381 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1382 }
1383
1384 @Override
1385 public void onUpgradedToVideo(boolean switchToSpeaker) {
1386 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1387
1388 if (!switchToSpeaker) {
1389 return;
1390 }
1391
1392 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1393
1394 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1395 LogUtil.e(
1396 "DialerCall.onUpgradedToVideo",
1397 "toggling speakerphone not allowed when bluetooth supported.");
1398 return;
1399 }
1400
1401 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1402 return;
1403 }
1404
1405 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001406 }
1407
Eric Erfanian2ca43182017-08-31 06:57:16 -07001408 @Override
1409 public void onCapabilitiesUpdated() {
1410 if (getNumber() == null) {
1411 return;
1412 }
1413 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001414 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001415 if (capabilities != null) {
1416 setEnrichedCallCapabilities(capabilities);
1417 update();
1418 }
1419 }
1420
1421 @Override
1422 public void onEnrichedCallStateChanged() {
1423 updateEnrichedCallSession();
1424 }
1425
1426 @Override
1427 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001428 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001429 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1430 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001431 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001432 .logCallImpression(
1433 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1434 getUniqueCallId(),
1435 getTimeAddedMs());
1436 }
1437 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001438 }
1439
1440 private void updateEnrichedCallSession() {
1441 if (getNumber() == null) {
1442 return;
1443 }
1444 if (getEnrichedCallSession() != null) {
1445 // State changes to existing sessions are currently handled by the UI components (which have
1446 // their own listeners). Someday instead we could remove those and just call update() here and
1447 // have the usual onDialerCallUpdate update the UI.
1448 dispatchOnEnrichedCallSessionUpdate();
1449 return;
1450 }
1451
linyuh183cb712017-12-27 17:02:37 -08001452 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001453
1454 Filter filter =
1455 isIncoming()
1456 ? manager.createIncomingCallComposerFilter()
1457 : manager.createOutgoingCallComposerFilter();
1458
1459 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1460 if (session == null) {
1461 return;
1462 }
1463
1464 session.setUniqueDialerCallId(getUniqueCallId());
1465 setEnrichedCallSession(session);
1466
1467 LogUtil.i(
1468 "DialerCall.updateEnrichedCallSession",
1469 "setting session %d's dialer id to %s",
1470 session.getSessionId(),
1471 getUniqueCallId());
1472
1473 dispatchOnEnrichedCallSessionUpdate();
1474 }
1475
1476 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001477 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001478 listener.onEnrichedCallSessionUpdate();
1479 }
1480 }
1481
1482 void onRemovedFromCallList() {
1483 // Ensure we clean up when this call is removed.
linyuh183cb712017-12-27 17:02:37 -08001484 videoTechManager.dispatchRemovedFromCallList();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001485 }
1486
wangqi4d705e52017-09-28 12:23:35 -07001487 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1488 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001489 }
1490
Android Dialer974fc292018-02-01 16:12:25 -08001491 public void markFeedbackRequested() {
1492 feedbackRequested = true;
1493 }
1494
1495 public boolean isFeedbackRequested() {
1496 return feedbackRequested;
1497 }
1498
Eric Erfanianccca3152017-02-22 16:32:36 -08001499 /**
1500 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1501 * means there is no result.
1502 */
1503 @IntDef({
1504 CALL_HISTORY_STATUS_UNKNOWN,
1505 CALL_HISTORY_STATUS_PRESENT,
1506 CALL_HISTORY_STATUS_NOT_PRESENT
1507 })
1508 @Retention(RetentionPolicy.SOURCE)
1509 public @interface CallHistoryStatus {}
1510
1511 /* Defines different states of this call */
1512 public static class State {
1513
1514 public static final int INVALID = 0;
1515 public static final int NEW = 1; /* The call is new. */
1516 public static final int IDLE = 2; /* The call is idle. Nothing active */
1517 public static final int ACTIVE = 3; /* There is an active call */
1518 public static final int INCOMING = 4; /* A normal incoming phone call */
1519 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1520 public static final int DIALING = 6; /* An outgoing call during dial phase */
1521 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1522 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1523 public static final int DISCONNECTING = 9; /* A call is being ended. */
1524 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1525 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1526 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1527 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1528 public static final int BLOCKED = 14; /* The number was found on the block list */
1529 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001530 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001531
1532 public static boolean isConnectingOrConnected(int state) {
1533 switch (state) {
1534 case ACTIVE:
1535 case INCOMING:
1536 case CALL_WAITING:
1537 case CONNECTING:
1538 case DIALING:
1539 case PULLING:
1540 case REDIALING:
1541 case ONHOLD:
1542 case CONFERENCED:
1543 return true;
1544 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001545 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001546 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001547 }
1548
1549 public static boolean isDialing(int state) {
1550 return state == DIALING || state == PULLING || state == REDIALING;
1551 }
1552
1553 public static String toString(int state) {
1554 switch (state) {
1555 case INVALID:
1556 return "INVALID";
1557 case NEW:
1558 return "NEW";
1559 case IDLE:
1560 return "IDLE";
1561 case ACTIVE:
1562 return "ACTIVE";
1563 case INCOMING:
1564 return "INCOMING";
1565 case CALL_WAITING:
1566 return "CALL_WAITING";
1567 case DIALING:
1568 return "DIALING";
1569 case PULLING:
1570 return "PULLING";
1571 case REDIALING:
1572 return "REDIALING";
1573 case ONHOLD:
1574 return "ONHOLD";
1575 case DISCONNECTING:
1576 return "DISCONNECTING";
1577 case DISCONNECTED:
1578 return "DISCONNECTED";
1579 case CONFERENCED:
1580 return "CONFERENCED";
1581 case SELECT_PHONE_ACCOUNT:
1582 return "SELECT_PHONE_ACCOUNT";
1583 case CONNECTING:
1584 return "CONNECTING";
1585 case BLOCKED:
1586 return "BLOCKED";
1587 default:
1588 return "UNKNOWN";
1589 }
1590 }
1591 }
1592
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001593 /** Camera direction constants */
1594 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001595 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1596 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1597 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001598 }
1599
1600 /**
1601 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1602 * existing call member variables, but this duplication helps to ensure that none of these logging
1603 * variables will interface with/and affect call logic.
1604 */
1605 public static class LogState {
1606
1607 public DisconnectCause disconnectCause;
1608 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001609 public ContactLookupResult.Type contactLookupResult =
1610 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001611 public CallSpecificAppData callSpecificAppData;
1612 // If this was a conference call, the total number of calls involved in the conference.
1613 public int conferencedCalls = 0;
1614 public long duration = 0;
1615 public boolean isLogged = false;
1616
Eric Erfanian8369df02017-05-03 10:27:13 -07001617 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001618 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001619 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001620 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001621 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001622 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001623 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001624 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001625 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001626 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001627 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001628 return "Voicemail";
1629 default:
1630 return "Not found";
1631 }
1632 }
1633
1634 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1635 if (callSpecificAppData == null) {
1636 return "null";
1637 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001638 switch (callSpecificAppData.getCallInitiationType()) {
1639 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001640 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001641 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001642 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001643 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001644 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001645 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001646 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001647 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001648 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001649 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001650 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001651 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001652 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001653 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001654 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001655 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001656 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001657 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001658 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001659 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001660 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001661 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001662 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001663 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001664 return "Launcher Shortcut";
1665 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001666 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001667 }
1668 }
1669
1670 @Override
1671 public String toString() {
1672 return String.format(
1673 Locale.US,
1674 "["
1675 + "%s, " // DisconnectCause toString already describes the object type
1676 + "isIncoming: %s, "
1677 + "contactLookup: %s, "
1678 + "callInitiation: %s, "
1679 + "duration: %s"
1680 + "]",
1681 disconnectCause,
1682 isIncoming,
1683 lookupToString(contactLookupResult),
1684 initiationToString(callSpecificAppData),
1685 duration);
1686 }
1687 }
1688
roldenburgd7490db2018-01-09 13:51:29 -08001689 /** Coordinates the available VideoTech implementations for a call. */
1690 @VisibleForTesting
1691 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001692 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001693 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001694 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001695 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001696 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001697
roldenburgd7490db2018-01-09 13:51:29 -08001698 @VisibleForTesting
1699 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001700 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001701
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001702 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001703 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001704 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001705
1706 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001707 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001708
linyuh183cb712017-12-27 17:02:37 -08001709 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001710
roldenburgd7490db2018-01-09 13:51:29 -08001711 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001712 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001713 .getRcsVideoShareFactory()
1714 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001715 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001716 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001717 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001718 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001719
1720 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001721 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001722 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001723
1724 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001725 }
1726
roldenburgd7490db2018-01-09 13:51:29 -08001727 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001728 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001729 if (savedTech == emptyVideoTech) {
1730 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001731 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001732 savedTech = tech;
1733 savedTech.becomePrimary();
1734 break;
1735 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001736 }
roldenburg6bd612f2018-01-18 12:57:19 -08001737 } else if (savedTech instanceof DuoVideoTech
1738 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001739 // RCS Video Share will become available after the capability exchange which is slower than
1740 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1741 // not in the middle of any session changes, let it take over.
1742 savedTech = rcsVideoShare;
1743 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001744 }
1745
roldenburgd7490db2018-01-09 13:51:29 -08001746 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001747 }
1748
roldenburgd7490db2018-01-09 13:51:29 -08001749 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001750 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001751 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001752 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001753 }
1754 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001755
1756 void dispatchRemovedFromCallList() {
1757 for (VideoTech videoTech : videoTechs) {
1758 videoTech.onRemovedFromCallList();
1759 }
1760 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001761 }
1762
Eric Erfanianccca3152017-02-22 16:32:36 -08001763 /** Called when canned text responses have been loaded. */
1764 public interface CannedTextResponsesLoadedListener {
1765 void onCannedTextResponsesLoaded(DialerCall call);
1766 }
1767}