blob: 1c274464497c2d3011689c3c4d909226b0508459 [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;
wangqida410d32018-03-06 16:51:38 -080020import android.annotation.SuppressLint;
wangqi8d407a02018-02-15 15:32:52 -080021import android.annotation.TargetApi;
Eric Erfanianccca3152017-02-22 16:32:36 -080022import android.content.Context;
23import android.hardware.camera2.CameraCharacteristics;
24import android.net.Uri;
erfanian2cf2c342017-12-21 12:01:33 -080025import android.os.Build;
Eric Erfanianccca3152017-02-22 16:32:36 -080026import android.os.Build.VERSION;
27import android.os.Build.VERSION_CODES;
28import android.os.Bundle;
wangqida410d32018-03-06 16:51:38 -080029import android.os.PersistableBundle;
Eric Erfanianccca3152017-02-22 16:32:36 -080030import android.os.Trace;
31import android.support.annotation.IntDef;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070032import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080033import android.support.annotation.Nullable;
twyendde01c52017-09-22 10:07:31 -070034import android.support.annotation.VisibleForTesting;
wangqif4ba3452018-01-09 11:26:29 -080035import android.support.v4.os.BuildCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import android.telecom.Call;
37import android.telecom.Call.Details;
wangqi219b8702018-02-13 09:34:41 -080038import android.telecom.Call.RttCall;
Eric Erfanian8369df02017-05-03 10:27:13 -070039import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080040import android.telecom.Connection;
41import android.telecom.DisconnectCause;
42import android.telecom.GatewayInfo;
43import android.telecom.InCallService.VideoCall;
44import android.telecom.PhoneAccount;
45import android.telecom.PhoneAccountHandle;
46import android.telecom.StatusHints;
47import android.telecom.TelecomManager;
48import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080049import android.text.TextUtils;
roldenburg08424ee2018-03-28 17:02:46 -070050import android.widget.Toast;
Eric Erfanianccca3152017-02-22 16:32:36 -080051import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080052import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
erfanian2cf2c342017-12-21 12:01:33 -080053import com.android.dialer.assisteddialing.ConcreteCreator;
erfaniand0f207f2017-10-11 12:23:29 -070054import com.android.dialer.assisteddialing.TransformationInfo;
Eric Erfanian8369df02017-05-03 10:27:13 -070055import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080056import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070057import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080058import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080059import com.android.dialer.common.LogUtil;
wangqid6b10d52018-04-12 14:44:06 -070060import com.android.dialer.common.concurrent.DefaultFutureCallback;
Eric Erfanian2ca43182017-08-31 06:57:16 -070061import com.android.dialer.compat.telephony.TelephonyManagerCompat;
62import com.android.dialer.configprovider.ConfigProviderBindings;
roldenburg4f026392017-10-13 18:42:20 -070063import com.android.dialer.duo.DuoComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070064import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070065import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070066import com.android.dialer.enrichedcall.EnrichedCallManager;
67import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
68import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
69import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070070import com.android.dialer.enrichedcall.Session;
wangqi9982f0d2017-10-11 17:46:07 -070071import com.android.dialer.location.GeoUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070072import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070073import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070074import com.android.dialer.logging.DialerImpression;
75import com.android.dialer.logging.Logger;
twyen73a74c32018-03-07 12:12:24 -080076import com.android.dialer.preferredsim.PreferredAccountRecorder;
wangqied677ef2018-04-04 12:03:00 -070077import com.android.dialer.rtt.RttTranscript;
wangqid6b10d52018-04-12 14:44:06 -070078import com.android.dialer.rtt.RttTranscriptUtil;
twyena4745bd2017-12-12 18:40:11 -080079import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070080import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070081import com.android.dialer.theme.R;
wangqi9982f0d2017-10-11 17:46:07 -070082import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070083import com.android.incallui.audiomode.AudioModeProvider;
wangqibb94ca62018-04-27 14:34:04 -070084import com.android.incallui.call.state.DialerCallState;
Eric Erfanianccca3152017-02-22 16:32:36 -080085import com.android.incallui.latencyreport.LatencyReport;
wangqibed9e2f2018-04-30 11:55:07 -070086import com.android.incallui.rtt.protocol.RttChatMessage;
erfaniandcc84cd2018-04-17 12:41:05 -070087import com.android.incallui.speakeasy.runtime.Constraints;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070088import com.android.incallui.videotech.VideoTech;
89import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070090import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070091import com.android.incallui.videotech.empty.EmptyVideoTech;
92import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070093import com.android.incallui.videotech.utils.VideoUtils;
wangqid6b10d52018-04-12 14:44:06 -070094import com.google.common.util.concurrent.Futures;
wangqid6b10d52018-04-12 14:44:06 -070095import com.google.common.util.concurrent.MoreExecutors;
wangqibed9e2f2018-04-30 11:55:07 -070096import java.io.IOException;
Eric Erfanianccca3152017-02-22 16:32:36 -080097import java.lang.annotation.Retention;
98import java.lang.annotation.RetentionPolicy;
99import java.util.ArrayList;
100import java.util.List;
101import java.util.Locale;
102import java.util.Objects;
103import java.util.UUID;
104import java.util.concurrent.CopyOnWriteArrayList;
105import java.util.concurrent.TimeUnit;
106
107/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -0700108public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -0800109
110 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
111 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
112 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700113
114 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -0700115 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700116 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
117
Eric Erfanianccca3152017-02-22 16:32:36 -0800118 private static final String ID_PREFIX = "DialerCall_";
119 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
120 "emergency_callback_window_millis";
linyuh183cb712017-12-27 17:02:37 -0800121 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800122
123 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700124 * A counter used to append to restricted/private/hidden calls so that users can identify them in
125 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
126 * are no live calls.
127 */
linyuh183cb712017-12-27 17:02:37 -0800128 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700129
130 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800131 * The unique call ID for every call. This will help us to identify each call and allow us the
132 * ability to stitch impressions to calls if needed.
133 */
134 private final String uniqueCallId = UUID.randomUUID().toString();
135
linyuh183cb712017-12-27 17:02:37 -0800136 private final Call telecomCall;
137 private final LatencyReport latencyReport;
138 private final String id;
139 private final int hiddenId;
140 private final List<String> childCallIds = new ArrayList<>();
141 private final LogState logState = new LogState();
142 private final Context context;
143 private final DialerCallDelegate dialerCallDelegate;
144 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
145 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800146 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800147 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800148
erfaniand05d8992018-03-20 19:42:26 -0700149 private boolean isSpeakEasyCall;
linyuh183cb712017-12-27 17:02:37 -0800150 private boolean isEmergencyCall;
151 private Uri handle;
wangqibb94ca62018-04-27 14:34:04 -0700152 private int state = DialerCallState.INVALID;
linyuh183cb712017-12-27 17:02:37 -0800153 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800154
roldenburg08424ee2018-03-28 17:02:46 -0700155 private boolean hasShownLteToWiFiHandoverToast;
Eric Erfanianccca3152017-02-22 16:32:36 -0800156 private boolean hasShownWiFiToLteHandoverToast;
157 private boolean doNotShowDialogForHandoffToWifiFailure;
158
linyuh183cb712017-12-27 17:02:37 -0800159 private String childNumber;
160 private String lastForwardedNumber;
wangqif4ba3452018-01-09 11:26:29 -0800161 private boolean isCallForwarded;
linyuh183cb712017-12-27 17:02:37 -0800162 private String callSubject;
twyen27c37182018-04-24 11:48:12 -0700163 @Nullable private PhoneAccountHandle phoneAccountHandle;
linyuh183cb712017-12-27 17:02:37 -0800164 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
165 private boolean isSpam;
166 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700167
168 @Nullable private Boolean isInUserSpamList;
169
170 @Nullable private Boolean isInUserWhiteList;
171
172 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800173 private boolean didShowCameraPermission;
wangqida410d32018-03-06 16:51:38 -0800174 private boolean didDismissVideoChargesAlertDialog;
175 private PersistableBundle carrierConfig;
Eric Erfanianccca3152017-02-22 16:32:36 -0800176 private String callProviderLabel;
177 private String callbackNumber;
linyuh183cb712017-12-27 17:02:37 -0800178 private int cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
179 private EnrichedCallCapabilities enrichedCallCapabilities;
180 private Session enrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800181
Eric Erfanian2ca43182017-08-31 06:57:16 -0700182 private int answerAndReleaseButtonDisplayedTimes = 0;
183 private boolean releasedByAnsweringSecondCall = false;
184 // Times when a second call is received but AnswerAndRelease button is not shown
185 // since it's not supported.
186 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700187 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700188
wangqi4d705e52017-09-28 12:23:35 -0700189 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
190 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700191 private boolean isVoicemailNumber;
192 private List<PhoneAccountHandle> callCapableAccounts;
193 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700194
Android Dialer974fc292018-02-01 16:12:25 -0800195 private volatile boolean feedbackRequested = false;
196
twyen73a74c32018-03-07 12:12:24 -0800197 @Nullable private PreferredAccountRecorder preferredAccountRecorder;
wangqia0c49f62018-04-25 15:04:01 -0700198 private boolean isCallRemoved;
twyen73a74c32018-03-07 12:12:24 -0800199
Eric Erfanianccca3152017-02-22 16:32:36 -0800200 public static String getNumberFromHandle(Uri handle) {
201 return handle == null ? "" : handle.getSchemeSpecificPart();
202 }
203
204 /**
205 * Whether the call is put on hold by remote party. This is different than the {@link
wangqibb94ca62018-04-27 14:34:04 -0700206 * DialerCallState#ONHOLD} state which indicates that the call is being held locally on the
207 * device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800208 */
209 private boolean isRemotelyHeld;
210
Eric Erfanian2ca43182017-08-31 06:57:16 -0700211 /** Indicates whether this call is currently in the process of being merged into a conference. */
212 private boolean isMergeInProcess;
213
Eric Erfanianccca3152017-02-22 16:32:36 -0800214 /**
215 * Indicates whether the phone account associated with this call supports specifying a call
216 * subject.
217 */
linyuh183cb712017-12-27 17:02:37 -0800218 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800219
wangqied677ef2018-04-04 12:03:00 -0700220 public RttTranscript getRttTranscript() {
221 return rttTranscript;
222 }
223
224 public void setRttTranscript(RttTranscript rttTranscript) {
225 this.rttTranscript = rttTranscript;
226 }
227
228 private RttTranscript rttTranscript;
229
linyuh183cb712017-12-27 17:02:37 -0800230 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800231 new Call.Callback() {
232 @Override
233 public void onStateChanged(Call call, int newState) {
234 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
235 update();
236 }
237
238 @Override
239 public void onParentChanged(Call call, Call newParent) {
240 LogUtil.v(
241 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
242 update();
243 }
244
245 @Override
246 public void onChildrenChanged(Call call, List<Call> children) {
247 update();
248 }
249
250 @Override
251 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700252 LogUtil.v(
253 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800254 update();
255 }
256
257 @Override
258 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
259 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700260 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800261 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800262 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800263 listener.onCannedTextResponsesLoaded(DialerCall.this);
264 }
265 }
266
267 @Override
268 public void onPostDialWait(Call call, String remainingPostDialSequence) {
269 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700270 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800271 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
272 update();
273 }
274
275 @Override
276 public void onVideoCallChanged(Call call, VideoCall videoCall) {
277 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700278 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800279 update();
280 }
281
282 @Override
283 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700284 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700285 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800286 }
287
288 @Override
289 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
290 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700291 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800292 "call %s, conferenceable calls: %d",
293 call,
294 conferenceableCalls.size());
295 update();
296 }
297
298 @Override
wangqi219b8702018-02-13 09:34:41 -0800299 public void onRttModeChanged(Call call, int mode) {
300 LogUtil.v("TelecomCallCallback.onRttModeChanged", "mode=%d", mode);
301 }
302
303 @Override
304 public void onRttRequest(Call call, int id) {
305 LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id);
wangqibc28ea72018-04-02 16:23:00 -0700306 for (DialerCallListener listener : listeners) {
307 listener.onDialerCallUpgradeToRtt(id);
308 }
wangqi219b8702018-02-13 09:34:41 -0800309 }
310
311 @Override
312 public void onRttInitiationFailure(Call call, int reason) {
313 LogUtil.v("TelecomCallCallback.onRttInitiationFailure", "reason=%d", reason);
314 update();
315 }
316
317 @Override
318 public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {
319 LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled);
wangqi81e373e2018-04-24 10:50:57 -0700320 if (enabled) {
321 Logger.get(context)
322 .logCallImpression(
323 DialerImpression.Type.RTT_MID_CALL_ENABLED,
324 getUniqueCallId(),
325 getTimeAddedMs());
326 }
wangqi219b8702018-02-13 09:34:41 -0800327 update();
328 }
329
330 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800331 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
332 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700333 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800334 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
335 switch (event) {
336 // The Previous attempt to Merge two calls together has failed in Telecom. We must
337 // now update the UI to possibly re-enable the Merge button based on the number of
338 // currently conferenceable calls available or Connection Capabilities.
339 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
340 update();
341 break;
342 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
343 notifyWiFiToLteHandover();
344 break;
roldenburg08424ee2018-03-28 17:02:46 -0700345 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI:
346 onLteToWifiHandover();
347 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800348 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
349 notifyHandoverToWifiFailed();
350 break;
351 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
352 isRemotelyHeld = true;
353 update();
354 break;
355 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
356 isRemotelyHeld = false;
357 update();
358 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700359 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
360 notifyInternationalCallOnWifi();
361 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700362 case TelephonyManagerCompat.EVENT_MERGE_START:
363 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
364 isMergeInProcess = true;
365 break;
366 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
367 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
368 isMergeInProcess = false;
369 break;
wangqif4ba3452018-01-09 11:26:29 -0800370 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
371 // Only handle this event for P+ since it's unreliable pre-P.
372 if (BuildCompat.isAtLeastP()) {
373 isCallForwarded = true;
374 update();
375 }
376 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800377 default:
378 break;
379 }
380 }
381 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700382
linyuh183cb712017-12-27 17:02:37 -0800383 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800384
385 public DialerCall(
386 Context context,
387 DialerCallDelegate dialerCallDelegate,
388 Call telecomCall,
389 LatencyReport latencyReport,
390 boolean registerCallback) {
391 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800392 this.context = context;
393 this.dialerCallDelegate = dialerCallDelegate;
394 this.telecomCall = telecomCall;
395 this.latencyReport = latencyReport;
396 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800397
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700398 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800399 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700400
401 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700402 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800403 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700404 } else {
linyuh183cb712017-12-27 17:02:37 -0800405 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700406 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800407
408 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800409 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800410 }
411
linyuh183cb712017-12-27 17:02:37 -0800412 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800413 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700414
415 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800416 }
417
twyendde01c52017-09-22 10:07:31 -0700418 /** Test only constructor to avoid initializing dependencies. */
419 @VisibleForTesting
420 DialerCall(Context context) {
linyuh183cb712017-12-27 17:02:37 -0800421 this.context = context;
422 telecomCall = null;
423 latencyReport = null;
424 id = null;
425 hiddenId = 0;
426 dialerCallDelegate = null;
427 videoTechManager = null;
twyendde01c52017-09-22 10:07:31 -0700428 }
429
Eric Erfanianccca3152017-02-22 16:32:36 -0800430 private static int translateState(int state) {
431 switch (state) {
432 case Call.STATE_NEW:
433 case Call.STATE_CONNECTING:
wangqibb94ca62018-04-27 14:34:04 -0700434 return DialerCallState.CONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800435 case Call.STATE_SELECT_PHONE_ACCOUNT:
wangqibb94ca62018-04-27 14:34:04 -0700436 return DialerCallState.SELECT_PHONE_ACCOUNT;
Eric Erfanianccca3152017-02-22 16:32:36 -0800437 case Call.STATE_DIALING:
wangqibb94ca62018-04-27 14:34:04 -0700438 return DialerCallState.DIALING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800439 case Call.STATE_PULLING_CALL:
wangqibb94ca62018-04-27 14:34:04 -0700440 return DialerCallState.PULLING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800441 case Call.STATE_RINGING:
wangqibb94ca62018-04-27 14:34:04 -0700442 return DialerCallState.INCOMING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800443 case Call.STATE_ACTIVE:
wangqibb94ca62018-04-27 14:34:04 -0700444 return DialerCallState.ACTIVE;
Eric Erfanianccca3152017-02-22 16:32:36 -0800445 case Call.STATE_HOLDING:
wangqibb94ca62018-04-27 14:34:04 -0700446 return DialerCallState.ONHOLD;
Eric Erfanianccca3152017-02-22 16:32:36 -0800447 case Call.STATE_DISCONNECTED:
wangqibb94ca62018-04-27 14:34:04 -0700448 return DialerCallState.DISCONNECTED;
Eric Erfanianccca3152017-02-22 16:32:36 -0800449 case Call.STATE_DISCONNECTING:
wangqibb94ca62018-04-27 14:34:04 -0700450 return DialerCallState.DISCONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800451 default:
wangqibb94ca62018-04-27 14:34:04 -0700452 return DialerCallState.INVALID;
Eric Erfanianccca3152017-02-22 16:32:36 -0800453 }
454 }
455
456 public static boolean areSame(DialerCall call1, DialerCall call2) {
457 if (call1 == null && call2 == null) {
458 return true;
459 } else if (call1 == null || call2 == null) {
460 return false;
461 }
462
463 // otherwise compare call Ids
464 return call1.getId().equals(call2.getId());
465 }
466
Eric Erfanianccca3152017-02-22 16:32:36 -0800467 public void addListener(DialerCallListener listener) {
468 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800469 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800470 }
471
472 public void removeListener(DialerCallListener listener) {
473 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800474 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800475 }
476
477 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
478 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800479 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800480 }
481
482 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
483 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800484 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800485 }
486
roldenburg08424ee2018-03-28 17:02:46 -0700487 private void onLteToWifiHandover() {
488 LogUtil.enterBlock("DialerCall.onLteToWifiHandover");
489 if (hasShownLteToWiFiHandoverToast) {
490 return;
491 }
492
493 Toast.makeText(context, R.string.video_call_lte_to_wifi_handover_toast, Toast.LENGTH_LONG)
494 .show();
495 hasShownLteToWiFiHandoverToast = true;
496 }
497
Eric Erfanianccca3152017-02-22 16:32:36 -0800498 public void notifyWiFiToLteHandover() {
499 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800500 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800501 listener.onWiFiToLteHandover();
502 }
503 }
504
505 public void notifyHandoverToWifiFailed() {
506 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800507 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800508 listener.onHandoverToWifiFailure();
509 }
510 }
511
Eric Erfanianc857f902017-05-15 14:05:33 -0700512 public void notifyInternationalCallOnWifi() {
513 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800514 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700515 dialerCallListener.onInternationalCallOnWifi();
516 }
517 }
518
Eric Erfanianccca3152017-02-22 16:32:36 -0800519 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800520 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800521 }
wangqida410d32018-03-06 16:51:38 -0800522
Eric Erfanianccca3152017-02-22 16:32:36 -0800523 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800524 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800525 }
526
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700527 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800528 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700529 }
530
531 public void setCameraDir(int cameraDir) {
532 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
533 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800534 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700535 } else {
linyuh183cb712017-12-27 17:02:37 -0800536 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700537 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800538 }
539
Eric Erfanian2ca43182017-08-31 06:57:16 -0700540 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800541 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700542 }
543
wangqi9982f0d2017-10-11 17:46:07 -0700544 public boolean isVoiceMailNumber() {
545 return isVoicemailNumber;
546 }
547
548 public List<PhoneAccountHandle> getCallCapableAccounts() {
549 return callCapableAccounts;
550 }
551
552 public String getCountryIso() {
553 return countryIso;
554 }
555
556 private void updateIsVoiceMailNumber() {
557 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
558 isVoicemailNumber = true;
roldenburg37a969d2018-02-22 14:46:44 -0800559 return;
wangqi9982f0d2017-10-11 17:46:07 -0700560 }
561
linyuh183cb712017-12-27 17:02:37 -0800562 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700563 isVoicemailNumber = false;
roldenburg37a969d2018-02-22 14:46:44 -0800564 return;
wangqi9982f0d2017-10-11 17:46:07 -0700565 }
566
linyuh183cb712017-12-27 17:02:37 -0800567 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700568 }
569
Eric Erfanianccca3152017-02-22 16:32:36 -0800570 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700571 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800572 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700573 // Clear any cache here that could potentially change on update.
574 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800575 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700576 updateFromTelecomCall();
wangqibb94ca62018-04-27 14:34:04 -0700577 if (oldState != getState() && getState() == DialerCallState.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800578 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800579 listener.onDialerCallDisconnect();
580 }
linyuh183cb712017-12-27 17:02:37 -0800581 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700582 .getEnrichedCallManager()
583 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800584 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700585 .getEnrichedCallManager()
586 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 } else {
linyuh183cb712017-12-27 17:02:37 -0800588 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800589 listener.onDialerCallUpdate();
590 }
591 }
592 Trace.endSection();
593 }
594
wangqi9982f0d2017-10-11 17:46:07 -0700595 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700596 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700597 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800598 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700599
roldenburg6bd612f2018-01-18 12:57:19 -0800600 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700601
linyuh183cb712017-12-27 17:02:37 -0800602 final int translatedState = translateState(telecomCall.getState());
wangqibb94ca62018-04-27 14:34:04 -0700603 if (state != DialerCallState.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800604 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800605 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800606 }
607
linyuh183cb712017-12-27 17:02:37 -0800608 childCallIds.clear();
609 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800610 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800611 childCallIds.add(
612 dialerCallDelegate
613 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800614 .getId());
615 }
616
617 // The number of conferenced calls can change over the course of the call, so use the
618 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800619 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800620
linyuh183cb712017-12-27 17:02:37 -0800621 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800622
623 // If the handle of the call has changed, update state for the call determining if it is an
624 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800625 Uri newHandle = telecomCall.getDetails().getHandle();
626 if (!Objects.equals(handle, newHandle)) {
627 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800628 updateEmergencyCallState();
629 }
630
linyuh183cb712017-12-27 17:02:37 -0800631 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800632 // If the phone account handle of the call is set, cache capability bit indicating whether
633 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800634 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
635 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
636 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800637
linyuh183cb712017-12-27 17:02:37 -0800638 if (phoneAccountHandle != null) {
639 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800640 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800641 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800642 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
wangqida410d32018-03-06 16:51:38 -0800643 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
644 cacheCarrierConfiguration(phoneAccountHandle);
645 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800646 }
647 }
648 }
linyuh183cb712017-12-27 17:02:37 -0800649 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700650 updateIsVoiceMailNumber();
651 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800652 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700653 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700654 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800655 }
656
657 /**
wangqida410d32018-03-06 16:51:38 -0800658 * Caches frequently used carrier configuration locally.
659 *
660 * @param accountHandle The PhoneAccount handle.
661 */
662 @SuppressLint("MissingPermission")
663 private void cacheCarrierConfiguration(PhoneAccountHandle accountHandle) {
664 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
665 return;
666 }
667 if (VERSION.SDK_INT < VERSION_CODES.O) {
668 return;
669 }
670 // TODO(a bug): This may take several seconds to complete, revisit it to move it to worker
671 // thread.
672 carrierConfig =
673 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, accountHandle)
674 .getCarrierConfig();
675 }
676
677 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800678 * Tests corruption of the {@code callExtras} bundle by calling {@link
679 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
680 * be thrown and caught by this function.
681 *
682 * @param callExtras the bundle to verify
683 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
684 */
685 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
686 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700687 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800688 * bundle, resulting in a IllegalArgumentException while validating data under {@link
689 * Bundle#containsKey(String)}.
690 */
691 try {
692 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
693 return false;
694 } catch (IllegalArgumentException e) {
695 LogUtil.e(
696 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
697 return true;
698 }
699 }
700
701 protected void updateFromCallExtras(Bundle callExtras) {
702 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
703 /**
704 * If the bundle is corrupted, abandon information update as a work around. These are not
705 * critical for the dialer to function.
706 */
707 return;
708 }
709 // Check for a change in the child address and notify any listeners.
710 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
711 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800712 if (!Objects.equals(childNumber, this.childNumber)) {
713 this.childNumber = childNumber;
714 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800715 listener.onDialerCallChildNumberChange();
716 }
717 }
718 }
719
720 // Last forwarded number comes in as an array of strings. We want to choose the
721 // last item in the array. The forwarding numbers arrive independently of when the
722 // call is originally set up, so we need to notify the the UI of the change.
723 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
724 ArrayList<String> lastForwardedNumbers =
725 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
726
727 if (lastForwardedNumbers != null) {
728 String lastForwardedNumber = null;
729 if (!lastForwardedNumbers.isEmpty()) {
730 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
731 }
732
linyuh183cb712017-12-27 17:02:37 -0800733 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
734 this.lastForwardedNumber = lastForwardedNumber;
735 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800736 listener.onDialerCallLastForwardedNumberChange();
737 }
738 }
739 }
740 }
741
742 // DialerCall subject is present in the extras at the start of call, so we do not need to
743 // notify any other listeners of this.
744 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
745 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800746 if (!Objects.equals(this.callSubject, callSubject)) {
747 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800748 }
749 }
750 }
751
Eric Erfanianccca3152017-02-22 16:32:36 -0800752 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800753 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800754 }
755
Eric Erfanianc857f902017-05-15 14:05:33 -0700756 /**
757 * @return name appended with a number if the number is restricted/unknown and the user has
758 * received more than one restricted/unknown call.
759 */
760 @Nullable
761 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800762 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
763 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700764 }
765 return name;
766 }
767
768 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800769 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700770 }
771
772 private boolean isHiddenNumber() {
773 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
774 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
775 }
776
Eric Erfanianccca3152017-02-22 16:32:36 -0800777 public boolean hasShownWiFiToLteHandoverToast() {
778 return hasShownWiFiToLteHandoverToast;
779 }
780
781 public void setHasShownWiFiToLteHandoverToast() {
782 hasShownWiFiToLteHandoverToast = true;
783 }
784
785 public boolean showWifiHandoverAlertAsToast() {
786 return doNotShowDialogForHandoffToWifiFailure;
787 }
788
789 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
790 doNotShowDialogForHandoffToWifiFailure = bool;
791 }
792
wangqida410d32018-03-06 16:51:38 -0800793 public boolean showVideoChargesAlertDialog() {
794 if (carrierConfig == null) {
795 return false;
796 }
797 return carrierConfig.getBoolean(
798 TelephonyManagerCompat.CARRIER_CONFIG_KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL);
799 }
800
Eric Erfanianccca3152017-02-22 16:32:36 -0800801 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800802 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800803 }
804
805 @Nullable
806 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800807 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800808 }
809
810 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800811 telecomCall.reject(false, null);
wangqibb94ca62018-04-27 14:34:04 -0700812 setState(DialerCallState.BLOCKED);
Eric Erfanianccca3152017-02-22 16:32:36 -0800813 }
814
815 @Nullable
816 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800817 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800818 }
819
820 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800821 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800822 }
823
824 public boolean isPotentialEmergencyCallback() {
825 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
826 // is actually in emergency callback mode (ie data is disabled).
827 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
828 return true;
829 }
830 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
831 // as a potential emergency callback.
832 if (getExtras() != null
833 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
834 > 0) {
835 long lastEmergencyCallMillis =
836 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
837 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
838 return true;
839 }
840 }
841 return false;
842 }
843
844 boolean isInEmergencyCallbackWindow(long timestampMillis) {
845 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800846 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800847 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
848 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
849 }
850
851 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800852 if (telecomCall != null && telecomCall.getParent() != null) {
wangqibb94ca62018-04-27 14:34:04 -0700853 return DialerCallState.CONFERENCED;
Eric Erfanianccca3152017-02-22 16:32:36 -0800854 } else {
linyuh183cb712017-12-27 17:02:37 -0800855 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800856 }
857 }
858
yueg265089a2017-10-06 14:35:15 -0700859 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800860 return state;
yueg265089a2017-10-06 14:35:15 -0700861 }
862
Eric Erfanianccca3152017-02-22 16:32:36 -0800863 public void setState(int state) {
wangqibb94ca62018-04-27 14:34:04 -0700864 if (state == DialerCallState.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800865 logState.isIncoming = true;
wangqibb94ca62018-04-27 14:34:04 -0700866 } else if (state == DialerCallState.DISCONNECTED) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700867 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800868 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
linyuh183cb712017-12-27 17:02:37 -0800869 if (this.state != state) {
870 logState.duration = newDuration;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700871 } else {
872 LogUtil.i(
873 "DialerCall.setState",
874 "ignoring state transition from DISCONNECTED to DISCONNECTED."
875 + " Duration would have changed from %s to %s",
linyuh183cb712017-12-27 17:02:37 -0800876 logState.duration,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700877 newDuration);
878 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800879 }
linyuh183cb712017-12-27 17:02:37 -0800880 this.state = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800881 }
882
883 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800884 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800885 }
886
887 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800888 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800889 }
890
891 @Nullable
892 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800893 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800894 }
895
896 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800897 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800898 }
899
900 @Nullable
901 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800902 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800903 }
904
905 /** @return The child number for the call, or {@code null} if none specified. */
906 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800907 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800908 }
909
910 /** @return The last forwarded number for the call, or {@code null} if none specified. */
911 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800912 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800913 }
914
wangqif4ba3452018-01-09 11:26:29 -0800915 public boolean isCallForwarded() {
916 return isCallForwarded;
917 }
918
Eric Erfanianccca3152017-02-22 16:32:36 -0800919 /** @return The call subject, or {@code null} if none specified. */
920 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800921 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800922 }
923
924 /**
925 * @return {@code true} if the call's phone account supports call subjects, {@code false}
926 * otherwise.
927 */
928 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800929 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800930 }
931
932 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
933 public DisconnectCause getDisconnectCause() {
wangqibb94ca62018-04-27 14:34:04 -0700934 if (state == DialerCallState.DISCONNECTED || state == DialerCallState.IDLE) {
linyuh183cb712017-12-27 17:02:37 -0800935 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800936 }
937
938 return new DisconnectCause(DisconnectCause.UNKNOWN);
939 }
940
941 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800942 this.disconnectCause = disconnectCause;
943 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800944 }
945
946 /** Returns the possible text message responses. */
947 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800948 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800949 }
950
951 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
952 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800953 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800954
955 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
956 // We allow you to merge if the capabilities allow it or if it is a call with
957 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800958 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800959 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
960 // Cannot merge calls if there are no calls to merge with.
961 return false;
962 }
963 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
964 }
965 return (capabilities == (capabilities & supportedCapabilities));
966 }
967
968 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800969 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800970 }
971
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700972 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800973 public String getUniqueCallId() {
974 return uniqueCallId;
975 }
976
977 /** Gets the time when the call first became active. */
978 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800979 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800980 }
981
wangqied677ef2018-04-04 12:03:00 -0700982 /**
983 * Gets the time when the call is created (see {@link Details#getCreationTimeMillis()}). This is
984 * the same time that is logged as the start time in the Call Log (see {@link
985 * android.provider.CallLog.Calls#DATE}).
986 */
987 @TargetApi(26)
988 public long getCreationTimeMillis() {
989 return telecomCall.getDetails().getCreationTimeMillis();
990 }
991
Eric Erfanianccca3152017-02-22 16:32:36 -0800992 public boolean isConferenceCall() {
993 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
994 }
995
996 @Nullable
997 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -0800998 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -0800999 }
1000
1001 @Nullable
1002 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -08001003 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -08001004 }
1005
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001006 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -08001007 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -08001008 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -08001009 }
1010
1011 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -08001012 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -08001013 }
1014
1015 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -08001016 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -08001017 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -08001018 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001019 }
1020 return null;
1021 }
1022
1023 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -08001024 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -08001025 }
1026
1027 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -07001028 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001029 }
1030
wangqi8d407a02018-02-15 15:32:52 -08001031 @TargetApi(28)
wangqif6be6172018-03-30 15:57:56 -07001032 public boolean isActiveRttCall() {
wangqi219b8702018-02-13 09:34:41 -08001033 if (BuildCompat.isAtLeastP()) {
1034 return getTelecomCall().isRttActive();
1035 } else {
1036 return false;
1037 }
1038 }
1039
wangqi153af2f2018-02-15 16:21:49 -08001040 @TargetApi(28)
1041 public RttCall getRttCall() {
wangqif6be6172018-03-30 15:57:56 -07001042 if (!isActiveRttCall()) {
wangqi153af2f2018-02-15 16:21:49 -08001043 return null;
1044 }
1045 return getTelecomCall().getRttCall();
1046 }
1047
wangqif6be6172018-03-30 15:57:56 -07001048 @TargetApi(28)
1049 public boolean canUpgradeToRttCall() {
1050 PhoneAccount phoneAccount = getPhoneAccount();
1051 if (phoneAccount == null) {
1052 return false;
1053 }
1054 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
1055 return false;
1056 }
1057 if (isActiveRttCall()) {
1058 return false;
1059 }
1060 if (isVideoCall()) {
1061 return false;
1062 }
1063 if (isConferenceCall()) {
1064 return false;
1065 }
1066 if (CallList.getInstance().hasActiveRttCall()) {
1067 return false;
1068 }
1069 return true;
1070 }
1071
1072 @TargetApi(28)
1073 public void sendRttUpgradeRequest() {
1074 getTelecomCall().sendRttRequest();
1075 }
1076
wangqibc28ea72018-04-02 16:23:00 -07001077 @TargetApi(28)
1078 public void respondToRttRequest(boolean accept, int rttRequestId) {
wangqi81e373e2018-04-24 10:50:57 -07001079 Logger.get(context)
1080 .logCallImpression(
1081 accept
1082 ? DialerImpression.Type.RTT_MID_CALL_ACCEPTED
1083 : DialerImpression.Type.RTT_MID_CALL_REJECTED,
1084 getUniqueCallId(),
1085 getTimeAddedMs());
wangqibc28ea72018-04-02 16:23:00 -07001086 getTelecomCall().respondToRttRequest(rttRequestId, accept);
1087 }
1088
wangqibed9e2f2018-04-30 11:55:07 -07001089 @TargetApi(28)
1090 private void saveRttTranscript() {
1091 if (!BuildCompat.isAtLeastP()) {
1092 return;
1093 }
1094 // Save any remaining text in the buffer that's not shown by UI yet.
1095 // This may happen when the call is switched to background before disconnect.
1096 try {
1097 String messageLeft = getRttCall().readImmediately();
1098 if (!TextUtils.isEmpty(messageLeft)) {
1099 rttTranscript =
1100 RttChatMessage.getRttTranscriptWithNewRemoteMessage(rttTranscript, messageLeft);
1101 }
1102 } catch (IOException e) {
1103 LogUtil.e("DialerCall.saveRttTranscript", "error when reading remaining message", e);
1104 }
wangqi6e0d6392018-04-30 16:22:34 -07001105 // Don't save transcript if it's empty.
1106 if (rttTranscript.getMessagesCount() == 0) {
1107 return;
1108 }
wangqibed9e2f2018-04-30 11:55:07 -07001109 Futures.addCallback(
1110 RttTranscriptUtil.saveRttTranscript(context, rttTranscript),
1111 new DefaultFutureCallback<>(),
1112 MoreExecutors.directExecutor());
1113 }
1114
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001115 public boolean hasReceivedVideoUpgradeRequest() {
1116 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
1117 }
1118
1119 public boolean hasSentVideoUpgradeRequest() {
1120 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -08001121 }
1122
wangqi219b8702018-02-13 09:34:41 -08001123 public boolean hasSentRttUpgradeRequest() {
wangqi219b8702018-02-13 09:34:41 -08001124 return false;
1125 }
1126
Eric Erfanianccca3152017-02-22 16:32:36 -08001127 /**
1128 * Determines if the call handle is an emergency number or not and caches the result to avoid
1129 * repeated calls to isEmergencyNumber.
1130 */
1131 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -08001132 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -08001133 }
1134
Eric Erfanianccca3152017-02-22 16:32:36 -08001135 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -08001136 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -08001137 }
1138
1139 /**
1140 * Determines if the call is an external call.
1141 *
1142 * <p>An external call is one which does not exist locally for the {@link
1143 * android.telecom.ConnectionService} it is associated with.
1144 *
Eric Erfanianccca3152017-02-22 16:32:36 -08001145 * @return {@code true} if the call is an external call, {@code false} otherwise.
1146 */
linyuh437ae952018-03-26 12:46:18 -07001147 boolean isExternalCall() {
1148 return hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
Eric Erfanianccca3152017-02-22 16:32:36 -08001149 }
1150
1151 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001152 * Determines if answering this call will cause an ongoing video call to be dropped.
1153 *
1154 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
1155 * otherwise.
1156 */
1157 public boolean answeringDisconnectsForegroundVideoCall() {
1158 Bundle extras = getExtras();
1159 if (extras == null
1160 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
1161 return false;
1162 }
1163 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
1164 }
1165
1166 private void parseCallSpecificAppData() {
1167 if (isExternalCall()) {
1168 return;
1169 }
1170
linyuh183cb712017-12-27 17:02:37 -08001171 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
1172 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001173
linyuh183cb712017-12-27 17:02:37 -08001174 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -07001175 CallSpecificAppData.newBuilder()
1176 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
1177 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001178 }
wangqibb94ca62018-04-27 14:34:04 -07001179 if (getState() == DialerCallState.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -08001180 logState.callSpecificAppData =
1181 logState
Eric Erfanian8369df02017-05-03 10:27:13 -07001182 .callSpecificAppData
1183 .toBuilder()
1184 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
1185 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001186 }
1187 }
1188
1189 @Override
1190 public String toString() {
linyuh183cb712017-12-27 17:02:37 -08001191 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001192 // This should happen only in testing since otherwise we would never have a null
1193 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -08001194 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -08001195 }
1196
1197 return String.format(
1198 Locale.US,
1199 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001200 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001201 id,
wangqibb94ca62018-04-27 14:34:04 -07001202 DialerCallState.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001203 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1204 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1205 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001206 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001207 this.telecomCall.getConferenceableCalls(),
1208 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001209 getVideoTech().getSessionModificationState(),
1210 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001211 }
1212
1213 public String toSimpleString() {
1214 return super.toString();
1215 }
1216
1217 @CallHistoryStatus
1218 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001219 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001220 }
1221
1222 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001223 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001224 }
1225
1226 public boolean didShowCameraPermission() {
1227 return didShowCameraPermission;
1228 }
1229
1230 public void setDidShowCameraPermission(boolean didShow) {
1231 didShowCameraPermission = didShow;
1232 }
1233
wangqida410d32018-03-06 16:51:38 -08001234 public boolean didDismissVideoChargesAlertDialog() {
1235 return didDismissVideoChargesAlertDialog;
1236 }
1237
1238 public void setDidDismissVideoChargesAlertDialog(boolean didDismiss) {
1239 didDismissVideoChargesAlertDialog = didDismiss;
1240 }
1241
Eric Erfanian938468d2017-10-24 14:05:52 -07001242 @Nullable
1243 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001244 return isInGlobalSpamList;
1245 }
1246
1247 public void setIsInGlobalSpamList(boolean inSpamList) {
1248 isInGlobalSpamList = inSpamList;
1249 }
1250
Eric Erfanian938468d2017-10-24 14:05:52 -07001251 @Nullable
1252 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001253 return isInUserSpamList;
1254 }
1255
1256 public void setIsInUserSpamList(boolean inSpamList) {
1257 isInUserSpamList = inSpamList;
1258 }
1259
Eric Erfanian938468d2017-10-24 14:05:52 -07001260 @Nullable
1261 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001262 return isInUserWhiteList;
1263 }
1264
1265 public void setIsInUserWhiteList(boolean inWhiteList) {
1266 isInUserWhiteList = inWhiteList;
1267 }
1268
1269 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001270 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001271 }
1272
1273 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001274 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001275 }
1276
1277 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001278 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001279 }
1280
1281 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001282 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001283 }
1284
1285 public boolean isRemotelyHeld() {
1286 return isRemotelyHeld;
1287 }
1288
Eric Erfanian2ca43182017-08-31 06:57:16 -07001289 public boolean isMergeInProcess() {
1290 return isMergeInProcess;
1291 }
1292
Eric Erfanianccca3152017-02-22 16:32:36 -08001293 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001294 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001295 }
1296
erfanian2cf2c342017-12-21 12:01:33 -08001297 /**
1298 * Try and determine if the call used assisted dialing.
1299 *
1300 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1301 * implmentation is complete in P+.
1302 *
1303 * @return a boolean indicating assisted dialing may have been performed
1304 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001305 public boolean isAssistedDialed() {
1306 if (getIntentExtras() != null) {
erfaniand2e5d0b2018-03-02 14:54:35 -08001307 // P and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
erfanian2cf2c342017-12-21 12:01:33 -08001308 // was used. The Dialer client is responsible for performing assisted dialing before
1309 // placing the outgoing call.
1310 //
1311 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1312 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1313 && getAssistedDialingExtras() != null
1314 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1315 return true;
1316 }
1317 }
1318
Eric Erfanian2ca43182017-08-31 06:57:16 -07001319 return false;
1320 }
1321
erfanian2cf2c342017-12-21 12:01:33 -08001322 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001323 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001324 if (getIntentExtras() == null) {
1325 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001326 }
erfanian2cf2c342017-12-21 12:01:33 -08001327
1328 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1329 return null;
1330 }
1331
erfanianf2556612018-01-23 09:55:59 -08001332 // Used in N-OMR1
erfanian2cf2c342017-12-21 12:01:33 -08001333 return TransformationInfo.newInstanceFromBundle(
1334 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001335 }
1336
Eric Erfanianccca3152017-02-22 16:32:36 -08001337 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001338 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001339 }
1340
Eric Erfanian2ca43182017-08-31 06:57:16 -07001341 public int getAnswerAndReleaseButtonDisplayedTimes() {
1342 return answerAndReleaseButtonDisplayedTimes;
1343 }
1344
1345 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1346 answerAndReleaseButtonDisplayedTimes++;
1347 }
1348
1349 public boolean getReleasedByAnsweringSecondCall() {
1350 return releasedByAnsweringSecondCall;
1351 }
1352
1353 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1354 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1355 }
1356
1357 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1358 return secondCallWithoutAnswerAndReleasedButtonTimes;
1359 }
1360
1361 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1362 secondCallWithoutAnswerAndReleasedButtonTimes++;
1363 }
1364
Eric Erfanian8369df02017-05-03 10:27:13 -07001365 @Nullable
1366 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001367 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001368 }
1369
1370 public void setEnrichedCallCapabilities(
1371 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001372 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001373 }
1374
1375 @Nullable
1376 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001377 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001378 }
1379
1380 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001381 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001382 }
1383
Eric Erfanianccca3152017-02-22 16:32:36 -08001384 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001385 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001386 }
1387
Eric Erfanianccca3152017-02-22 16:32:36 -08001388 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1389 LogUtil.i(
1390 "DialerCall.phoneAccountSelected",
1391 "accountHandle: %s, setDefault: %b",
1392 accountHandle,
1393 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001394 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001395 }
1396
1397 public void disconnect() {
1398 LogUtil.i("DialerCall.disconnect", "");
wangqibb94ca62018-04-27 14:34:04 -07001399 setState(DialerCallState.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001400 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001401 listener.onDialerCallUpdate();
1402 }
linyuh183cb712017-12-27 17:02:37 -08001403 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001404 }
1405
1406 public void hold() {
1407 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001408 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001409 }
1410
1411 public void unhold() {
1412 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001413 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001414 }
1415
1416 public void splitFromConference() {
1417 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001418 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001419 }
1420
1421 public void answer(int videoState) {
1422 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001423 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001424 }
1425
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001426 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001427 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001428 }
1429
Eric Erfanianccca3152017-02-22 16:32:36 -08001430 public void reject(boolean rejectWithMessage, String message) {
1431 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001432 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001433 }
1434
1435 /** Return the string label to represent the call provider */
1436 public String getCallProviderLabel() {
1437 if (callProviderLabel == null) {
1438 PhoneAccount account = getPhoneAccount();
1439 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001440 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001441 callProviderLabel = account.getLabel().toString();
1442 }
1443 }
1444 if (callProviderLabel == null) {
1445 callProviderLabel = "";
1446 }
1447 }
1448 return callProviderLabel;
1449 }
1450
1451 private PhoneAccount getPhoneAccount() {
1452 PhoneAccountHandle accountHandle = getAccountHandle();
1453 if (accountHandle == null) {
1454 return null;
1455 }
linyuh183cb712017-12-27 17:02:37 -08001456 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001457 }
1458
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001459 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001460 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001461 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001462
1463 // Only store the first video tech type found to be available during the life of the call.
1464 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1465 // Update the video tech.
1466 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1467 }
1468 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001469 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001470 }
1471
Eric Erfanianccca3152017-02-22 16:32:36 -08001472 public String getCallbackNumber() {
1473 if (callbackNumber == null) {
1474 // Show the emergency callback number if either:
1475 // 1. This is an emergency call.
1476 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1477 // number.
1478 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1479
1480 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001481 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001482 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001483 }
1484
Eric Erfanianccca3152017-02-22 16:32:36 -08001485 if (callbackNumber == null) {
1486 callbackNumber = "";
1487 }
1488 }
1489 return callbackNumber;
1490 }
1491
wangqi97539352017-09-25 11:15:16 -07001492 public String getSimCountryIso() {
1493 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001494 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001495 .getSimCountryIso();
1496 if (!TextUtils.isEmpty(simCountryIso)) {
1497 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1498 }
1499 return simCountryIso;
1500 }
1501
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001502 @Override
1503 public void onVideoTechStateChanged() {
1504 update();
1505 }
1506
1507 @Override
1508 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001509 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001510 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001511 listener.onDialerCallSessionModificationStateChange();
1512 }
wangqi9982f0d2017-10-11 17:46:07 -07001513 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001514 }
1515
1516 @Override
1517 public void onCameraDimensionsChanged(int width, int height) {
1518 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1519 }
1520
1521 @Override
1522 public void onPeerDimensionsChanged(int width, int height) {
1523 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1524 }
1525
1526 @Override
1527 public void onVideoUpgradeRequestReceived() {
1528 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1529
linyuh183cb712017-12-27 17:02:37 -08001530 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001531 listener.onDialerCallUpgradeToVideo();
1532 }
1533
1534 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001535
linyuh183cb712017-12-27 17:02:37 -08001536 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001537 .logCallImpression(
1538 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1539 }
1540
1541 @Override
1542 public void onUpgradedToVideo(boolean switchToSpeaker) {
1543 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1544
1545 if (!switchToSpeaker) {
1546 return;
1547 }
1548
1549 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1550
1551 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1552 LogUtil.e(
1553 "DialerCall.onUpgradedToVideo",
1554 "toggling speakerphone not allowed when bluetooth supported.");
1555 return;
1556 }
1557
1558 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1559 return;
1560 }
1561
1562 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001563 }
1564
Eric Erfanian2ca43182017-08-31 06:57:16 -07001565 @Override
1566 public void onCapabilitiesUpdated() {
1567 if (getNumber() == null) {
1568 return;
1569 }
1570 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001571 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001572 if (capabilities != null) {
1573 setEnrichedCallCapabilities(capabilities);
1574 update();
1575 }
1576 }
1577
1578 @Override
1579 public void onEnrichedCallStateChanged() {
1580 updateEnrichedCallSession();
1581 }
1582
1583 @Override
1584 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001585 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001586 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1587 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001588 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001589 .logCallImpression(
1590 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1591 getUniqueCallId(),
1592 getTimeAddedMs());
1593 }
1594 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001595 }
1596
1597 private void updateEnrichedCallSession() {
1598 if (getNumber() == null) {
1599 return;
1600 }
1601 if (getEnrichedCallSession() != null) {
1602 // State changes to existing sessions are currently handled by the UI components (which have
1603 // their own listeners). Someday instead we could remove those and just call update() here and
1604 // have the usual onDialerCallUpdate update the UI.
1605 dispatchOnEnrichedCallSessionUpdate();
1606 return;
1607 }
1608
linyuh183cb712017-12-27 17:02:37 -08001609 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001610
1611 Filter filter =
1612 isIncoming()
1613 ? manager.createIncomingCallComposerFilter()
1614 : manager.createOutgoingCallComposerFilter();
1615
1616 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1617 if (session == null) {
1618 return;
1619 }
1620
1621 session.setUniqueDialerCallId(getUniqueCallId());
1622 setEnrichedCallSession(session);
1623
1624 LogUtil.i(
1625 "DialerCall.updateEnrichedCallSession",
1626 "setting session %d's dialer id to %s",
1627 session.getSessionId(),
1628 getUniqueCallId());
1629
1630 dispatchOnEnrichedCallSessionUpdate();
1631 }
1632
1633 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001634 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001635 listener.onEnrichedCallSessionUpdate();
1636 }
1637 }
1638
1639 void onRemovedFromCallList() {
wangqif1861052018-04-19 15:06:06 -07001640 LogUtil.enterBlock("DialerCall.onRemovedFromCallList");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001641 // Ensure we clean up when this call is removed.
wangqia0c49f62018-04-25 15:04:01 -07001642 if (videoTechManager != null) {
1643 videoTechManager.dispatchRemovedFromCallList();
1644 }
1645 // TODO(a bug): Add tests for it to make sure no crash on subsequent call to this method.
wangqibed9e2f2018-04-30 11:55:07 -07001646 // TODO(wangqi): Consider moving this to a DialerCallListener.
wangqia0c49f62018-04-25 15:04:01 -07001647 if (rttTranscript != null && !isCallRemoved) {
wangqibed9e2f2018-04-30 11:55:07 -07001648 saveRttTranscript();
wangqif1861052018-04-19 15:06:06 -07001649 }
wangqia0c49f62018-04-25 15:04:01 -07001650 isCallRemoved = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001651 }
1652
wangqi4d705e52017-09-28 12:23:35 -07001653 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1654 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001655 }
1656
Android Dialer974fc292018-02-01 16:12:25 -08001657 public void markFeedbackRequested() {
1658 feedbackRequested = true;
1659 }
1660
1661 public boolean isFeedbackRequested() {
1662 return feedbackRequested;
1663 }
1664
Eric Erfanianccca3152017-02-22 16:32:36 -08001665 /**
twyen73a74c32018-03-07 12:12:24 -08001666 * If the in call UI has shown the phone account selection dialog for the call, the {@link
1667 * PreferredAccountRecorder} to record the result from the dialog.
1668 */
1669 @Nullable
1670 public PreferredAccountRecorder getPreferredAccountRecorder() {
1671 return preferredAccountRecorder;
1672 }
1673
1674 public void setPreferredAccountRecorder(PreferredAccountRecorder preferredAccountRecorder) {
1675 this.preferredAccountRecorder = preferredAccountRecorder;
1676 }
1677
erfaniand05d8992018-03-20 19:42:26 -07001678 /** Indicates the call is eligible for SpeakEasy */
1679 public boolean isSpeakEasyEligible() {
erfaniandcc84cd2018-04-17 12:41:05 -07001680 if (!Constraints.isAvailable(context)) {
1681 return false;
1682 }
1683
1684 return !isPotentialEmergencyCallback()
1685 && !isEmergencyCall()
1686 && !isActiveRttCall()
1687 && !isConferenceCall()
1688 && !isVideoCall()
1689 && !isVoiceMailNumber();
erfaniand05d8992018-03-20 19:42:26 -07001690 }
1691
1692 /** Indicates the user has selected SpeakEasy */
1693 public boolean isSpeakEasyCall() {
1694 if (!isSpeakEasyEligible()) {
1695 return false;
1696 }
1697 return isSpeakEasyCall;
1698 }
1699
1700 /** Sets the user preference for SpeakEasy */
1701 public void setIsSpeakEasyCall(boolean isSpeakEasyCall) {
1702 this.isSpeakEasyCall = isSpeakEasyCall;
1703 }
1704
twyen73a74c32018-03-07 12:12:24 -08001705 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001706 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1707 * means there is no result.
1708 */
1709 @IntDef({
1710 CALL_HISTORY_STATUS_UNKNOWN,
1711 CALL_HISTORY_STATUS_PRESENT,
1712 CALL_HISTORY_STATUS_NOT_PRESENT
1713 })
1714 @Retention(RetentionPolicy.SOURCE)
1715 public @interface CallHistoryStatus {}
1716
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001717 /** Camera direction constants */
1718 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001719 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1720 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1721 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001722 }
1723
1724 /**
1725 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1726 * existing call member variables, but this duplication helps to ensure that none of these logging
1727 * variables will interface with/and affect call logic.
1728 */
1729 public static class LogState {
1730
1731 public DisconnectCause disconnectCause;
1732 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001733 public ContactLookupResult.Type contactLookupResult =
1734 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001735 public CallSpecificAppData callSpecificAppData;
1736 // If this was a conference call, the total number of calls involved in the conference.
1737 public int conferencedCalls = 0;
1738 public long duration = 0;
1739 public boolean isLogged = false;
1740
Eric Erfanian8369df02017-05-03 10:27:13 -07001741 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001742 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001743 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001744 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001745 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001746 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001747 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001748 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001749 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001750 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001751 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001752 return "Voicemail";
1753 default:
1754 return "Not found";
1755 }
1756 }
1757
1758 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1759 if (callSpecificAppData == null) {
1760 return "null";
1761 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001762 switch (callSpecificAppData.getCallInitiationType()) {
1763 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001764 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001765 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001766 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001767 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001768 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001769 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001770 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001771 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001772 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001773 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001774 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001775 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001776 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001777 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001778 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001779 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001780 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001781 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001782 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001783 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001784 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001785 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001786 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001787 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001788 return "Launcher Shortcut";
1789 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001790 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001791 }
1792 }
1793
1794 @Override
1795 public String toString() {
1796 return String.format(
1797 Locale.US,
1798 "["
1799 + "%s, " // DisconnectCause toString already describes the object type
1800 + "isIncoming: %s, "
1801 + "contactLookup: %s, "
1802 + "callInitiation: %s, "
1803 + "duration: %s"
1804 + "]",
1805 disconnectCause,
1806 isIncoming,
1807 lookupToString(contactLookupResult),
1808 initiationToString(callSpecificAppData),
1809 duration);
1810 }
1811 }
1812
roldenburgd7490db2018-01-09 13:51:29 -08001813 /** Coordinates the available VideoTech implementations for a call. */
1814 @VisibleForTesting
1815 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001816 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001817 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001818 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001819 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001820 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001821
roldenburgd7490db2018-01-09 13:51:29 -08001822 @VisibleForTesting
1823 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001824 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001825
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001826 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001827 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001828 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001829
1830 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001831 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001832
linyuh183cb712017-12-27 17:02:37 -08001833 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001834
roldenburgd7490db2018-01-09 13:51:29 -08001835 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001836 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001837 .getRcsVideoShareFactory()
1838 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001839 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001840 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001841 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001842 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001843
1844 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001845 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001846 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001847
1848 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001849 }
1850
roldenburgd7490db2018-01-09 13:51:29 -08001851 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001852 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001853 if (savedTech == emptyVideoTech) {
1854 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001855 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001856 savedTech = tech;
1857 savedTech.becomePrimary();
1858 break;
1859 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001860 }
roldenburg6bd612f2018-01-18 12:57:19 -08001861 } else if (savedTech instanceof DuoVideoTech
1862 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001863 // RCS Video Share will become available after the capability exchange which is slower than
1864 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1865 // not in the middle of any session changes, let it take over.
1866 savedTech = rcsVideoShare;
1867 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001868 }
1869
roldenburgd7490db2018-01-09 13:51:29 -08001870 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001871 }
1872
roldenburgd7490db2018-01-09 13:51:29 -08001873 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001874 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001875 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001876 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001877 }
1878 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001879
1880 void dispatchRemovedFromCallList() {
1881 for (VideoTech videoTech : videoTechs) {
1882 videoTech.onRemovedFromCallList();
1883 }
1884 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001885 }
1886
Eric Erfanianccca3152017-02-22 16:32:36 -08001887 /** Called when canned text responses have been loaded. */
1888 public interface CannedTextResponsesLoadedListener {
1889 void onCannedTextResponsesLoaded(DialerCall call);
1890 }
1891}