blob: 27243225b4c08550064f67cf710b12e54f043d2e [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;
Eric Erfanian8369df02017-05-03 10:27:13 -070035import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import android.telecom.Connection;
37import android.telecom.DisconnectCause;
38import android.telecom.GatewayInfo;
39import android.telecom.InCallService.VideoCall;
40import android.telecom.PhoneAccount;
41import android.telecom.PhoneAccountHandle;
42import android.telecom.StatusHints;
43import android.telecom.TelecomManager;
44import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080045import android.text.TextUtils;
46import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080047import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
erfanian2cf2c342017-12-21 12:01:33 -080048import com.android.dialer.assisteddialing.ConcreteCreator;
erfaniand0f207f2017-10-11 12:23:29 -070049import com.android.dialer.assisteddialing.TransformationInfo;
Eric Erfanian8369df02017-05-03 10:27:13 -070050import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080051import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070052import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080053import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080054import com.android.dialer.common.LogUtil;
Eric Erfanian2ca43182017-08-31 06:57:16 -070055import com.android.dialer.compat.telephony.TelephonyManagerCompat;
56import com.android.dialer.configprovider.ConfigProviderBindings;
roldenburg4f026392017-10-13 18:42:20 -070057import com.android.dialer.duo.DuoComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070058import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070059import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070060import com.android.dialer.enrichedcall.EnrichedCallManager;
61import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
62import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
63import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070064import com.android.dialer.enrichedcall.Session;
wangqi9982f0d2017-10-11 17:46:07 -070065import com.android.dialer.location.GeoUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070066import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070067import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070068import com.android.dialer.logging.DialerImpression;
69import com.android.dialer.logging.Logger;
twyena4745bd2017-12-12 18:40:11 -080070import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070071import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070072import com.android.dialer.theme.R;
wangqi9982f0d2017-10-11 17:46:07 -070073import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070074import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080075import com.android.incallui.latencyreport.LatencyReport;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070076import com.android.incallui.videotech.VideoTech;
77import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070078import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070079import com.android.incallui.videotech.empty.EmptyVideoTech;
80import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070081import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080082import java.lang.annotation.Retention;
83import java.lang.annotation.RetentionPolicy;
84import java.util.ArrayList;
85import java.util.List;
86import java.util.Locale;
87import java.util.Objects;
88import java.util.UUID;
89import java.util.concurrent.CopyOnWriteArrayList;
90import java.util.concurrent.TimeUnit;
91
92/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -070093public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -080094
95 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
96 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
97 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070098
99 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -0700100 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700101 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
102
Eric Erfanianccca3152017-02-22 16:32:36 -0800103 private static final String ID_PREFIX = "DialerCall_";
104 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
105 "emergency_callback_window_millis";
linyuh183cb712017-12-27 17:02:37 -0800106 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800107
108 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700109 * A counter used to append to restricted/private/hidden calls so that users can identify them in
110 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
111 * are no live calls.
112 */
linyuh183cb712017-12-27 17:02:37 -0800113 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700114
115 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800116 * The unique call ID for every call. This will help us to identify each call and allow us the
117 * ability to stitch impressions to calls if needed.
118 */
119 private final String uniqueCallId = UUID.randomUUID().toString();
120
linyuh183cb712017-12-27 17:02:37 -0800121 private final Call telecomCall;
122 private final LatencyReport latencyReport;
123 private final String id;
124 private final int hiddenId;
125 private final List<String> childCallIds = new ArrayList<>();
126 private final LogState logState = new LogState();
127 private final Context context;
128 private final DialerCallDelegate dialerCallDelegate;
129 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
130 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800131 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800132 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800133
linyuh183cb712017-12-27 17:02:37 -0800134 private boolean isEmergencyCall;
135 private Uri handle;
136 private int state = State.INVALID;
137 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800138
139 private boolean hasShownWiFiToLteHandoverToast;
140 private boolean doNotShowDialogForHandoffToWifiFailure;
141
linyuh183cb712017-12-27 17:02:37 -0800142 private String childNumber;
143 private String lastForwardedNumber;
wangqif4ba3452018-01-09 11:26:29 -0800144 private boolean isCallForwarded;
linyuh183cb712017-12-27 17:02:37 -0800145 private String callSubject;
146 private PhoneAccountHandle phoneAccountHandle;
147 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
148 private boolean isSpam;
149 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700150
151 @Nullable private Boolean isInUserSpamList;
152
153 @Nullable private Boolean isInUserWhiteList;
154
155 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800156 private boolean didShowCameraPermission;
157 private String callProviderLabel;
158 private String callbackNumber;
linyuh183cb712017-12-27 17:02:37 -0800159 private int cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
160 private EnrichedCallCapabilities enrichedCallCapabilities;
161 private Session enrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800162
Eric Erfanian2ca43182017-08-31 06:57:16 -0700163 private int answerAndReleaseButtonDisplayedTimes = 0;
164 private boolean releasedByAnsweringSecondCall = false;
165 // Times when a second call is received but AnswerAndRelease button is not shown
166 // since it's not supported.
167 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700168 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700169
wangqi4d705e52017-09-28 12:23:35 -0700170 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
171 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700172 private boolean isVoicemailNumber;
173 private List<PhoneAccountHandle> callCapableAccounts;
174 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700175
Eric Erfanianccca3152017-02-22 16:32:36 -0800176 public static String getNumberFromHandle(Uri handle) {
177 return handle == null ? "" : handle.getSchemeSpecificPart();
178 }
179
180 /**
181 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700182 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800183 */
184 private boolean isRemotelyHeld;
185
Eric Erfanian2ca43182017-08-31 06:57:16 -0700186 /** Indicates whether this call is currently in the process of being merged into a conference. */
187 private boolean isMergeInProcess;
188
Eric Erfanianccca3152017-02-22 16:32:36 -0800189 /**
190 * Indicates whether the phone account associated with this call supports specifying a call
191 * subject.
192 */
linyuh183cb712017-12-27 17:02:37 -0800193 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800194
linyuh183cb712017-12-27 17:02:37 -0800195 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800196 new Call.Callback() {
197 @Override
198 public void onStateChanged(Call call, int newState) {
199 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
200 update();
201 }
202
203 @Override
204 public void onParentChanged(Call call, Call newParent) {
205 LogUtil.v(
206 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
207 update();
208 }
209
210 @Override
211 public void onChildrenChanged(Call call, List<Call> children) {
212 update();
213 }
214
215 @Override
216 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700217 LogUtil.v(
218 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800219 update();
220 }
221
222 @Override
223 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
224 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700225 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800226 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800227 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800228 listener.onCannedTextResponsesLoaded(DialerCall.this);
229 }
230 }
231
232 @Override
233 public void onPostDialWait(Call call, String remainingPostDialSequence) {
234 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700235 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800236 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
237 update();
238 }
239
240 @Override
241 public void onVideoCallChanged(Call call, VideoCall videoCall) {
242 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700243 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800244 update();
245 }
246
247 @Override
248 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700249 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700250 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800251 }
252
253 @Override
254 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
255 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700256 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800257 "call %s, conferenceable calls: %d",
258 call,
259 conferenceableCalls.size());
260 update();
261 }
262
263 @Override
264 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
265 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700266 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800267 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
268 switch (event) {
269 // The Previous attempt to Merge two calls together has failed in Telecom. We must
270 // now update the UI to possibly re-enable the Merge button based on the number of
271 // currently conferenceable calls available or Connection Capabilities.
272 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
273 update();
274 break;
275 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
276 notifyWiFiToLteHandover();
277 break;
278 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
279 notifyHandoverToWifiFailed();
280 break;
281 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
282 isRemotelyHeld = true;
283 update();
284 break;
285 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
286 isRemotelyHeld = false;
287 update();
288 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700289 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
290 notifyInternationalCallOnWifi();
291 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700292 case TelephonyManagerCompat.EVENT_MERGE_START:
293 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
294 isMergeInProcess = true;
295 break;
296 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
297 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
298 isMergeInProcess = false;
299 break;
wangqif4ba3452018-01-09 11:26:29 -0800300 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
301 // Only handle this event for P+ since it's unreliable pre-P.
302 if (BuildCompat.isAtLeastP()) {
303 isCallForwarded = true;
304 update();
305 }
306 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800307 default:
308 break;
309 }
310 }
311 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700312
linyuh183cb712017-12-27 17:02:37 -0800313 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800314
315 public DialerCall(
316 Context context,
317 DialerCallDelegate dialerCallDelegate,
318 Call telecomCall,
319 LatencyReport latencyReport,
320 boolean registerCallback) {
321 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800322 this.context = context;
323 this.dialerCallDelegate = dialerCallDelegate;
324 this.telecomCall = telecomCall;
325 this.latencyReport = latencyReport;
326 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800327
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700328 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800329 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700330
331 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700332 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800333 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700334 } else {
linyuh183cb712017-12-27 17:02:37 -0800335 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700336 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800337
338 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800339 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800340 }
341
linyuh183cb712017-12-27 17:02:37 -0800342 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800343 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700344
345 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800346 }
347
twyendde01c52017-09-22 10:07:31 -0700348 /** Test only constructor to avoid initializing dependencies. */
349 @VisibleForTesting
350 DialerCall(Context context) {
linyuh183cb712017-12-27 17:02:37 -0800351 this.context = context;
352 telecomCall = null;
353 latencyReport = null;
354 id = null;
355 hiddenId = 0;
356 dialerCallDelegate = null;
357 videoTechManager = null;
twyendde01c52017-09-22 10:07:31 -0700358 }
359
Eric Erfanianccca3152017-02-22 16:32:36 -0800360 private static int translateState(int state) {
361 switch (state) {
362 case Call.STATE_NEW:
363 case Call.STATE_CONNECTING:
364 return DialerCall.State.CONNECTING;
365 case Call.STATE_SELECT_PHONE_ACCOUNT:
366 return DialerCall.State.SELECT_PHONE_ACCOUNT;
367 case Call.STATE_DIALING:
368 return DialerCall.State.DIALING;
369 case Call.STATE_PULLING_CALL:
370 return DialerCall.State.PULLING;
371 case Call.STATE_RINGING:
372 return DialerCall.State.INCOMING;
373 case Call.STATE_ACTIVE:
374 return DialerCall.State.ACTIVE;
375 case Call.STATE_HOLDING:
376 return DialerCall.State.ONHOLD;
377 case Call.STATE_DISCONNECTED:
378 return DialerCall.State.DISCONNECTED;
379 case Call.STATE_DISCONNECTING:
380 return DialerCall.State.DISCONNECTING;
381 default:
382 return DialerCall.State.INVALID;
383 }
384 }
385
386 public static boolean areSame(DialerCall call1, DialerCall call2) {
387 if (call1 == null && call2 == null) {
388 return true;
389 } else if (call1 == null || call2 == null) {
390 return false;
391 }
392
393 // otherwise compare call Ids
394 return call1.getId().equals(call2.getId());
395 }
396
397 public static boolean areSameNumber(DialerCall call1, DialerCall call2) {
398 if (call1 == null && call2 == null) {
399 return true;
400 } else if (call1 == null || call2 == null) {
401 return false;
402 }
403
404 // otherwise compare call Numbers
405 return TextUtils.equals(call1.getNumber(), call2.getNumber());
406 }
407
408 public void addListener(DialerCallListener listener) {
409 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800410 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800411 }
412
413 public void removeListener(DialerCallListener listener) {
414 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800415 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800416 }
417
418 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
419 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800420 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800421 }
422
423 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
424 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800425 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800426 }
427
428 public void notifyWiFiToLteHandover() {
429 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800430 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800431 listener.onWiFiToLteHandover();
432 }
433 }
434
435 public void notifyHandoverToWifiFailed() {
436 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800437 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800438 listener.onHandoverToWifiFailure();
439 }
440 }
441
Eric Erfanianc857f902017-05-15 14:05:33 -0700442 public void notifyInternationalCallOnWifi() {
443 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800444 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700445 dialerCallListener.onInternationalCallOnWifi();
446 }
447 }
448
Eric Erfanianccca3152017-02-22 16:32:36 -0800449 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800450 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800451 }
452
453 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800454 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800455 }
456
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700457 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800458 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700459 }
460
461 public void setCameraDir(int cameraDir) {
462 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
463 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800464 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700465 } else {
linyuh183cb712017-12-27 17:02:37 -0800466 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700467 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800468 }
469
Eric Erfanian2ca43182017-08-31 06:57:16 -0700470 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800471 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700472 }
473
wangqi9982f0d2017-10-11 17:46:07 -0700474 public boolean isVoiceMailNumber() {
475 return isVoicemailNumber;
476 }
477
478 public List<PhoneAccountHandle> getCallCapableAccounts() {
479 return callCapableAccounts;
480 }
481
482 public String getCountryIso() {
483 return countryIso;
484 }
485
486 private void updateIsVoiceMailNumber() {
487 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
488 isVoicemailNumber = true;
489 }
490
linyuh183cb712017-12-27 17:02:37 -0800491 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700492 isVoicemailNumber = false;
493 }
494
linyuh183cb712017-12-27 17:02:37 -0800495 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700496 }
497
Eric Erfanianccca3152017-02-22 16:32:36 -0800498 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700499 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800500 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700501 // Clear any cache here that could potentially change on update.
502 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800503 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700504 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800505 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800506 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800507 listener.onDialerCallDisconnect();
508 }
linyuh183cb712017-12-27 17:02:37 -0800509 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700510 .getEnrichedCallManager()
511 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800512 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700513 .getEnrichedCallManager()
514 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800515 } else {
linyuh183cb712017-12-27 17:02:37 -0800516 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800517 listener.onDialerCallUpdate();
518 }
519 }
520 Trace.endSection();
521 }
522
wangqi9982f0d2017-10-11 17:46:07 -0700523 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700524 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700525 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800526 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700527
roldenburg6bd612f2018-01-18 12:57:19 -0800528 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700529
linyuh183cb712017-12-27 17:02:37 -0800530 final int translatedState = translateState(telecomCall.getState());
531 if (state != State.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800532 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800533 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800534 }
535
linyuh183cb712017-12-27 17:02:37 -0800536 childCallIds.clear();
537 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800538 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800539 childCallIds.add(
540 dialerCallDelegate
541 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800542 .getId());
543 }
544
545 // The number of conferenced calls can change over the course of the call, so use the
546 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800547 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800548
linyuh183cb712017-12-27 17:02:37 -0800549 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800550
551 // If the handle of the call has changed, update state for the call determining if it is an
552 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800553 Uri newHandle = telecomCall.getDetails().getHandle();
554 if (!Objects.equals(handle, newHandle)) {
555 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800556 updateEmergencyCallState();
557 }
558
linyuh183cb712017-12-27 17:02:37 -0800559 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800560 // If the phone account handle of the call is set, cache capability bit indicating whether
561 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800562 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
563 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
564 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800565
linyuh183cb712017-12-27 17:02:37 -0800566 if (phoneAccountHandle != null) {
567 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800568 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800569 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800570 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
571 }
572 }
573 }
linyuh183cb712017-12-27 17:02:37 -0800574 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700575 updateIsVoiceMailNumber();
576 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800577 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700578 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700579 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800580 }
581
582 /**
583 * Tests corruption of the {@code callExtras} bundle by calling {@link
584 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
585 * be thrown and caught by this function.
586 *
587 * @param callExtras the bundle to verify
588 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
589 */
590 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
591 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700592 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800593 * bundle, resulting in a IllegalArgumentException while validating data under {@link
594 * Bundle#containsKey(String)}.
595 */
596 try {
597 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
598 return false;
599 } catch (IllegalArgumentException e) {
600 LogUtil.e(
601 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
602 return true;
603 }
604 }
605
606 protected void updateFromCallExtras(Bundle callExtras) {
607 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
608 /**
609 * If the bundle is corrupted, abandon information update as a work around. These are not
610 * critical for the dialer to function.
611 */
612 return;
613 }
614 // Check for a change in the child address and notify any listeners.
615 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
616 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800617 if (!Objects.equals(childNumber, this.childNumber)) {
618 this.childNumber = childNumber;
619 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800620 listener.onDialerCallChildNumberChange();
621 }
622 }
623 }
624
625 // Last forwarded number comes in as an array of strings. We want to choose the
626 // last item in the array. The forwarding numbers arrive independently of when the
627 // call is originally set up, so we need to notify the the UI of the change.
628 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
629 ArrayList<String> lastForwardedNumbers =
630 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
631
632 if (lastForwardedNumbers != null) {
633 String lastForwardedNumber = null;
634 if (!lastForwardedNumbers.isEmpty()) {
635 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
636 }
637
linyuh183cb712017-12-27 17:02:37 -0800638 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
639 this.lastForwardedNumber = lastForwardedNumber;
640 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800641 listener.onDialerCallLastForwardedNumberChange();
642 }
643 }
644 }
645 }
646
647 // DialerCall subject is present in the extras at the start of call, so we do not need to
648 // notify any other listeners of this.
649 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
650 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800651 if (!Objects.equals(this.callSubject, callSubject)) {
652 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800653 }
654 }
655 }
656
Eric Erfanianccca3152017-02-22 16:32:36 -0800657 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800658 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800659 }
660
Eric Erfanianc857f902017-05-15 14:05:33 -0700661 /**
662 * @return name appended with a number if the number is restricted/unknown and the user has
663 * received more than one restricted/unknown call.
664 */
665 @Nullable
666 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800667 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
668 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700669 }
670 return name;
671 }
672
673 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800674 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700675 }
676
677 private boolean isHiddenNumber() {
678 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
679 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
680 }
681
Eric Erfanianccca3152017-02-22 16:32:36 -0800682 public boolean hasShownWiFiToLteHandoverToast() {
683 return hasShownWiFiToLteHandoverToast;
684 }
685
686 public void setHasShownWiFiToLteHandoverToast() {
687 hasShownWiFiToLteHandoverToast = true;
688 }
689
690 public boolean showWifiHandoverAlertAsToast() {
691 return doNotShowDialogForHandoffToWifiFailure;
692 }
693
694 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
695 doNotShowDialogForHandoffToWifiFailure = bool;
696 }
697
698 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800699 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800700 }
701
702 @Nullable
703 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800704 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800705 }
706
707 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800708 telecomCall.reject(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800709 setState(State.BLOCKED);
710 }
711
712 @Nullable
713 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800714 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800715 }
716
717 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800718 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800719 }
720
721 public boolean isPotentialEmergencyCallback() {
722 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
723 // is actually in emergency callback mode (ie data is disabled).
724 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
725 return true;
726 }
727 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
728 // as a potential emergency callback.
729 if (getExtras() != null
730 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
731 > 0) {
732 long lastEmergencyCallMillis =
733 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
734 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
735 return true;
736 }
737 }
738 return false;
739 }
740
741 boolean isInEmergencyCallbackWindow(long timestampMillis) {
742 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800743 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800744 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
745 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
746 }
747
748 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800749 if (telecomCall != null && telecomCall.getParent() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800750 return State.CONFERENCED;
751 } else {
linyuh183cb712017-12-27 17:02:37 -0800752 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800753 }
754 }
755
yueg265089a2017-10-06 14:35:15 -0700756 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800757 return state;
yueg265089a2017-10-06 14:35:15 -0700758 }
759
Eric Erfanianccca3152017-02-22 16:32:36 -0800760 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700761 if (state == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800762 logState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700763 } else if (state == State.DISCONNECTED) {
764 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800765 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
linyuh183cb712017-12-27 17:02:37 -0800766 if (this.state != state) {
767 logState.duration = newDuration;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700768 } else {
769 LogUtil.i(
770 "DialerCall.setState",
771 "ignoring state transition from DISCONNECTED to DISCONNECTED."
772 + " Duration would have changed from %s to %s",
linyuh183cb712017-12-27 17:02:37 -0800773 logState.duration,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700774 newDuration);
775 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800776 }
linyuh183cb712017-12-27 17:02:37 -0800777 this.state = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800778 }
779
780 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800781 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800782 }
783
784 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800785 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800786 }
787
788 @Nullable
789 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800790 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800791 }
792
793 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800794 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800795 }
796
797 @Nullable
798 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800799 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800800 }
801
802 /** @return The child number for the call, or {@code null} if none specified. */
803 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800804 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800805 }
806
807 /** @return The last forwarded number for the call, or {@code null} if none specified. */
808 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800809 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800810 }
811
wangqif4ba3452018-01-09 11:26:29 -0800812 public boolean isCallForwarded() {
813 return isCallForwarded;
814 }
815
Eric Erfanianccca3152017-02-22 16:32:36 -0800816 /** @return The call subject, or {@code null} if none specified. */
817 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800818 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800819 }
820
821 /**
822 * @return {@code true} if the call's phone account supports call subjects, {@code false}
823 * otherwise.
824 */
825 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800826 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800827 }
828
829 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
830 public DisconnectCause getDisconnectCause() {
linyuh183cb712017-12-27 17:02:37 -0800831 if (state == State.DISCONNECTED || state == State.IDLE) {
832 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800833 }
834
835 return new DisconnectCause(DisconnectCause.UNKNOWN);
836 }
837
838 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800839 this.disconnectCause = disconnectCause;
840 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800841 }
842
843 /** Returns the possible text message responses. */
844 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800845 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800846 }
847
848 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
849 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800850 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800851
852 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
853 // We allow you to merge if the capabilities allow it or if it is a call with
854 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800855 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800856 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
857 // Cannot merge calls if there are no calls to merge with.
858 return false;
859 }
860 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
861 }
862 return (capabilities == (capabilities & supportedCapabilities));
863 }
864
865 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800866 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800867 }
868
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700869 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800870 public String getUniqueCallId() {
871 return uniqueCallId;
872 }
873
874 /** Gets the time when the call first became active. */
875 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800876 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800877 }
878
879 public boolean isConferenceCall() {
880 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
881 }
882
883 @Nullable
884 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -0800885 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -0800886 }
887
888 @Nullable
889 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -0800890 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800891 }
892
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700893 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -0800894 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -0800895 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800896 }
897
898 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -0800899 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -0800900 }
901
902 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -0800903 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -0800904 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -0800905 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -0800906 }
907 return null;
908 }
909
910 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -0800911 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -0800912 }
913
914 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -0700915 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700916 }
917
918 public boolean hasReceivedVideoUpgradeRequest() {
919 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
920 }
921
922 public boolean hasSentVideoUpgradeRequest() {
923 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -0800924 }
925
926 /**
927 * Determines if the call handle is an emergency number or not and caches the result to avoid
928 * repeated calls to isEmergencyNumber.
929 */
930 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -0800931 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800932 }
933
Eric Erfanianccca3152017-02-22 16:32:36 -0800934 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -0800935 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -0800936 }
937
938 /**
939 * Determines if the call is an external call.
940 *
941 * <p>An external call is one which does not exist locally for the {@link
942 * android.telecom.ConnectionService} it is associated with.
943 *
944 * <p>External calls are only supported in N and higher.
945 *
946 * @return {@code true} if the call is an external call, {@code false} otherwise.
947 */
948 public boolean isExternalCall() {
949 return VERSION.SDK_INT >= VERSION_CODES.N
950 && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
951 }
952
953 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800954 * Determines if answering this call will cause an ongoing video call to be dropped.
955 *
956 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
957 * otherwise.
958 */
959 public boolean answeringDisconnectsForegroundVideoCall() {
960 Bundle extras = getExtras();
961 if (extras == null
962 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
963 return false;
964 }
965 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
966 }
967
968 private void parseCallSpecificAppData() {
969 if (isExternalCall()) {
970 return;
971 }
972
linyuh183cb712017-12-27 17:02:37 -0800973 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
974 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700975
linyuh183cb712017-12-27 17:02:37 -0800976 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -0700977 CallSpecificAppData.newBuilder()
978 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
979 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800980 }
981 if (getState() == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800982 logState.callSpecificAppData =
983 logState
Eric Erfanian8369df02017-05-03 10:27:13 -0700984 .callSpecificAppData
985 .toBuilder()
986 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
987 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800988 }
989 }
990
991 @Override
992 public String toString() {
linyuh183cb712017-12-27 17:02:37 -0800993 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800994 // This should happen only in testing since otherwise we would never have a null
995 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -0800996 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -0800997 }
998
999 return String.format(
1000 Locale.US,
1001 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001002 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001003 id,
Eric Erfanianccca3152017-02-22 16:32:36 -08001004 State.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001005 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1006 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1007 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001008 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001009 this.telecomCall.getConferenceableCalls(),
1010 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001011 getVideoTech().getSessionModificationState(),
1012 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001013 }
1014
1015 public String toSimpleString() {
1016 return super.toString();
1017 }
1018
1019 @CallHistoryStatus
1020 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001021 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001022 }
1023
1024 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001025 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001026 }
1027
1028 public boolean didShowCameraPermission() {
1029 return didShowCameraPermission;
1030 }
1031
1032 public void setDidShowCameraPermission(boolean didShow) {
1033 didShowCameraPermission = didShow;
1034 }
1035
Eric Erfanian938468d2017-10-24 14:05:52 -07001036 @Nullable
1037 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001038 return isInGlobalSpamList;
1039 }
1040
1041 public void setIsInGlobalSpamList(boolean inSpamList) {
1042 isInGlobalSpamList = inSpamList;
1043 }
1044
Eric Erfanian938468d2017-10-24 14:05:52 -07001045 @Nullable
1046 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001047 return isInUserSpamList;
1048 }
1049
1050 public void setIsInUserSpamList(boolean inSpamList) {
1051 isInUserSpamList = inSpamList;
1052 }
1053
Eric Erfanian938468d2017-10-24 14:05:52 -07001054 @Nullable
1055 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001056 return isInUserWhiteList;
1057 }
1058
1059 public void setIsInUserWhiteList(boolean inWhiteList) {
1060 isInUserWhiteList = inWhiteList;
1061 }
1062
1063 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001064 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001065 }
1066
1067 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001068 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001069 }
1070
1071 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001072 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001073 }
1074
1075 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001076 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001077 }
1078
1079 public boolean isRemotelyHeld() {
1080 return isRemotelyHeld;
1081 }
1082
Eric Erfanian2ca43182017-08-31 06:57:16 -07001083 public boolean isMergeInProcess() {
1084 return isMergeInProcess;
1085 }
1086
Eric Erfanianccca3152017-02-22 16:32:36 -08001087 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001088 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001089 }
1090
erfanian2cf2c342017-12-21 12:01:33 -08001091 /**
1092 * Try and determine if the call used assisted dialing.
1093 *
1094 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1095 * implmentation is complete in P+.
1096 *
1097 * @return a boolean indicating assisted dialing may have been performed
1098 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001099 public boolean isAssistedDialed() {
1100 if (getIntentExtras() != null) {
erfanian2cf2c342017-12-21 12:01:33 -08001101 // O_MR1 and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
1102 // was used. The Dialer client is responsible for performing assisted dialing before
1103 // placing the outgoing call.
1104 //
1105 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1106 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1107 && getAssistedDialingExtras() != null
1108 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1109 return true;
1110 }
1111 }
1112
1113 // Starting in P+ USE_ASSISTED_DIALING indicates that the client requested the platform
1114 // perform assisted dialing. PROPERTY_ASSISTED_DIALING_USED indicates assisted dialing took
1115 // place.
1116 if (hasProperty(TelephonyManagerCompat.PROPERTY_ASSISTED_DIALING_USED)
1117 && Build.VERSION.SDK_INT > ConcreteCreator.BUILD_CODE_CEILING) {
1118 return true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001119 }
1120 return false;
1121 }
1122
erfanian2cf2c342017-12-21 12:01:33 -08001123 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001124 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001125 if (getIntentExtras() == null) {
1126 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001127 }
erfanian2cf2c342017-12-21 12:01:33 -08001128
1129 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1130 return null;
1131 }
1132
1133 return TransformationInfo.newInstanceFromBundle(
1134 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001135 }
1136
Eric Erfanianccca3152017-02-22 16:32:36 -08001137 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001138 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001139 }
1140
Eric Erfanian2ca43182017-08-31 06:57:16 -07001141 public int getAnswerAndReleaseButtonDisplayedTimes() {
1142 return answerAndReleaseButtonDisplayedTimes;
1143 }
1144
1145 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1146 answerAndReleaseButtonDisplayedTimes++;
1147 }
1148
1149 public boolean getReleasedByAnsweringSecondCall() {
1150 return releasedByAnsweringSecondCall;
1151 }
1152
1153 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1154 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1155 }
1156
1157 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1158 return secondCallWithoutAnswerAndReleasedButtonTimes;
1159 }
1160
1161 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1162 secondCallWithoutAnswerAndReleasedButtonTimes++;
1163 }
1164
Eric Erfanian8369df02017-05-03 10:27:13 -07001165 @Nullable
1166 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001167 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001168 }
1169
1170 public void setEnrichedCallCapabilities(
1171 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001172 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001173 }
1174
1175 @Nullable
1176 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001177 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001178 }
1179
1180 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001181 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001182 }
1183
Eric Erfanianccca3152017-02-22 16:32:36 -08001184 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001185 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001186 }
1187
Eric Erfanianccca3152017-02-22 16:32:36 -08001188 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1189 LogUtil.i(
1190 "DialerCall.phoneAccountSelected",
1191 "accountHandle: %s, setDefault: %b",
1192 accountHandle,
1193 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001194 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001195 }
1196
1197 public void disconnect() {
1198 LogUtil.i("DialerCall.disconnect", "");
1199 setState(DialerCall.State.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001200 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001201 listener.onDialerCallUpdate();
1202 }
linyuh183cb712017-12-27 17:02:37 -08001203 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001204 }
1205
1206 public void hold() {
1207 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001208 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001209 }
1210
1211 public void unhold() {
1212 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001213 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001214 }
1215
1216 public void splitFromConference() {
1217 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001218 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001219 }
1220
1221 public void answer(int videoState) {
1222 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001223 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001224 }
1225
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001226 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001227 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001228 }
1229
Eric Erfanianccca3152017-02-22 16:32:36 -08001230 public void reject(boolean rejectWithMessage, String message) {
1231 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001232 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001233 }
1234
1235 /** Return the string label to represent the call provider */
1236 public String getCallProviderLabel() {
1237 if (callProviderLabel == null) {
1238 PhoneAccount account = getPhoneAccount();
1239 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001240 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001241 callProviderLabel = account.getLabel().toString();
1242 }
1243 }
1244 if (callProviderLabel == null) {
1245 callProviderLabel = "";
1246 }
1247 }
1248 return callProviderLabel;
1249 }
1250
1251 private PhoneAccount getPhoneAccount() {
1252 PhoneAccountHandle accountHandle = getAccountHandle();
1253 if (accountHandle == null) {
1254 return null;
1255 }
linyuh183cb712017-12-27 17:02:37 -08001256 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001257 }
1258
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001259 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001260 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001261 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001262
1263 // Only store the first video tech type found to be available during the life of the call.
1264 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1265 // Update the video tech.
1266 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1267 }
1268 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001269 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001270 }
1271
Eric Erfanianccca3152017-02-22 16:32:36 -08001272 public String getCallbackNumber() {
1273 if (callbackNumber == null) {
1274 // Show the emergency callback number if either:
1275 // 1. This is an emergency call.
1276 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1277 // number.
1278 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1279
1280 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001281 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001282 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001283 }
1284
Eric Erfanianccca3152017-02-22 16:32:36 -08001285 if (callbackNumber == null) {
1286 callbackNumber = "";
1287 }
1288 }
1289 return callbackNumber;
1290 }
1291
wangqi97539352017-09-25 11:15:16 -07001292 public String getSimCountryIso() {
1293 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001294 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001295 .getSimCountryIso();
1296 if (!TextUtils.isEmpty(simCountryIso)) {
1297 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1298 }
1299 return simCountryIso;
1300 }
1301
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001302 @Override
1303 public void onVideoTechStateChanged() {
1304 update();
1305 }
1306
1307 @Override
1308 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001309 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001310 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001311 listener.onDialerCallSessionModificationStateChange();
1312 }
wangqi9982f0d2017-10-11 17:46:07 -07001313 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001314 }
1315
1316 @Override
1317 public void onCameraDimensionsChanged(int width, int height) {
1318 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1319 }
1320
1321 @Override
1322 public void onPeerDimensionsChanged(int width, int height) {
1323 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1324 }
1325
1326 @Override
1327 public void onVideoUpgradeRequestReceived() {
1328 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1329
linyuh183cb712017-12-27 17:02:37 -08001330 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001331 listener.onDialerCallUpgradeToVideo();
1332 }
1333
1334 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001335
linyuh183cb712017-12-27 17:02:37 -08001336 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001337 .logCallImpression(
1338 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1339 }
1340
1341 @Override
1342 public void onUpgradedToVideo(boolean switchToSpeaker) {
1343 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1344
1345 if (!switchToSpeaker) {
1346 return;
1347 }
1348
1349 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1350
1351 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1352 LogUtil.e(
1353 "DialerCall.onUpgradedToVideo",
1354 "toggling speakerphone not allowed when bluetooth supported.");
1355 return;
1356 }
1357
1358 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1359 return;
1360 }
1361
1362 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001363 }
1364
Eric Erfanian2ca43182017-08-31 06:57:16 -07001365 @Override
1366 public void onCapabilitiesUpdated() {
1367 if (getNumber() == null) {
1368 return;
1369 }
1370 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001371 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001372 if (capabilities != null) {
1373 setEnrichedCallCapabilities(capabilities);
1374 update();
1375 }
1376 }
1377
1378 @Override
1379 public void onEnrichedCallStateChanged() {
1380 updateEnrichedCallSession();
1381 }
1382
1383 @Override
1384 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001385 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001386 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1387 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001388 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001389 .logCallImpression(
1390 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1391 getUniqueCallId(),
1392 getTimeAddedMs());
1393 }
1394 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001395 }
1396
1397 private void updateEnrichedCallSession() {
1398 if (getNumber() == null) {
1399 return;
1400 }
1401 if (getEnrichedCallSession() != null) {
1402 // State changes to existing sessions are currently handled by the UI components (which have
1403 // their own listeners). Someday instead we could remove those and just call update() here and
1404 // have the usual onDialerCallUpdate update the UI.
1405 dispatchOnEnrichedCallSessionUpdate();
1406 return;
1407 }
1408
linyuh183cb712017-12-27 17:02:37 -08001409 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001410
1411 Filter filter =
1412 isIncoming()
1413 ? manager.createIncomingCallComposerFilter()
1414 : manager.createOutgoingCallComposerFilter();
1415
1416 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1417 if (session == null) {
1418 return;
1419 }
1420
1421 session.setUniqueDialerCallId(getUniqueCallId());
1422 setEnrichedCallSession(session);
1423
1424 LogUtil.i(
1425 "DialerCall.updateEnrichedCallSession",
1426 "setting session %d's dialer id to %s",
1427 session.getSessionId(),
1428 getUniqueCallId());
1429
1430 dispatchOnEnrichedCallSessionUpdate();
1431 }
1432
1433 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001434 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001435 listener.onEnrichedCallSessionUpdate();
1436 }
1437 }
1438
1439 void onRemovedFromCallList() {
1440 // Ensure we clean up when this call is removed.
linyuh183cb712017-12-27 17:02:37 -08001441 videoTechManager.dispatchRemovedFromCallList();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001442 }
1443
wangqi4d705e52017-09-28 12:23:35 -07001444 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1445 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001446 }
1447
Eric Erfanianccca3152017-02-22 16:32:36 -08001448 /**
1449 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1450 * means there is no result.
1451 */
1452 @IntDef({
1453 CALL_HISTORY_STATUS_UNKNOWN,
1454 CALL_HISTORY_STATUS_PRESENT,
1455 CALL_HISTORY_STATUS_NOT_PRESENT
1456 })
1457 @Retention(RetentionPolicy.SOURCE)
1458 public @interface CallHistoryStatus {}
1459
1460 /* Defines different states of this call */
1461 public static class State {
1462
1463 public static final int INVALID = 0;
1464 public static final int NEW = 1; /* The call is new. */
1465 public static final int IDLE = 2; /* The call is idle. Nothing active */
1466 public static final int ACTIVE = 3; /* There is an active call */
1467 public static final int INCOMING = 4; /* A normal incoming phone call */
1468 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1469 public static final int DIALING = 6; /* An outgoing call during dial phase */
1470 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1471 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1472 public static final int DISCONNECTING = 9; /* A call is being ended. */
1473 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1474 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1475 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1476 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1477 public static final int BLOCKED = 14; /* The number was found on the block list */
1478 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001479 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001480
1481 public static boolean isConnectingOrConnected(int state) {
1482 switch (state) {
1483 case ACTIVE:
1484 case INCOMING:
1485 case CALL_WAITING:
1486 case CONNECTING:
1487 case DIALING:
1488 case PULLING:
1489 case REDIALING:
1490 case ONHOLD:
1491 case CONFERENCED:
1492 return true;
1493 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001494 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001495 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001496 }
1497
1498 public static boolean isDialing(int state) {
1499 return state == DIALING || state == PULLING || state == REDIALING;
1500 }
1501
1502 public static String toString(int state) {
1503 switch (state) {
1504 case INVALID:
1505 return "INVALID";
1506 case NEW:
1507 return "NEW";
1508 case IDLE:
1509 return "IDLE";
1510 case ACTIVE:
1511 return "ACTIVE";
1512 case INCOMING:
1513 return "INCOMING";
1514 case CALL_WAITING:
1515 return "CALL_WAITING";
1516 case DIALING:
1517 return "DIALING";
1518 case PULLING:
1519 return "PULLING";
1520 case REDIALING:
1521 return "REDIALING";
1522 case ONHOLD:
1523 return "ONHOLD";
1524 case DISCONNECTING:
1525 return "DISCONNECTING";
1526 case DISCONNECTED:
1527 return "DISCONNECTED";
1528 case CONFERENCED:
1529 return "CONFERENCED";
1530 case SELECT_PHONE_ACCOUNT:
1531 return "SELECT_PHONE_ACCOUNT";
1532 case CONNECTING:
1533 return "CONNECTING";
1534 case BLOCKED:
1535 return "BLOCKED";
1536 default:
1537 return "UNKNOWN";
1538 }
1539 }
1540 }
1541
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001542 /** Camera direction constants */
1543 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001544 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1545 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1546 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001547 }
1548
1549 /**
1550 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1551 * existing call member variables, but this duplication helps to ensure that none of these logging
1552 * variables will interface with/and affect call logic.
1553 */
1554 public static class LogState {
1555
1556 public DisconnectCause disconnectCause;
1557 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001558 public ContactLookupResult.Type contactLookupResult =
1559 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001560 public CallSpecificAppData callSpecificAppData;
1561 // If this was a conference call, the total number of calls involved in the conference.
1562 public int conferencedCalls = 0;
1563 public long duration = 0;
1564 public boolean isLogged = false;
1565
Eric Erfanian8369df02017-05-03 10:27:13 -07001566 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001567 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001568 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001569 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001570 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001571 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001572 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001573 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001574 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001575 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001576 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001577 return "Voicemail";
1578 default:
1579 return "Not found";
1580 }
1581 }
1582
1583 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1584 if (callSpecificAppData == null) {
1585 return "null";
1586 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001587 switch (callSpecificAppData.getCallInitiationType()) {
1588 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001589 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001590 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001591 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001592 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001593 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001594 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001595 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001596 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001597 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001598 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001599 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001600 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001601 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001602 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001603 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001604 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001605 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001606 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001607 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001608 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001609 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001610 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001611 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001612 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001613 return "Launcher Shortcut";
1614 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001615 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001616 }
1617 }
1618
1619 @Override
1620 public String toString() {
1621 return String.format(
1622 Locale.US,
1623 "["
1624 + "%s, " // DisconnectCause toString already describes the object type
1625 + "isIncoming: %s, "
1626 + "contactLookup: %s, "
1627 + "callInitiation: %s, "
1628 + "duration: %s"
1629 + "]",
1630 disconnectCause,
1631 isIncoming,
1632 lookupToString(contactLookupResult),
1633 initiationToString(callSpecificAppData),
1634 duration);
1635 }
1636 }
1637
roldenburgd7490db2018-01-09 13:51:29 -08001638 /** Coordinates the available VideoTech implementations for a call. */
1639 @VisibleForTesting
1640 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001641 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001642 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001643 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001644 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001645 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001646
roldenburgd7490db2018-01-09 13:51:29 -08001647 @VisibleForTesting
1648 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001649 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001650
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001651 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001652 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001653 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001654
1655 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001656 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001657
linyuh183cb712017-12-27 17:02:37 -08001658 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001659
roldenburgd7490db2018-01-09 13:51:29 -08001660 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001661 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001662 .getRcsVideoShareFactory()
1663 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001664 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001665 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001666 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001667 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001668
1669 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001670 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001671 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001672
1673 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001674 }
1675
roldenburgd7490db2018-01-09 13:51:29 -08001676 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001677 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001678 if (savedTech == emptyVideoTech) {
1679 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001680 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001681 savedTech = tech;
1682 savedTech.becomePrimary();
1683 break;
1684 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001685 }
roldenburg6bd612f2018-01-18 12:57:19 -08001686 } else if (savedTech instanceof DuoVideoTech
1687 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001688 // RCS Video Share will become available after the capability exchange which is slower than
1689 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1690 // not in the middle of any session changes, let it take over.
1691 savedTech = rcsVideoShare;
1692 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001693 }
1694
roldenburgd7490db2018-01-09 13:51:29 -08001695 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001696 }
1697
roldenburgd7490db2018-01-09 13:51:29 -08001698 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001699 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001700 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001701 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001702 }
1703 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001704
1705 void dispatchRemovedFromCallList() {
1706 for (VideoTech videoTech : videoTechs) {
1707 videoTech.onRemovedFromCallList();
1708 }
1709 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001710 }
1711
Eric Erfanianccca3152017-02-22 16:32:36 -08001712 /** Called when canned text responses have been loaded. */
1713 public interface CannedTextResponsesLoadedListener {
1714 void onCannedTextResponsesLoaded(DialerCall call);
1715 }
1716}