blob: 1a0de1960a477e45c023554b0160ec00962682be [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;
maxwelbd95d0f62018-05-01 17:50:55 -070030import android.os.SystemClock;
Eric Erfanianccca3152017-02-22 16:32:36 -080031import android.os.Trace;
32import android.support.annotation.IntDef;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070033import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080034import android.support.annotation.Nullable;
twyendde01c52017-09-22 10:07:31 -070035import android.support.annotation.VisibleForTesting;
wangqif4ba3452018-01-09 11:26:29 -080036import android.support.v4.os.BuildCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080037import android.telecom.Call;
38import android.telecom.Call.Details;
wangqi219b8702018-02-13 09:34:41 -080039import android.telecom.Call.RttCall;
Eric Erfanian8369df02017-05-03 10:27:13 -070040import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080041import android.telecom.Connection;
42import android.telecom.DisconnectCause;
43import android.telecom.GatewayInfo;
44import android.telecom.InCallService.VideoCall;
45import android.telecom.PhoneAccount;
46import android.telecom.PhoneAccountHandle;
47import android.telecom.StatusHints;
48import android.telecom.TelecomManager;
49import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080050import android.text.TextUtils;
roldenburg08424ee2018-03-28 17:02:46 -070051import android.widget.Toast;
Eric Erfanianccca3152017-02-22 16:32:36 -080052import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080053import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
erfanian2cf2c342017-12-21 12:01:33 -080054import com.android.dialer.assisteddialing.ConcreteCreator;
erfaniand0f207f2017-10-11 12:23:29 -070055import com.android.dialer.assisteddialing.TransformationInfo;
Eric Erfanian8369df02017-05-03 10:27:13 -070056import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080057import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070058import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080059import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080060import com.android.dialer.common.LogUtil;
wangqid6b10d52018-04-12 14:44:06 -070061import com.android.dialer.common.concurrent.DefaultFutureCallback;
Eric Erfanian2ca43182017-08-31 06:57:16 -070062import com.android.dialer.compat.telephony.TelephonyManagerCompat;
63import com.android.dialer.configprovider.ConfigProviderBindings;
roldenburg4f026392017-10-13 18:42:20 -070064import com.android.dialer.duo.DuoComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070065import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070066import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070067import com.android.dialer.enrichedcall.EnrichedCallManager;
68import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
69import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
70import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070071import com.android.dialer.enrichedcall.Session;
wangqi9982f0d2017-10-11 17:46:07 -070072import com.android.dialer.location.GeoUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070073import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070074import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070075import com.android.dialer.logging.DialerImpression;
76import com.android.dialer.logging.Logger;
twyen73a74c32018-03-07 12:12:24 -080077import com.android.dialer.preferredsim.PreferredAccountRecorder;
wangqied677ef2018-04-04 12:03:00 -070078import com.android.dialer.rtt.RttTranscript;
wangqid6b10d52018-04-12 14:44:06 -070079import com.android.dialer.rtt.RttTranscriptUtil;
twyena4745bd2017-12-12 18:40:11 -080080import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070081import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070082import com.android.dialer.theme.R;
maxwelbd95d0f62018-05-01 17:50:55 -070083import com.android.dialer.time.Clock;
wangqi9982f0d2017-10-11 17:46:07 -070084import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070085import com.android.incallui.audiomode.AudioModeProvider;
wangqibb94ca62018-04-27 14:34:04 -070086import com.android.incallui.call.state.DialerCallState;
Eric Erfanianccca3152017-02-22 16:32:36 -080087import com.android.incallui.latencyreport.LatencyReport;
wangqibed9e2f2018-04-30 11:55:07 -070088import com.android.incallui.rtt.protocol.RttChatMessage;
erfaniandcc84cd2018-04-17 12:41:05 -070089import com.android.incallui.speakeasy.runtime.Constraints;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070090import com.android.incallui.videotech.VideoTech;
91import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070092import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070093import com.android.incallui.videotech.empty.EmptyVideoTech;
94import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070095import com.android.incallui.videotech.utils.VideoUtils;
wangqid6b10d52018-04-12 14:44:06 -070096import com.google.common.util.concurrent.Futures;
wangqid6b10d52018-04-12 14:44:06 -070097import com.google.common.util.concurrent.MoreExecutors;
wangqibed9e2f2018-04-30 11:55:07 -070098import java.io.IOException;
Eric Erfanianccca3152017-02-22 16:32:36 -080099import java.lang.annotation.Retention;
100import java.lang.annotation.RetentionPolicy;
101import java.util.ArrayList;
102import java.util.List;
103import java.util.Locale;
104import java.util.Objects;
105import java.util.UUID;
106import java.util.concurrent.CopyOnWriteArrayList;
107import java.util.concurrent.TimeUnit;
108
109/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -0700110public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -0800111
112 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
113 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
114 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700115
116 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -0700117 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700118 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
119
Eric Erfanianccca3152017-02-22 16:32:36 -0800120 private static final String ID_PREFIX = "DialerCall_";
121 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
122 "emergency_callback_window_millis";
linyuh183cb712017-12-27 17:02:37 -0800123 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800124
125 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700126 * A counter used to append to restricted/private/hidden calls so that users can identify them in
127 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
128 * are no live calls.
129 */
linyuh183cb712017-12-27 17:02:37 -0800130 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700131
132 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800133 * The unique call ID for every call. This will help us to identify each call and allow us the
134 * ability to stitch impressions to calls if needed.
135 */
136 private final String uniqueCallId = UUID.randomUUID().toString();
137
linyuh183cb712017-12-27 17:02:37 -0800138 private final Call telecomCall;
139 private final LatencyReport latencyReport;
140 private final String id;
141 private final int hiddenId;
142 private final List<String> childCallIds = new ArrayList<>();
143 private final LogState logState = new LogState();
144 private final Context context;
145 private final DialerCallDelegate dialerCallDelegate;
146 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
147 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800148 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800149 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800150
erfaniand05d8992018-03-20 19:42:26 -0700151 private boolean isSpeakEasyCall;
linyuh183cb712017-12-27 17:02:37 -0800152 private boolean isEmergencyCall;
153 private Uri handle;
wangqibb94ca62018-04-27 14:34:04 -0700154 private int state = DialerCallState.INVALID;
linyuh183cb712017-12-27 17:02:37 -0800155 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800156
roldenburg08424ee2018-03-28 17:02:46 -0700157 private boolean hasShownLteToWiFiHandoverToast;
Eric Erfanianccca3152017-02-22 16:32:36 -0800158 private boolean hasShownWiFiToLteHandoverToast;
159 private boolean doNotShowDialogForHandoffToWifiFailure;
160
linyuh183cb712017-12-27 17:02:37 -0800161 private String childNumber;
162 private String lastForwardedNumber;
wangqif4ba3452018-01-09 11:26:29 -0800163 private boolean isCallForwarded;
linyuh183cb712017-12-27 17:02:37 -0800164 private String callSubject;
twyen27c37182018-04-24 11:48:12 -0700165 @Nullable private PhoneAccountHandle phoneAccountHandle;
linyuh183cb712017-12-27 17:02:37 -0800166 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
167 private boolean isSpam;
168 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700169
170 @Nullable private Boolean isInUserSpamList;
171
172 @Nullable private Boolean isInUserWhiteList;
173
174 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800175 private boolean didShowCameraPermission;
wangqida410d32018-03-06 16:51:38 -0800176 private boolean didDismissVideoChargesAlertDialog;
177 private PersistableBundle carrierConfig;
Eric Erfanianccca3152017-02-22 16:32:36 -0800178 private String callProviderLabel;
179 private String callbackNumber;
linyuh183cb712017-12-27 17:02:37 -0800180 private int cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
181 private EnrichedCallCapabilities enrichedCallCapabilities;
182 private Session enrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800183
Eric Erfanian2ca43182017-08-31 06:57:16 -0700184 private int answerAndReleaseButtonDisplayedTimes = 0;
185 private boolean releasedByAnsweringSecondCall = false;
186 // Times when a second call is received but AnswerAndRelease button is not shown
187 // since it's not supported.
188 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700189 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700190
wangqi4d705e52017-09-28 12:23:35 -0700191 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
192 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700193 private boolean isVoicemailNumber;
194 private List<PhoneAccountHandle> callCapableAccounts;
195 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700196
Android Dialer974fc292018-02-01 16:12:25 -0800197 private volatile boolean feedbackRequested = false;
198
maxwelbd95d0f62018-05-01 17:50:55 -0700199 private Clock clock = System::currentTimeMillis;
200
twyen73a74c32018-03-07 12:12:24 -0800201 @Nullable private PreferredAccountRecorder preferredAccountRecorder;
wangqia0c49f62018-04-25 15:04:01 -0700202 private boolean isCallRemoved;
twyen73a74c32018-03-07 12:12:24 -0800203
Eric Erfanianccca3152017-02-22 16:32:36 -0800204 public static String getNumberFromHandle(Uri handle) {
205 return handle == null ? "" : handle.getSchemeSpecificPart();
206 }
207
208 /**
209 * Whether the call is put on hold by remote party. This is different than the {@link
wangqibb94ca62018-04-27 14:34:04 -0700210 * DialerCallState#ONHOLD} state which indicates that the call is being held locally on the
211 * device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800212 */
213 private boolean isRemotelyHeld;
214
Eric Erfanian2ca43182017-08-31 06:57:16 -0700215 /** Indicates whether this call is currently in the process of being merged into a conference. */
216 private boolean isMergeInProcess;
217
Eric Erfanianccca3152017-02-22 16:32:36 -0800218 /**
219 * Indicates whether the phone account associated with this call supports specifying a call
220 * subject.
221 */
linyuh183cb712017-12-27 17:02:37 -0800222 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800223
wangqied677ef2018-04-04 12:03:00 -0700224 public RttTranscript getRttTranscript() {
225 return rttTranscript;
226 }
227
228 public void setRttTranscript(RttTranscript rttTranscript) {
229 this.rttTranscript = rttTranscript;
230 }
231
232 private RttTranscript rttTranscript;
233
linyuh183cb712017-12-27 17:02:37 -0800234 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800235 new Call.Callback() {
236 @Override
237 public void onStateChanged(Call call, int newState) {
238 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
239 update();
240 }
241
242 @Override
243 public void onParentChanged(Call call, Call newParent) {
244 LogUtil.v(
245 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
246 update();
247 }
248
249 @Override
250 public void onChildrenChanged(Call call, List<Call> children) {
251 update();
252 }
253
254 @Override
255 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700256 LogUtil.v(
257 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800258 update();
259 }
260
261 @Override
262 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
263 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700264 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800265 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800266 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800267 listener.onCannedTextResponsesLoaded(DialerCall.this);
268 }
269 }
270
271 @Override
272 public void onPostDialWait(Call call, String remainingPostDialSequence) {
273 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700274 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800275 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
276 update();
277 }
278
279 @Override
280 public void onVideoCallChanged(Call call, VideoCall videoCall) {
281 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700282 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800283 update();
284 }
285
286 @Override
287 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700288 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700289 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800290 }
291
292 @Override
293 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
294 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700295 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800296 "call %s, conferenceable calls: %d",
297 call,
298 conferenceableCalls.size());
299 update();
300 }
301
302 @Override
wangqi219b8702018-02-13 09:34:41 -0800303 public void onRttModeChanged(Call call, int mode) {
304 LogUtil.v("TelecomCallCallback.onRttModeChanged", "mode=%d", mode);
305 }
306
307 @Override
308 public void onRttRequest(Call call, int id) {
309 LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id);
wangqibc28ea72018-04-02 16:23:00 -0700310 for (DialerCallListener listener : listeners) {
311 listener.onDialerCallUpgradeToRtt(id);
312 }
wangqi219b8702018-02-13 09:34:41 -0800313 }
314
315 @Override
316 public void onRttInitiationFailure(Call call, int reason) {
317 LogUtil.v("TelecomCallCallback.onRttInitiationFailure", "reason=%d", reason);
318 update();
319 }
320
321 @Override
322 public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {
323 LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled);
wangqi81e373e2018-04-24 10:50:57 -0700324 if (enabled) {
325 Logger.get(context)
326 .logCallImpression(
327 DialerImpression.Type.RTT_MID_CALL_ENABLED,
328 getUniqueCallId(),
329 getTimeAddedMs());
330 }
wangqi219b8702018-02-13 09:34:41 -0800331 update();
332 }
333
334 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800335 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
336 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700337 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800338 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
339 switch (event) {
340 // The Previous attempt to Merge two calls together has failed in Telecom. We must
341 // now update the UI to possibly re-enable the Merge button based on the number of
342 // currently conferenceable calls available or Connection Capabilities.
343 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
344 update();
345 break;
346 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
347 notifyWiFiToLteHandover();
348 break;
roldenburg08424ee2018-03-28 17:02:46 -0700349 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI:
350 onLteToWifiHandover();
351 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800352 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
353 notifyHandoverToWifiFailed();
354 break;
355 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
356 isRemotelyHeld = true;
357 update();
358 break;
359 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
360 isRemotelyHeld = false;
361 update();
362 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700363 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
364 notifyInternationalCallOnWifi();
365 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700366 case TelephonyManagerCompat.EVENT_MERGE_START:
367 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
368 isMergeInProcess = true;
369 break;
370 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
371 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
372 isMergeInProcess = false;
373 break;
wangqif4ba3452018-01-09 11:26:29 -0800374 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
375 // Only handle this event for P+ since it's unreliable pre-P.
376 if (BuildCompat.isAtLeastP()) {
377 isCallForwarded = true;
378 update();
379 }
380 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800381 default:
382 break;
383 }
384 }
385 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700386
linyuh183cb712017-12-27 17:02:37 -0800387 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800388
389 public DialerCall(
390 Context context,
391 DialerCallDelegate dialerCallDelegate,
392 Call telecomCall,
393 LatencyReport latencyReport,
394 boolean registerCallback) {
395 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800396 this.context = context;
397 this.dialerCallDelegate = dialerCallDelegate;
398 this.telecomCall = telecomCall;
399 this.latencyReport = latencyReport;
400 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800401
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700402 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800403 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700404
405 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700406 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800407 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700408 } else {
linyuh183cb712017-12-27 17:02:37 -0800409 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700410 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800411
412 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800413 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800414 }
415
linyuh183cb712017-12-27 17:02:37 -0800416 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800417 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700418
419 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800420 }
421
422 private static int translateState(int state) {
423 switch (state) {
424 case Call.STATE_NEW:
425 case Call.STATE_CONNECTING:
wangqibb94ca62018-04-27 14:34:04 -0700426 return DialerCallState.CONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800427 case Call.STATE_SELECT_PHONE_ACCOUNT:
wangqibb94ca62018-04-27 14:34:04 -0700428 return DialerCallState.SELECT_PHONE_ACCOUNT;
Eric Erfanianccca3152017-02-22 16:32:36 -0800429 case Call.STATE_DIALING:
wangqibb94ca62018-04-27 14:34:04 -0700430 return DialerCallState.DIALING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800431 case Call.STATE_PULLING_CALL:
wangqibb94ca62018-04-27 14:34:04 -0700432 return DialerCallState.PULLING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800433 case Call.STATE_RINGING:
wangqibb94ca62018-04-27 14:34:04 -0700434 return DialerCallState.INCOMING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800435 case Call.STATE_ACTIVE:
wangqibb94ca62018-04-27 14:34:04 -0700436 return DialerCallState.ACTIVE;
Eric Erfanianccca3152017-02-22 16:32:36 -0800437 case Call.STATE_HOLDING:
wangqibb94ca62018-04-27 14:34:04 -0700438 return DialerCallState.ONHOLD;
Eric Erfanianccca3152017-02-22 16:32:36 -0800439 case Call.STATE_DISCONNECTED:
wangqibb94ca62018-04-27 14:34:04 -0700440 return DialerCallState.DISCONNECTED;
Eric Erfanianccca3152017-02-22 16:32:36 -0800441 case Call.STATE_DISCONNECTING:
wangqibb94ca62018-04-27 14:34:04 -0700442 return DialerCallState.DISCONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800443 default:
wangqibb94ca62018-04-27 14:34:04 -0700444 return DialerCallState.INVALID;
Eric Erfanianccca3152017-02-22 16:32:36 -0800445 }
446 }
447
448 public static boolean areSame(DialerCall call1, DialerCall call2) {
449 if (call1 == null && call2 == null) {
450 return true;
451 } else if (call1 == null || call2 == null) {
452 return false;
453 }
454
455 // otherwise compare call Ids
456 return call1.getId().equals(call2.getId());
457 }
458
Eric Erfanianccca3152017-02-22 16:32:36 -0800459 public void addListener(DialerCallListener listener) {
460 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800461 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800462 }
463
464 public void removeListener(DialerCallListener listener) {
465 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800466 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800467 }
468
469 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
470 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800471 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800472 }
473
474 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
475 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800476 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800477 }
478
roldenburg08424ee2018-03-28 17:02:46 -0700479 private void onLteToWifiHandover() {
480 LogUtil.enterBlock("DialerCall.onLteToWifiHandover");
481 if (hasShownLteToWiFiHandoverToast) {
482 return;
483 }
484
485 Toast.makeText(context, R.string.video_call_lte_to_wifi_handover_toast, Toast.LENGTH_LONG)
486 .show();
487 hasShownLteToWiFiHandoverToast = true;
488 }
489
Eric Erfanianccca3152017-02-22 16:32:36 -0800490 public void notifyWiFiToLteHandover() {
491 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800492 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800493 listener.onWiFiToLteHandover();
494 }
495 }
496
497 public void notifyHandoverToWifiFailed() {
498 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800499 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800500 listener.onHandoverToWifiFailure();
501 }
502 }
503
Eric Erfanianc857f902017-05-15 14:05:33 -0700504 public void notifyInternationalCallOnWifi() {
505 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800506 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700507 dialerCallListener.onInternationalCallOnWifi();
508 }
509 }
510
Eric Erfanianccca3152017-02-22 16:32:36 -0800511 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800512 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800513 }
wangqida410d32018-03-06 16:51:38 -0800514
Eric Erfanianccca3152017-02-22 16:32:36 -0800515 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800516 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800517 }
518
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700519 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800520 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700521 }
522
523 public void setCameraDir(int cameraDir) {
524 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
525 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800526 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700527 } else {
linyuh183cb712017-12-27 17:02:37 -0800528 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700529 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800530 }
531
Eric Erfanian2ca43182017-08-31 06:57:16 -0700532 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800533 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700534 }
535
wangqi9982f0d2017-10-11 17:46:07 -0700536 public boolean isVoiceMailNumber() {
537 return isVoicemailNumber;
538 }
539
540 public List<PhoneAccountHandle> getCallCapableAccounts() {
541 return callCapableAccounts;
542 }
543
544 public String getCountryIso() {
545 return countryIso;
546 }
547
548 private void updateIsVoiceMailNumber() {
549 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
550 isVoicemailNumber = true;
roldenburg37a969d2018-02-22 14:46:44 -0800551 return;
wangqi9982f0d2017-10-11 17:46:07 -0700552 }
553
linyuh183cb712017-12-27 17:02:37 -0800554 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700555 isVoicemailNumber = false;
roldenburg37a969d2018-02-22 14:46:44 -0800556 return;
wangqi9982f0d2017-10-11 17:46:07 -0700557 }
558
linyuh183cb712017-12-27 17:02:37 -0800559 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700560 }
561
Eric Erfanianccca3152017-02-22 16:32:36 -0800562 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700563 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800564 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700565 // Clear any cache here that could potentially change on update.
566 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800567 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700568 updateFromTelecomCall();
wangqibb94ca62018-04-27 14:34:04 -0700569 if (oldState != getState() && getState() == DialerCallState.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800570 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800571 listener.onDialerCallDisconnect();
572 }
linyuh183cb712017-12-27 17:02:37 -0800573 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700574 .getEnrichedCallManager()
575 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800576 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700577 .getEnrichedCallManager()
578 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800579 } else {
linyuh183cb712017-12-27 17:02:37 -0800580 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800581 listener.onDialerCallUpdate();
582 }
583 }
584 Trace.endSection();
585 }
586
wangqi9982f0d2017-10-11 17:46:07 -0700587 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700588 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700589 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800590 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700591
roldenburg6bd612f2018-01-18 12:57:19 -0800592 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700593
linyuh183cb712017-12-27 17:02:37 -0800594 final int translatedState = translateState(telecomCall.getState());
wangqibb94ca62018-04-27 14:34:04 -0700595 if (state != DialerCallState.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800596 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800597 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800598 }
599
linyuh183cb712017-12-27 17:02:37 -0800600 childCallIds.clear();
601 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800602 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800603 childCallIds.add(
604 dialerCallDelegate
605 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800606 .getId());
607 }
608
609 // The number of conferenced calls can change over the course of the call, so use the
610 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800611 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800612
linyuh183cb712017-12-27 17:02:37 -0800613 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800614
615 // If the handle of the call has changed, update state for the call determining if it is an
616 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800617 Uri newHandle = telecomCall.getDetails().getHandle();
618 if (!Objects.equals(handle, newHandle)) {
619 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800620 updateEmergencyCallState();
621 }
622
linyuh183cb712017-12-27 17:02:37 -0800623 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800624 // If the phone account handle of the call is set, cache capability bit indicating whether
625 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800626 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
627 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
628 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800629
linyuh183cb712017-12-27 17:02:37 -0800630 if (phoneAccountHandle != null) {
631 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800632 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800633 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800634 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
wangqida410d32018-03-06 16:51:38 -0800635 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
636 cacheCarrierConfiguration(phoneAccountHandle);
637 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800638 }
639 }
640 }
linyuh183cb712017-12-27 17:02:37 -0800641 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700642 updateIsVoiceMailNumber();
643 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800644 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700645 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700646 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800647 }
648
649 /**
wangqida410d32018-03-06 16:51:38 -0800650 * Caches frequently used carrier configuration locally.
651 *
652 * @param accountHandle The PhoneAccount handle.
653 */
654 @SuppressLint("MissingPermission")
655 private void cacheCarrierConfiguration(PhoneAccountHandle accountHandle) {
656 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
657 return;
658 }
659 if (VERSION.SDK_INT < VERSION_CODES.O) {
660 return;
661 }
662 // TODO(a bug): This may take several seconds to complete, revisit it to move it to worker
663 // thread.
664 carrierConfig =
665 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, accountHandle)
666 .getCarrierConfig();
667 }
668
669 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800670 * Tests corruption of the {@code callExtras} bundle by calling {@link
671 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
672 * be thrown and caught by this function.
673 *
674 * @param callExtras the bundle to verify
675 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
676 */
677 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
678 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700679 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800680 * bundle, resulting in a IllegalArgumentException while validating data under {@link
681 * Bundle#containsKey(String)}.
682 */
683 try {
684 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
685 return false;
686 } catch (IllegalArgumentException e) {
687 LogUtil.e(
688 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
689 return true;
690 }
691 }
692
693 protected void updateFromCallExtras(Bundle callExtras) {
694 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
695 /**
696 * If the bundle is corrupted, abandon information update as a work around. These are not
697 * critical for the dialer to function.
698 */
699 return;
700 }
701 // Check for a change in the child address and notify any listeners.
702 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
703 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800704 if (!Objects.equals(childNumber, this.childNumber)) {
705 this.childNumber = childNumber;
706 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800707 listener.onDialerCallChildNumberChange();
708 }
709 }
710 }
711
712 // Last forwarded number comes in as an array of strings. We want to choose the
713 // last item in the array. The forwarding numbers arrive independently of when the
714 // call is originally set up, so we need to notify the the UI of the change.
715 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
716 ArrayList<String> lastForwardedNumbers =
717 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
718
719 if (lastForwardedNumbers != null) {
720 String lastForwardedNumber = null;
721 if (!lastForwardedNumbers.isEmpty()) {
722 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
723 }
724
linyuh183cb712017-12-27 17:02:37 -0800725 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
726 this.lastForwardedNumber = lastForwardedNumber;
727 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800728 listener.onDialerCallLastForwardedNumberChange();
729 }
730 }
731 }
732 }
733
734 // DialerCall subject is present in the extras at the start of call, so we do not need to
735 // notify any other listeners of this.
736 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
737 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800738 if (!Objects.equals(this.callSubject, callSubject)) {
739 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800740 }
741 }
742 }
743
Eric Erfanianccca3152017-02-22 16:32:36 -0800744 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800745 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800746 }
747
Eric Erfanianc857f902017-05-15 14:05:33 -0700748 /**
749 * @return name appended with a number if the number is restricted/unknown and the user has
750 * received more than one restricted/unknown call.
751 */
752 @Nullable
753 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800754 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
755 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700756 }
757 return name;
758 }
759
760 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800761 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700762 }
763
764 private boolean isHiddenNumber() {
765 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
766 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
767 }
768
Eric Erfanianccca3152017-02-22 16:32:36 -0800769 public boolean hasShownWiFiToLteHandoverToast() {
770 return hasShownWiFiToLteHandoverToast;
771 }
772
773 public void setHasShownWiFiToLteHandoverToast() {
774 hasShownWiFiToLteHandoverToast = true;
775 }
776
777 public boolean showWifiHandoverAlertAsToast() {
778 return doNotShowDialogForHandoffToWifiFailure;
779 }
780
781 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
782 doNotShowDialogForHandoffToWifiFailure = bool;
783 }
784
wangqida410d32018-03-06 16:51:38 -0800785 public boolean showVideoChargesAlertDialog() {
786 if (carrierConfig == null) {
787 return false;
788 }
789 return carrierConfig.getBoolean(
790 TelephonyManagerCompat.CARRIER_CONFIG_KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL);
791 }
792
Eric Erfanianccca3152017-02-22 16:32:36 -0800793 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800794 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800795 }
796
797 @Nullable
798 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800799 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800800 }
801
802 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800803 telecomCall.reject(false, null);
wangqibb94ca62018-04-27 14:34:04 -0700804 setState(DialerCallState.BLOCKED);
Eric Erfanianccca3152017-02-22 16:32:36 -0800805 }
806
807 @Nullable
808 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800809 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800810 }
811
812 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800813 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800814 }
815
816 public boolean isPotentialEmergencyCallback() {
817 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
818 // is actually in emergency callback mode (ie data is disabled).
819 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
820 return true;
821 }
822 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
823 // as a potential emergency callback.
824 if (getExtras() != null
825 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
826 > 0) {
827 long lastEmergencyCallMillis =
828 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
829 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
830 return true;
831 }
832 }
833 return false;
834 }
835
836 boolean isInEmergencyCallbackWindow(long timestampMillis) {
837 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800838 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800839 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
840 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
841 }
842
843 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800844 if (telecomCall != null && telecomCall.getParent() != null) {
wangqibb94ca62018-04-27 14:34:04 -0700845 return DialerCallState.CONFERENCED;
Eric Erfanianccca3152017-02-22 16:32:36 -0800846 } else {
linyuh183cb712017-12-27 17:02:37 -0800847 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800848 }
849 }
850
yueg265089a2017-10-06 14:35:15 -0700851 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800852 return state;
yueg265089a2017-10-06 14:35:15 -0700853 }
854
Eric Erfanianccca3152017-02-22 16:32:36 -0800855 public void setState(int state) {
wangqibb94ca62018-04-27 14:34:04 -0700856 if (state == DialerCallState.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800857 logState.isIncoming = true;
maxwelbd95d0f62018-05-01 17:50:55 -0700858 }
859 updateCallTiming(state);
860
861 this.state = state;
862 }
863
864 private void updateCallTiming(int newState) {
865 if (newState == DialerCallState.ACTIVE) {
866 if (this.state == DialerCallState.ACTIVE) {
867 LogUtil.i("DialerCall.updateCallTiming", "state is already active");
868 return;
869 }
870 logState.dialerConnectTimeMillis = clock.currentTimeMillis();
871 logState.dialerConnectTimeMillisElapsedRealtime = SystemClock.elapsedRealtime();
872 }
873
874 if (newState == DialerCallState.DISCONNECTED) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700875 long newDuration =
maxwelbd95d0f62018-05-01 17:50:55 -0700876 getConnectTimeMillis() == 0 ? 0 : clock.currentTimeMillis() - getConnectTimeMillis();
877 if (this.state == DialerCallState.DISCONNECTED) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700878 LogUtil.i(
879 "DialerCall.setState",
880 "ignoring state transition from DISCONNECTED to DISCONNECTED."
881 + " Duration would have changed from %s to %s",
maxwelbd95d0f62018-05-01 17:50:55 -0700882 logState.telecomDurationMillis,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700883 newDuration);
maxwelbd95d0f62018-05-01 17:50:55 -0700884 return;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700885 }
maxwelbd95d0f62018-05-01 17:50:55 -0700886 logState.telecomDurationMillis = newDuration;
887 logState.dialerDurationMillis =
888 logState.dialerConnectTimeMillis == 0
889 ? 0
890 : clock.currentTimeMillis() - logState.dialerConnectTimeMillis;
891 logState.dialerDurationMillisElapsedRealtime =
892 logState.dialerConnectTimeMillisElapsedRealtime == 0
893 ? 0
894 : SystemClock.elapsedRealtime() - logState.dialerConnectTimeMillisElapsedRealtime;
Eric Erfanianccca3152017-02-22 16:32:36 -0800895 }
maxwelbd95d0f62018-05-01 17:50:55 -0700896 }
897
898 @VisibleForTesting
899 void setClock(Clock clock) {
900 this.clock = clock;
Eric Erfanianccca3152017-02-22 16:32:36 -0800901 }
902
903 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800904 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800905 }
906
907 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800908 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800909 }
910
911 @Nullable
912 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800913 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800914 }
915
916 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800917 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800918 }
919
920 @Nullable
921 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800922 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800923 }
924
925 /** @return The child number for the call, or {@code null} if none specified. */
926 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800927 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800928 }
929
930 /** @return The last forwarded number for the call, or {@code null} if none specified. */
931 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800932 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800933 }
934
wangqif4ba3452018-01-09 11:26:29 -0800935 public boolean isCallForwarded() {
936 return isCallForwarded;
937 }
938
Eric Erfanianccca3152017-02-22 16:32:36 -0800939 /** @return The call subject, or {@code null} if none specified. */
940 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800941 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800942 }
943
944 /**
945 * @return {@code true} if the call's phone account supports call subjects, {@code false}
946 * otherwise.
947 */
948 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800949 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800950 }
951
952 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
953 public DisconnectCause getDisconnectCause() {
wangqibb94ca62018-04-27 14:34:04 -0700954 if (state == DialerCallState.DISCONNECTED || state == DialerCallState.IDLE) {
linyuh183cb712017-12-27 17:02:37 -0800955 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800956 }
957
958 return new DisconnectCause(DisconnectCause.UNKNOWN);
959 }
960
961 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800962 this.disconnectCause = disconnectCause;
963 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800964 }
965
966 /** Returns the possible text message responses. */
967 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800968 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800969 }
970
971 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
972 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800973 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800974
975 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
976 // We allow you to merge if the capabilities allow it or if it is a call with
977 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800978 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800979 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
980 // Cannot merge calls if there are no calls to merge with.
981 return false;
982 }
983 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
984 }
985 return (capabilities == (capabilities & supportedCapabilities));
986 }
987
988 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800989 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800990 }
991
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700992 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800993 public String getUniqueCallId() {
994 return uniqueCallId;
995 }
996
997 /** Gets the time when the call first became active. */
998 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800999 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -08001000 }
1001
wangqied677ef2018-04-04 12:03:00 -07001002 /**
1003 * Gets the time when the call is created (see {@link Details#getCreationTimeMillis()}). This is
1004 * the same time that is logged as the start time in the Call Log (see {@link
1005 * android.provider.CallLog.Calls#DATE}).
1006 */
1007 @TargetApi(26)
1008 public long getCreationTimeMillis() {
1009 return telecomCall.getDetails().getCreationTimeMillis();
1010 }
1011
Eric Erfanianccca3152017-02-22 16:32:36 -08001012 public boolean isConferenceCall() {
1013 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
1014 }
1015
1016 @Nullable
1017 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -08001018 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -08001019 }
1020
1021 @Nullable
1022 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -08001023 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -08001024 }
1025
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001026 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -08001027 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -08001028 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -08001029 }
1030
1031 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -08001032 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -08001033 }
1034
1035 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -08001036 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -08001037 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -08001038 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001039 }
1040 return null;
1041 }
1042
1043 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -08001044 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -08001045 }
1046
1047 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -07001048 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001049 }
1050
wangqi8d407a02018-02-15 15:32:52 -08001051 @TargetApi(28)
wangqif6be6172018-03-30 15:57:56 -07001052 public boolean isActiveRttCall() {
wangqi219b8702018-02-13 09:34:41 -08001053 if (BuildCompat.isAtLeastP()) {
1054 return getTelecomCall().isRttActive();
1055 } else {
1056 return false;
1057 }
1058 }
1059
wangqi153af2f2018-02-15 16:21:49 -08001060 @TargetApi(28)
1061 public RttCall getRttCall() {
wangqif6be6172018-03-30 15:57:56 -07001062 if (!isActiveRttCall()) {
wangqi153af2f2018-02-15 16:21:49 -08001063 return null;
1064 }
1065 return getTelecomCall().getRttCall();
1066 }
1067
wangqif6be6172018-03-30 15:57:56 -07001068 @TargetApi(28)
1069 public boolean canUpgradeToRttCall() {
1070 PhoneAccount phoneAccount = getPhoneAccount();
1071 if (phoneAccount == null) {
1072 return false;
1073 }
1074 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
1075 return false;
1076 }
1077 if (isActiveRttCall()) {
1078 return false;
1079 }
1080 if (isVideoCall()) {
1081 return false;
1082 }
1083 if (isConferenceCall()) {
1084 return false;
1085 }
1086 if (CallList.getInstance().hasActiveRttCall()) {
1087 return false;
1088 }
1089 return true;
1090 }
1091
1092 @TargetApi(28)
1093 public void sendRttUpgradeRequest() {
1094 getTelecomCall().sendRttRequest();
1095 }
1096
wangqibc28ea72018-04-02 16:23:00 -07001097 @TargetApi(28)
1098 public void respondToRttRequest(boolean accept, int rttRequestId) {
wangqi81e373e2018-04-24 10:50:57 -07001099 Logger.get(context)
1100 .logCallImpression(
1101 accept
1102 ? DialerImpression.Type.RTT_MID_CALL_ACCEPTED
1103 : DialerImpression.Type.RTT_MID_CALL_REJECTED,
1104 getUniqueCallId(),
1105 getTimeAddedMs());
wangqibc28ea72018-04-02 16:23:00 -07001106 getTelecomCall().respondToRttRequest(rttRequestId, accept);
1107 }
1108
wangqibed9e2f2018-04-30 11:55:07 -07001109 @TargetApi(28)
1110 private void saveRttTranscript() {
1111 if (!BuildCompat.isAtLeastP()) {
1112 return;
1113 }
1114 // Save any remaining text in the buffer that's not shown by UI yet.
1115 // This may happen when the call is switched to background before disconnect.
1116 try {
1117 String messageLeft = getRttCall().readImmediately();
1118 if (!TextUtils.isEmpty(messageLeft)) {
1119 rttTranscript =
1120 RttChatMessage.getRttTranscriptWithNewRemoteMessage(rttTranscript, messageLeft);
1121 }
1122 } catch (IOException e) {
1123 LogUtil.e("DialerCall.saveRttTranscript", "error when reading remaining message", e);
1124 }
wangqi6e0d6392018-04-30 16:22:34 -07001125 // Don't save transcript if it's empty.
1126 if (rttTranscript.getMessagesCount() == 0) {
1127 return;
1128 }
wangqibed9e2f2018-04-30 11:55:07 -07001129 Futures.addCallback(
1130 RttTranscriptUtil.saveRttTranscript(context, rttTranscript),
1131 new DefaultFutureCallback<>(),
1132 MoreExecutors.directExecutor());
1133 }
1134
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001135 public boolean hasReceivedVideoUpgradeRequest() {
1136 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
1137 }
1138
1139 public boolean hasSentVideoUpgradeRequest() {
1140 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -08001141 }
1142
wangqi219b8702018-02-13 09:34:41 -08001143 public boolean hasSentRttUpgradeRequest() {
wangqi219b8702018-02-13 09:34:41 -08001144 return false;
1145 }
1146
Eric Erfanianccca3152017-02-22 16:32:36 -08001147 /**
1148 * Determines if the call handle is an emergency number or not and caches the result to avoid
1149 * repeated calls to isEmergencyNumber.
1150 */
1151 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -08001152 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -08001153 }
1154
Eric Erfanianccca3152017-02-22 16:32:36 -08001155 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -08001156 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -08001157 }
1158
1159 /**
1160 * Determines if the call is an external call.
1161 *
1162 * <p>An external call is one which does not exist locally for the {@link
1163 * android.telecom.ConnectionService} it is associated with.
1164 *
Eric Erfanianccca3152017-02-22 16:32:36 -08001165 * @return {@code true} if the call is an external call, {@code false} otherwise.
1166 */
linyuh437ae952018-03-26 12:46:18 -07001167 boolean isExternalCall() {
1168 return hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
Eric Erfanianccca3152017-02-22 16:32:36 -08001169 }
1170
1171 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001172 * Determines if answering this call will cause an ongoing video call to be dropped.
1173 *
1174 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
1175 * otherwise.
1176 */
1177 public boolean answeringDisconnectsForegroundVideoCall() {
1178 Bundle extras = getExtras();
1179 if (extras == null
1180 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
1181 return false;
1182 }
1183 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
1184 }
1185
1186 private void parseCallSpecificAppData() {
1187 if (isExternalCall()) {
1188 return;
1189 }
1190
linyuh183cb712017-12-27 17:02:37 -08001191 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
1192 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001193
linyuh183cb712017-12-27 17:02:37 -08001194 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -07001195 CallSpecificAppData.newBuilder()
1196 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
1197 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001198 }
wangqibb94ca62018-04-27 14:34:04 -07001199 if (getState() == DialerCallState.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -08001200 logState.callSpecificAppData =
1201 logState
Eric Erfanian8369df02017-05-03 10:27:13 -07001202 .callSpecificAppData
1203 .toBuilder()
1204 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
1205 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001206 }
1207 }
1208
1209 @Override
1210 public String toString() {
linyuh183cb712017-12-27 17:02:37 -08001211 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001212 // This should happen only in testing since otherwise we would never have a null
1213 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -08001214 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -08001215 }
1216
1217 return String.format(
1218 Locale.US,
1219 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001220 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001221 id,
wangqibb94ca62018-04-27 14:34:04 -07001222 DialerCallState.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001223 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1224 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1225 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001226 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001227 this.telecomCall.getConferenceableCalls(),
1228 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001229 getVideoTech().getSessionModificationState(),
1230 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001231 }
1232
1233 public String toSimpleString() {
1234 return super.toString();
1235 }
1236
1237 @CallHistoryStatus
1238 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001239 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001240 }
1241
1242 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001243 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001244 }
1245
1246 public boolean didShowCameraPermission() {
1247 return didShowCameraPermission;
1248 }
1249
1250 public void setDidShowCameraPermission(boolean didShow) {
1251 didShowCameraPermission = didShow;
1252 }
1253
wangqida410d32018-03-06 16:51:38 -08001254 public boolean didDismissVideoChargesAlertDialog() {
1255 return didDismissVideoChargesAlertDialog;
1256 }
1257
1258 public void setDidDismissVideoChargesAlertDialog(boolean didDismiss) {
1259 didDismissVideoChargesAlertDialog = didDismiss;
1260 }
1261
Eric Erfanian938468d2017-10-24 14:05:52 -07001262 @Nullable
1263 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001264 return isInGlobalSpamList;
1265 }
1266
1267 public void setIsInGlobalSpamList(boolean inSpamList) {
1268 isInGlobalSpamList = inSpamList;
1269 }
1270
Eric Erfanian938468d2017-10-24 14:05:52 -07001271 @Nullable
1272 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001273 return isInUserSpamList;
1274 }
1275
1276 public void setIsInUserSpamList(boolean inSpamList) {
1277 isInUserSpamList = inSpamList;
1278 }
1279
Eric Erfanian938468d2017-10-24 14:05:52 -07001280 @Nullable
1281 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001282 return isInUserWhiteList;
1283 }
1284
1285 public void setIsInUserWhiteList(boolean inWhiteList) {
1286 isInUserWhiteList = inWhiteList;
1287 }
1288
1289 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001290 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001291 }
1292
1293 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001294 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001295 }
1296
1297 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001298 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001299 }
1300
1301 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001302 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001303 }
1304
1305 public boolean isRemotelyHeld() {
1306 return isRemotelyHeld;
1307 }
1308
Eric Erfanian2ca43182017-08-31 06:57:16 -07001309 public boolean isMergeInProcess() {
1310 return isMergeInProcess;
1311 }
1312
Eric Erfanianccca3152017-02-22 16:32:36 -08001313 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001314 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001315 }
1316
erfanian2cf2c342017-12-21 12:01:33 -08001317 /**
1318 * Try and determine if the call used assisted dialing.
1319 *
1320 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1321 * implmentation is complete in P+.
1322 *
1323 * @return a boolean indicating assisted dialing may have been performed
1324 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001325 public boolean isAssistedDialed() {
1326 if (getIntentExtras() != null) {
erfaniand2e5d0b2018-03-02 14:54:35 -08001327 // P and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
erfanian2cf2c342017-12-21 12:01:33 -08001328 // was used. The Dialer client is responsible for performing assisted dialing before
1329 // placing the outgoing call.
1330 //
1331 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1332 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1333 && getAssistedDialingExtras() != null
1334 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1335 return true;
1336 }
1337 }
1338
Eric Erfanian2ca43182017-08-31 06:57:16 -07001339 return false;
1340 }
1341
erfanian2cf2c342017-12-21 12:01:33 -08001342 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001343 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001344 if (getIntentExtras() == null) {
1345 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001346 }
erfanian2cf2c342017-12-21 12:01:33 -08001347
1348 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1349 return null;
1350 }
1351
erfanianf2556612018-01-23 09:55:59 -08001352 // Used in N-OMR1
erfanian2cf2c342017-12-21 12:01:33 -08001353 return TransformationInfo.newInstanceFromBundle(
1354 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001355 }
1356
Eric Erfanianccca3152017-02-22 16:32:36 -08001357 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001358 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001359 }
1360
Eric Erfanian2ca43182017-08-31 06:57:16 -07001361 public int getAnswerAndReleaseButtonDisplayedTimes() {
1362 return answerAndReleaseButtonDisplayedTimes;
1363 }
1364
1365 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1366 answerAndReleaseButtonDisplayedTimes++;
1367 }
1368
1369 public boolean getReleasedByAnsweringSecondCall() {
1370 return releasedByAnsweringSecondCall;
1371 }
1372
1373 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1374 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1375 }
1376
1377 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1378 return secondCallWithoutAnswerAndReleasedButtonTimes;
1379 }
1380
1381 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1382 secondCallWithoutAnswerAndReleasedButtonTimes++;
1383 }
1384
Eric Erfanian8369df02017-05-03 10:27:13 -07001385 @Nullable
1386 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001387 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001388 }
1389
1390 public void setEnrichedCallCapabilities(
1391 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001392 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001393 }
1394
1395 @Nullable
1396 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001397 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001398 }
1399
1400 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001401 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001402 }
1403
Eric Erfanianccca3152017-02-22 16:32:36 -08001404 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001405 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001406 }
1407
Eric Erfanianccca3152017-02-22 16:32:36 -08001408 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1409 LogUtil.i(
1410 "DialerCall.phoneAccountSelected",
1411 "accountHandle: %s, setDefault: %b",
1412 accountHandle,
1413 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001414 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001415 }
1416
1417 public void disconnect() {
1418 LogUtil.i("DialerCall.disconnect", "");
wangqibb94ca62018-04-27 14:34:04 -07001419 setState(DialerCallState.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001420 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001421 listener.onDialerCallUpdate();
1422 }
linyuh183cb712017-12-27 17:02:37 -08001423 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001424 }
1425
1426 public void hold() {
1427 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001428 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001429 }
1430
1431 public void unhold() {
1432 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001433 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001434 }
1435
1436 public void splitFromConference() {
1437 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001438 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001439 }
1440
1441 public void answer(int videoState) {
1442 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001443 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001444 }
1445
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001446 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001447 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001448 }
1449
Eric Erfanianccca3152017-02-22 16:32:36 -08001450 public void reject(boolean rejectWithMessage, String message) {
1451 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001452 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001453 }
1454
1455 /** Return the string label to represent the call provider */
1456 public String getCallProviderLabel() {
1457 if (callProviderLabel == null) {
1458 PhoneAccount account = getPhoneAccount();
1459 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001460 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001461 callProviderLabel = account.getLabel().toString();
1462 }
1463 }
1464 if (callProviderLabel == null) {
1465 callProviderLabel = "";
1466 }
1467 }
1468 return callProviderLabel;
1469 }
1470
1471 private PhoneAccount getPhoneAccount() {
1472 PhoneAccountHandle accountHandle = getAccountHandle();
1473 if (accountHandle == null) {
1474 return null;
1475 }
linyuh183cb712017-12-27 17:02:37 -08001476 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001477 }
1478
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001479 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001480 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001481 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001482
1483 // Only store the first video tech type found to be available during the life of the call.
1484 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1485 // Update the video tech.
1486 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1487 }
1488 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001489 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001490 }
1491
Eric Erfanianccca3152017-02-22 16:32:36 -08001492 public String getCallbackNumber() {
1493 if (callbackNumber == null) {
1494 // Show the emergency callback number if either:
1495 // 1. This is an emergency call.
1496 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1497 // number.
1498 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1499
1500 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001501 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001502 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001503 }
1504
Eric Erfanianccca3152017-02-22 16:32:36 -08001505 if (callbackNumber == null) {
1506 callbackNumber = "";
1507 }
1508 }
1509 return callbackNumber;
1510 }
1511
wangqi97539352017-09-25 11:15:16 -07001512 public String getSimCountryIso() {
1513 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001514 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001515 .getSimCountryIso();
1516 if (!TextUtils.isEmpty(simCountryIso)) {
1517 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1518 }
1519 return simCountryIso;
1520 }
1521
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001522 @Override
1523 public void onVideoTechStateChanged() {
1524 update();
1525 }
1526
1527 @Override
1528 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001529 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001530 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001531 listener.onDialerCallSessionModificationStateChange();
1532 }
wangqi9982f0d2017-10-11 17:46:07 -07001533 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001534 }
1535
1536 @Override
1537 public void onCameraDimensionsChanged(int width, int height) {
1538 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1539 }
1540
1541 @Override
1542 public void onPeerDimensionsChanged(int width, int height) {
1543 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1544 }
1545
1546 @Override
1547 public void onVideoUpgradeRequestReceived() {
1548 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1549
linyuh183cb712017-12-27 17:02:37 -08001550 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001551 listener.onDialerCallUpgradeToVideo();
1552 }
1553
1554 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001555
linyuh183cb712017-12-27 17:02:37 -08001556 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001557 .logCallImpression(
1558 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1559 }
1560
1561 @Override
1562 public void onUpgradedToVideo(boolean switchToSpeaker) {
1563 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1564
1565 if (!switchToSpeaker) {
1566 return;
1567 }
1568
1569 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1570
1571 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1572 LogUtil.e(
1573 "DialerCall.onUpgradedToVideo",
1574 "toggling speakerphone not allowed when bluetooth supported.");
1575 return;
1576 }
1577
1578 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1579 return;
1580 }
1581
1582 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001583 }
1584
Eric Erfanian2ca43182017-08-31 06:57:16 -07001585 @Override
1586 public void onCapabilitiesUpdated() {
1587 if (getNumber() == null) {
1588 return;
1589 }
1590 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001591 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001592 if (capabilities != null) {
1593 setEnrichedCallCapabilities(capabilities);
1594 update();
1595 }
1596 }
1597
1598 @Override
1599 public void onEnrichedCallStateChanged() {
1600 updateEnrichedCallSession();
1601 }
1602
1603 @Override
1604 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001605 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001606 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1607 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001608 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001609 .logCallImpression(
1610 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1611 getUniqueCallId(),
1612 getTimeAddedMs());
1613 }
1614 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001615 }
1616
1617 private void updateEnrichedCallSession() {
1618 if (getNumber() == null) {
1619 return;
1620 }
1621 if (getEnrichedCallSession() != null) {
1622 // State changes to existing sessions are currently handled by the UI components (which have
1623 // their own listeners). Someday instead we could remove those and just call update() here and
1624 // have the usual onDialerCallUpdate update the UI.
1625 dispatchOnEnrichedCallSessionUpdate();
1626 return;
1627 }
1628
linyuh183cb712017-12-27 17:02:37 -08001629 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001630
1631 Filter filter =
1632 isIncoming()
1633 ? manager.createIncomingCallComposerFilter()
1634 : manager.createOutgoingCallComposerFilter();
1635
1636 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1637 if (session == null) {
1638 return;
1639 }
1640
1641 session.setUniqueDialerCallId(getUniqueCallId());
1642 setEnrichedCallSession(session);
1643
1644 LogUtil.i(
1645 "DialerCall.updateEnrichedCallSession",
1646 "setting session %d's dialer id to %s",
1647 session.getSessionId(),
1648 getUniqueCallId());
1649
1650 dispatchOnEnrichedCallSessionUpdate();
1651 }
1652
1653 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001654 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001655 listener.onEnrichedCallSessionUpdate();
1656 }
1657 }
1658
1659 void onRemovedFromCallList() {
wangqif1861052018-04-19 15:06:06 -07001660 LogUtil.enterBlock("DialerCall.onRemovedFromCallList");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001661 // Ensure we clean up when this call is removed.
wangqia0c49f62018-04-25 15:04:01 -07001662 if (videoTechManager != null) {
1663 videoTechManager.dispatchRemovedFromCallList();
1664 }
1665 // TODO(a bug): Add tests for it to make sure no crash on subsequent call to this method.
wangqibed9e2f2018-04-30 11:55:07 -07001666 // TODO(wangqi): Consider moving this to a DialerCallListener.
wangqia0c49f62018-04-25 15:04:01 -07001667 if (rttTranscript != null && !isCallRemoved) {
wangqibed9e2f2018-04-30 11:55:07 -07001668 saveRttTranscript();
wangqif1861052018-04-19 15:06:06 -07001669 }
wangqia0c49f62018-04-25 15:04:01 -07001670 isCallRemoved = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001671 }
1672
wangqi4d705e52017-09-28 12:23:35 -07001673 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1674 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001675 }
1676
Android Dialer974fc292018-02-01 16:12:25 -08001677 public void markFeedbackRequested() {
1678 feedbackRequested = true;
1679 }
1680
1681 public boolean isFeedbackRequested() {
1682 return feedbackRequested;
1683 }
1684
Eric Erfanianccca3152017-02-22 16:32:36 -08001685 /**
twyen73a74c32018-03-07 12:12:24 -08001686 * If the in call UI has shown the phone account selection dialog for the call, the {@link
1687 * PreferredAccountRecorder} to record the result from the dialog.
1688 */
1689 @Nullable
1690 public PreferredAccountRecorder getPreferredAccountRecorder() {
1691 return preferredAccountRecorder;
1692 }
1693
1694 public void setPreferredAccountRecorder(PreferredAccountRecorder preferredAccountRecorder) {
1695 this.preferredAccountRecorder = preferredAccountRecorder;
1696 }
1697
erfaniand05d8992018-03-20 19:42:26 -07001698 /** Indicates the call is eligible for SpeakEasy */
1699 public boolean isSpeakEasyEligible() {
erfaniandcc84cd2018-04-17 12:41:05 -07001700 if (!Constraints.isAvailable(context)) {
1701 return false;
1702 }
1703
1704 return !isPotentialEmergencyCallback()
1705 && !isEmergencyCall()
1706 && !isActiveRttCall()
1707 && !isConferenceCall()
1708 && !isVideoCall()
1709 && !isVoiceMailNumber();
erfaniand05d8992018-03-20 19:42:26 -07001710 }
1711
1712 /** Indicates the user has selected SpeakEasy */
1713 public boolean isSpeakEasyCall() {
1714 if (!isSpeakEasyEligible()) {
1715 return false;
1716 }
1717 return isSpeakEasyCall;
1718 }
1719
1720 /** Sets the user preference for SpeakEasy */
1721 public void setIsSpeakEasyCall(boolean isSpeakEasyCall) {
1722 this.isSpeakEasyCall = isSpeakEasyCall;
1723 }
1724
twyen73a74c32018-03-07 12:12:24 -08001725 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001726 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1727 * means there is no result.
1728 */
1729 @IntDef({
1730 CALL_HISTORY_STATUS_UNKNOWN,
1731 CALL_HISTORY_STATUS_PRESENT,
1732 CALL_HISTORY_STATUS_NOT_PRESENT
1733 })
1734 @Retention(RetentionPolicy.SOURCE)
1735 public @interface CallHistoryStatus {}
1736
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001737 /** Camera direction constants */
1738 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001739 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1740 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1741 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001742 }
1743
1744 /**
1745 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1746 * existing call member variables, but this duplication helps to ensure that none of these logging
1747 * variables will interface with/and affect call logic.
1748 */
1749 public static class LogState {
1750
1751 public DisconnectCause disconnectCause;
1752 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001753 public ContactLookupResult.Type contactLookupResult =
1754 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001755 public CallSpecificAppData callSpecificAppData;
1756 // If this was a conference call, the total number of calls involved in the conference.
1757 public int conferencedCalls = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -08001758 public boolean isLogged = false;
1759
maxwelbd95d0f62018-05-01 17:50:55 -07001760 // Result of subtracting android.telecom.Call.Details#getConnectTimeMillis from the current time
1761 public long telecomDurationMillis = 0;
1762
1763 // Result of a call to System.currentTimeMillis when Dialer sees that a call
1764 // moves to the ACTIVE state
1765 long dialerConnectTimeMillis = 0;
1766
1767 // Same as dialer_connect_time_millis, using SystemClock.elapsedRealtime
1768 // instead
1769 long dialerConnectTimeMillisElapsedRealtime = 0;
1770
1771 // Result of subtracting dialer_connect_time_millis from System.currentTimeMillis
1772 public long dialerDurationMillis = 0;
1773
1774 // Same as dialerDurationMillis, using SystemClock.elapsedRealtime instead
1775 public long dialerDurationMillisElapsedRealtime = 0;
1776
Eric Erfanian8369df02017-05-03 10:27:13 -07001777 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001778 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001779 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001780 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001781 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001782 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001783 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001784 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001785 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001786 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001787 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001788 return "Voicemail";
1789 default:
1790 return "Not found";
1791 }
1792 }
1793
1794 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1795 if (callSpecificAppData == null) {
1796 return "null";
1797 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001798 switch (callSpecificAppData.getCallInitiationType()) {
1799 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001800 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001801 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001802 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001803 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001804 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001805 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001806 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001807 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001808 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001809 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001810 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001811 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001812 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001813 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001814 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001815 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001816 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001817 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001818 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001819 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001820 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001821 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001822 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001823 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001824 return "Launcher Shortcut";
1825 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001826 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001827 }
1828 }
1829
1830 @Override
1831 public String toString() {
1832 return String.format(
1833 Locale.US,
1834 "["
1835 + "%s, " // DisconnectCause toString already describes the object type
1836 + "isIncoming: %s, "
1837 + "contactLookup: %s, "
1838 + "callInitiation: %s, "
1839 + "duration: %s"
1840 + "]",
1841 disconnectCause,
1842 isIncoming,
1843 lookupToString(contactLookupResult),
1844 initiationToString(callSpecificAppData),
maxwelbd95d0f62018-05-01 17:50:55 -07001845 telecomDurationMillis);
Eric Erfanianccca3152017-02-22 16:32:36 -08001846 }
1847 }
1848
roldenburgd7490db2018-01-09 13:51:29 -08001849 /** Coordinates the available VideoTech implementations for a call. */
1850 @VisibleForTesting
1851 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001852 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001853 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001854 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001855 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001856 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001857
roldenburgd7490db2018-01-09 13:51:29 -08001858 @VisibleForTesting
1859 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001860 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001861
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001862 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001863 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001864 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001865
1866 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001867 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001868
linyuh183cb712017-12-27 17:02:37 -08001869 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001870
roldenburgd7490db2018-01-09 13:51:29 -08001871 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001872 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001873 .getRcsVideoShareFactory()
1874 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001875 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001876 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001877 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001878 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001879
1880 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001881 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001882 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001883
1884 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001885 }
1886
roldenburgd7490db2018-01-09 13:51:29 -08001887 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001888 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001889 if (savedTech == emptyVideoTech) {
1890 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001891 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001892 savedTech = tech;
1893 savedTech.becomePrimary();
1894 break;
1895 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001896 }
roldenburg6bd612f2018-01-18 12:57:19 -08001897 } else if (savedTech instanceof DuoVideoTech
1898 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001899 // RCS Video Share will become available after the capability exchange which is slower than
1900 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1901 // not in the middle of any session changes, let it take over.
1902 savedTech = rcsVideoShare;
1903 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001904 }
1905
roldenburgd7490db2018-01-09 13:51:29 -08001906 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001907 }
1908
roldenburgd7490db2018-01-09 13:51:29 -08001909 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001910 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001911 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001912 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001913 }
1914 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001915
1916 void dispatchRemovedFromCallList() {
1917 for (VideoTech videoTech : videoTechs) {
1918 videoTech.onRemovedFromCallList();
1919 }
1920 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001921 }
1922
Eric Erfanianccca3152017-02-22 16:32:36 -08001923 /** Called when canned text responses have been loaded. */
1924 public interface CannedTextResponsesLoadedListener {
1925 void onCannedTextResponsesLoaded(DialerCall call);
1926 }
1927}