blob: fc2276232372d355a8db4e5f3b9e154ee0af5306 [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;
erfanian2cf2c342017-12-21 12:01:33 -080053import com.android.dialer.assisteddialing.ConcreteCreator;
erfaniand0f207f2017-10-11 12:23:29 -070054import com.android.dialer.assisteddialing.TransformationInfo;
maxwelbb87334b2018-05-14 13:57:32 -070055import com.android.dialer.blocking.FilteredNumbersUtil;
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;
zachh190343a2018-05-31 17:30:46 -070063import com.android.dialer.configprovider.ConfigProviderComponent;
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;
maxwelbb87334b2018-05-14 13:57:32 -070080import com.android.dialer.spam.status.SpamStatus;
twyena4745bd2017-12-12 18:40:11 -080081import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070082import com.android.dialer.telecom.TelecomUtil;
calderwoodrac5c42182018-05-22 15:30:39 -070083import com.android.dialer.theme.common.R;
maxwelbd95d0f62018-05-01 17:50:55 -070084import com.android.dialer.time.Clock;
wangqi9982f0d2017-10-11 17:46:07 -070085import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070086import com.android.incallui.audiomode.AudioModeProvider;
wangqibb94ca62018-04-27 14:34:04 -070087import com.android.incallui.call.state.DialerCallState;
Eric Erfanianccca3152017-02-22 16:32:36 -080088import com.android.incallui.latencyreport.LatencyReport;
wangqibed9e2f2018-04-30 11:55:07 -070089import com.android.incallui.rtt.protocol.RttChatMessage;
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;
maxwelbb87334b2018-05-14 13:57:32 -070096import com.google.common.base.Optional;
wangqid6b10d52018-04-12 14:44:06 -070097import com.google.common.util.concurrent.Futures;
wangqid6b10d52018-04-12 14:44:06 -070098import com.google.common.util.concurrent.MoreExecutors;
wangqibed9e2f2018-04-30 11:55:07 -070099import java.io.IOException;
Eric Erfanianccca3152017-02-22 16:32:36 -0800100import java.lang.annotation.Retention;
101import java.lang.annotation.RetentionPolicy;
102import java.util.ArrayList;
103import java.util.List;
104import java.util.Locale;
105import java.util.Objects;
106import java.util.UUID;
107import java.util.concurrent.CopyOnWriteArrayList;
108import java.util.concurrent.TimeUnit;
109
110/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -0700111public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -0800112
113 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
114 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
115 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700116
117 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -0700118 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700119 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
120
Eric Erfanianccca3152017-02-22 16:32:36 -0800121 private static final String ID_PREFIX = "DialerCall_";
maxwelbc1401222018-05-03 11:51:02 -0700122
123 @VisibleForTesting
124 public static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
Eric Erfanianccca3152017-02-22 16:32:36 -0800125 "emergency_callback_window_millis";
maxwelbc1401222018-05-03 11:51:02 -0700126
linyuh183cb712017-12-27 17:02:37 -0800127 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800128
129 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700130 * A counter used to append to restricted/private/hidden calls so that users can identify them in
131 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
132 * are no live calls.
133 */
linyuh183cb712017-12-27 17:02:37 -0800134 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700135
136 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800137 * The unique call ID for every call. This will help us to identify each call and allow us the
138 * ability to stitch impressions to calls if needed.
139 */
140 private final String uniqueCallId = UUID.randomUUID().toString();
141
linyuh183cb712017-12-27 17:02:37 -0800142 private final Call telecomCall;
143 private final LatencyReport latencyReport;
144 private final String id;
145 private final int hiddenId;
146 private final List<String> childCallIds = new ArrayList<>();
147 private final LogState logState = new LogState();
148 private final Context context;
149 private final DialerCallDelegate dialerCallDelegate;
150 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
151 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800152 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800153 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800154
erfaniand05d8992018-03-20 19:42:26 -0700155 private boolean isSpeakEasyCall;
linyuh183cb712017-12-27 17:02:37 -0800156 private boolean isEmergencyCall;
157 private Uri handle;
wangqibb94ca62018-04-27 14:34:04 -0700158 private int state = DialerCallState.INVALID;
linyuh183cb712017-12-27 17:02:37 -0800159 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800160
roldenburg08424ee2018-03-28 17:02:46 -0700161 private boolean hasShownLteToWiFiHandoverToast;
Eric Erfanianccca3152017-02-22 16:32:36 -0800162 private boolean hasShownWiFiToLteHandoverToast;
163 private boolean doNotShowDialogForHandoffToWifiFailure;
164
linyuh183cb712017-12-27 17:02:37 -0800165 private String childNumber;
166 private String lastForwardedNumber;
wangqif4ba3452018-01-09 11:26:29 -0800167 private boolean isCallForwarded;
linyuh183cb712017-12-27 17:02:37 -0800168 private String callSubject;
twyen27c37182018-04-24 11:48:12 -0700169 @Nullable private PhoneAccountHandle phoneAccountHandle;
linyuh183cb712017-12-27 17:02:37 -0800170 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
maxwelbb87334b2018-05-14 13:57:32 -0700171
172 @Nullable private SpamStatus spamStatus;
linyuh183cb712017-12-27 17:02:37 -0800173 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700174
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);
wangqi3d5e04f2018-05-30 12:12:59 -0700318 Toast.makeText(context, R.string.rtt_call_not_available_toast, Toast.LENGTH_LONG).show();
wangqi219b8702018-02-13 09:34:41 -0800319 update();
320 }
321
322 @Override
323 public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {
324 LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled);
wangqi81e373e2018-04-24 10:50:57 -0700325 if (enabled) {
326 Logger.get(context)
327 .logCallImpression(
328 DialerImpression.Type.RTT_MID_CALL_ENABLED,
329 getUniqueCallId(),
330 getTimeAddedMs());
331 }
wangqi219b8702018-02-13 09:34:41 -0800332 update();
333 }
334
335 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800336 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
337 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700338 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800339 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
340 switch (event) {
341 // The Previous attempt to Merge two calls together has failed in Telecom. We must
342 // now update the UI to possibly re-enable the Merge button based on the number of
343 // currently conferenceable calls available or Connection Capabilities.
344 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
345 update();
346 break;
347 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
348 notifyWiFiToLteHandover();
349 break;
roldenburg08424ee2018-03-28 17:02:46 -0700350 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI:
351 onLteToWifiHandover();
352 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800353 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
354 notifyHandoverToWifiFailed();
355 break;
356 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
357 isRemotelyHeld = true;
358 update();
359 break;
360 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
361 isRemotelyHeld = false;
362 update();
363 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700364 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
365 notifyInternationalCallOnWifi();
366 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700367 case TelephonyManagerCompat.EVENT_MERGE_START:
368 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
369 isMergeInProcess = true;
370 break;
371 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
372 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
373 isMergeInProcess = false;
374 break;
wangqif4ba3452018-01-09 11:26:29 -0800375 case TelephonyManagerCompat.EVENT_CALL_FORWARDED:
376 // Only handle this event for P+ since it's unreliable pre-P.
377 if (BuildCompat.isAtLeastP()) {
378 isCallForwarded = true;
379 update();
380 }
381 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800382 default:
383 break;
384 }
385 }
386 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700387
linyuh183cb712017-12-27 17:02:37 -0800388 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800389
390 public DialerCall(
391 Context context,
392 DialerCallDelegate dialerCallDelegate,
393 Call telecomCall,
394 LatencyReport latencyReport,
395 boolean registerCallback) {
396 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800397 this.context = context;
398 this.dialerCallDelegate = dialerCallDelegate;
399 this.telecomCall = telecomCall;
400 this.latencyReport = latencyReport;
401 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800402
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700403 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800404 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700405
406 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700407 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800408 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700409 } else {
linyuh183cb712017-12-27 17:02:37 -0800410 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700411 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800412
413 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800414 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800415 }
416
linyuh183cb712017-12-27 17:02:37 -0800417 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800418 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700419
420 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800421 }
422
423 private static int translateState(int state) {
424 switch (state) {
425 case Call.STATE_NEW:
426 case Call.STATE_CONNECTING:
wangqibb94ca62018-04-27 14:34:04 -0700427 return DialerCallState.CONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800428 case Call.STATE_SELECT_PHONE_ACCOUNT:
wangqibb94ca62018-04-27 14:34:04 -0700429 return DialerCallState.SELECT_PHONE_ACCOUNT;
Eric Erfanianccca3152017-02-22 16:32:36 -0800430 case Call.STATE_DIALING:
wangqibb94ca62018-04-27 14:34:04 -0700431 return DialerCallState.DIALING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800432 case Call.STATE_PULLING_CALL:
wangqibb94ca62018-04-27 14:34:04 -0700433 return DialerCallState.PULLING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800434 case Call.STATE_RINGING:
wangqibb94ca62018-04-27 14:34:04 -0700435 return DialerCallState.INCOMING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800436 case Call.STATE_ACTIVE:
wangqibb94ca62018-04-27 14:34:04 -0700437 return DialerCallState.ACTIVE;
Eric Erfanianccca3152017-02-22 16:32:36 -0800438 case Call.STATE_HOLDING:
wangqibb94ca62018-04-27 14:34:04 -0700439 return DialerCallState.ONHOLD;
Eric Erfanianccca3152017-02-22 16:32:36 -0800440 case Call.STATE_DISCONNECTED:
wangqibb94ca62018-04-27 14:34:04 -0700441 return DialerCallState.DISCONNECTED;
Eric Erfanianccca3152017-02-22 16:32:36 -0800442 case Call.STATE_DISCONNECTING:
wangqibb94ca62018-04-27 14:34:04 -0700443 return DialerCallState.DISCONNECTING;
Eric Erfanianccca3152017-02-22 16:32:36 -0800444 default:
wangqibb94ca62018-04-27 14:34:04 -0700445 return DialerCallState.INVALID;
Eric Erfanianccca3152017-02-22 16:32:36 -0800446 }
447 }
448
449 public static boolean areSame(DialerCall call1, DialerCall call2) {
450 if (call1 == null && call2 == null) {
451 return true;
452 } else if (call1 == null || call2 == null) {
453 return false;
454 }
455
456 // otherwise compare call Ids
457 return call1.getId().equals(call2.getId());
458 }
459
Eric Erfanianccca3152017-02-22 16:32:36 -0800460 public void addListener(DialerCallListener listener) {
461 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800462 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800463 }
464
465 public void removeListener(DialerCallListener listener) {
466 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800467 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800468 }
469
470 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
471 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800472 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800473 }
474
475 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
476 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800477 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800478 }
479
roldenburg08424ee2018-03-28 17:02:46 -0700480 private void onLteToWifiHandover() {
481 LogUtil.enterBlock("DialerCall.onLteToWifiHandover");
482 if (hasShownLteToWiFiHandoverToast) {
483 return;
484 }
485
486 Toast.makeText(context, R.string.video_call_lte_to_wifi_handover_toast, Toast.LENGTH_LONG)
487 .show();
488 hasShownLteToWiFiHandoverToast = true;
489 }
490
Eric Erfanianccca3152017-02-22 16:32:36 -0800491 public void notifyWiFiToLteHandover() {
492 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800493 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800494 listener.onWiFiToLteHandover();
495 }
496 }
497
498 public void notifyHandoverToWifiFailed() {
499 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800500 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800501 listener.onHandoverToWifiFailure();
502 }
503 }
504
Eric Erfanianc857f902017-05-15 14:05:33 -0700505 public void notifyInternationalCallOnWifi() {
506 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800507 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700508 dialerCallListener.onInternationalCallOnWifi();
509 }
510 }
511
Eric Erfanianccca3152017-02-22 16:32:36 -0800512 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800513 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800514 }
wangqida410d32018-03-06 16:51:38 -0800515
Eric Erfanianccca3152017-02-22 16:32:36 -0800516 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800517 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800518 }
519
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700520 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800521 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700522 }
523
524 public void setCameraDir(int cameraDir) {
525 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
526 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800527 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700528 } else {
linyuh183cb712017-12-27 17:02:37 -0800529 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700530 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800531 }
532
Eric Erfanian2ca43182017-08-31 06:57:16 -0700533 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800534 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700535 }
536
wangqi9982f0d2017-10-11 17:46:07 -0700537 public boolean isVoiceMailNumber() {
538 return isVoicemailNumber;
539 }
540
541 public List<PhoneAccountHandle> getCallCapableAccounts() {
542 return callCapableAccounts;
543 }
544
545 public String getCountryIso() {
546 return countryIso;
547 }
548
549 private void updateIsVoiceMailNumber() {
550 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
551 isVoicemailNumber = true;
roldenburg37a969d2018-02-22 14:46:44 -0800552 return;
wangqi9982f0d2017-10-11 17:46:07 -0700553 }
554
linyuh183cb712017-12-27 17:02:37 -0800555 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700556 isVoicemailNumber = false;
roldenburg37a969d2018-02-22 14:46:44 -0800557 return;
wangqi9982f0d2017-10-11 17:46:07 -0700558 }
559
linyuh183cb712017-12-27 17:02:37 -0800560 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700561 }
562
Eric Erfanianccca3152017-02-22 16:32:36 -0800563 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700564 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800565 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700566 // Clear any cache here that could potentially change on update.
567 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800568 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700569 updateFromTelecomCall();
wangqibb94ca62018-04-27 14:34:04 -0700570 if (oldState != getState() && getState() == DialerCallState.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800571 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800572 listener.onDialerCallDisconnect();
573 }
linyuh183cb712017-12-27 17:02:37 -0800574 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700575 .getEnrichedCallManager()
576 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800577 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700578 .getEnrichedCallManager()
579 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800580 } else {
linyuh183cb712017-12-27 17:02:37 -0800581 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800582 listener.onDialerCallUpdate();
583 }
584 }
585 Trace.endSection();
586 }
587
wangqi9982f0d2017-10-11 17:46:07 -0700588 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700589 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700590 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800591 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700592
roldenburg6bd612f2018-01-18 12:57:19 -0800593 videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700594
linyuh183cb712017-12-27 17:02:37 -0800595 final int translatedState = translateState(telecomCall.getState());
wangqibb94ca62018-04-27 14:34:04 -0700596 if (state != DialerCallState.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800597 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800598 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800599 }
600
linyuh183cb712017-12-27 17:02:37 -0800601 childCallIds.clear();
602 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800603 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800604 childCallIds.add(
605 dialerCallDelegate
606 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800607 .getId());
608 }
609
610 // The number of conferenced calls can change over the course of the call, so use the
611 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800612 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800613
linyuh183cb712017-12-27 17:02:37 -0800614 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800615
616 // If the handle of the call has changed, update state for the call determining if it is an
617 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800618 Uri newHandle = telecomCall.getDetails().getHandle();
619 if (!Objects.equals(handle, newHandle)) {
620 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800621 updateEmergencyCallState();
622 }
623
linyuh183cb712017-12-27 17:02:37 -0800624 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800625 // If the phone account handle of the call is set, cache capability bit indicating whether
626 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800627 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
628 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
629 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800630
linyuh183cb712017-12-27 17:02:37 -0800631 if (phoneAccountHandle != null) {
632 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800633 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800634 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800635 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
wangqida410d32018-03-06 16:51:38 -0800636 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
637 cacheCarrierConfiguration(phoneAccountHandle);
638 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800639 }
640 }
641 }
linyuh183cb712017-12-27 17:02:37 -0800642 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700643 updateIsVoiceMailNumber();
644 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800645 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700646 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700647 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800648 }
649
650 /**
wangqida410d32018-03-06 16:51:38 -0800651 * Caches frequently used carrier configuration locally.
652 *
653 * @param accountHandle The PhoneAccount handle.
654 */
655 @SuppressLint("MissingPermission")
656 private void cacheCarrierConfiguration(PhoneAccountHandle accountHandle) {
657 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
658 return;
659 }
660 if (VERSION.SDK_INT < VERSION_CODES.O) {
661 return;
662 }
663 // TODO(a bug): This may take several seconds to complete, revisit it to move it to worker
664 // thread.
665 carrierConfig =
666 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, accountHandle)
667 .getCarrierConfig();
668 }
669
670 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800671 * Tests corruption of the {@code callExtras} bundle by calling {@link
672 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
673 * be thrown and caught by this function.
674 *
675 * @param callExtras the bundle to verify
676 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
677 */
678 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
679 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700680 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800681 * bundle, resulting in a IllegalArgumentException while validating data under {@link
682 * Bundle#containsKey(String)}.
683 */
684 try {
685 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
686 return false;
687 } catch (IllegalArgumentException e) {
688 LogUtil.e(
689 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
690 return true;
691 }
692 }
693
694 protected void updateFromCallExtras(Bundle callExtras) {
695 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
696 /**
697 * If the bundle is corrupted, abandon information update as a work around. These are not
698 * critical for the dialer to function.
699 */
700 return;
701 }
702 // Check for a change in the child address and notify any listeners.
703 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
704 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800705 if (!Objects.equals(childNumber, this.childNumber)) {
706 this.childNumber = childNumber;
707 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800708 listener.onDialerCallChildNumberChange();
709 }
710 }
711 }
712
713 // Last forwarded number comes in as an array of strings. We want to choose the
714 // last item in the array. The forwarding numbers arrive independently of when the
715 // call is originally set up, so we need to notify the the UI of the change.
716 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
717 ArrayList<String> lastForwardedNumbers =
718 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
719
720 if (lastForwardedNumbers != null) {
721 String lastForwardedNumber = null;
722 if (!lastForwardedNumbers.isEmpty()) {
723 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
724 }
725
linyuh183cb712017-12-27 17:02:37 -0800726 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
727 this.lastForwardedNumber = lastForwardedNumber;
728 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800729 listener.onDialerCallLastForwardedNumberChange();
730 }
731 }
732 }
733 }
734
735 // DialerCall subject is present in the extras at the start of call, so we do not need to
736 // notify any other listeners of this.
737 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
738 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800739 if (!Objects.equals(this.callSubject, callSubject)) {
740 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800741 }
742 }
743 }
744
Eric Erfanianccca3152017-02-22 16:32:36 -0800745 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800746 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800747 }
748
Eric Erfanianc857f902017-05-15 14:05:33 -0700749 /**
750 * @return name appended with a number if the number is restricted/unknown and the user has
751 * received more than one restricted/unknown call.
752 */
753 @Nullable
754 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800755 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
756 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700757 }
758 return name;
759 }
760
761 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800762 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700763 }
764
765 private boolean isHiddenNumber() {
766 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
767 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
768 }
769
Eric Erfanianccca3152017-02-22 16:32:36 -0800770 public boolean hasShownWiFiToLteHandoverToast() {
771 return hasShownWiFiToLteHandoverToast;
772 }
773
774 public void setHasShownWiFiToLteHandoverToast() {
775 hasShownWiFiToLteHandoverToast = true;
776 }
777
778 public boolean showWifiHandoverAlertAsToast() {
779 return doNotShowDialogForHandoffToWifiFailure;
780 }
781
782 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
783 doNotShowDialogForHandoffToWifiFailure = bool;
784 }
785
wangqida410d32018-03-06 16:51:38 -0800786 public boolean showVideoChargesAlertDialog() {
787 if (carrierConfig == null) {
788 return false;
789 }
790 return carrierConfig.getBoolean(
791 TelephonyManagerCompat.CARRIER_CONFIG_KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL);
792 }
793
Eric Erfanianccca3152017-02-22 16:32:36 -0800794 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800795 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800796 }
797
798 @Nullable
799 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800800 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800801 }
802
803 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800804 telecomCall.reject(false, null);
wangqibb94ca62018-04-27 14:34:04 -0700805 setState(DialerCallState.BLOCKED);
Eric Erfanianccca3152017-02-22 16:32:36 -0800806 }
807
808 @Nullable
809 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800810 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800811 }
812
813 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800814 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800815 }
816
817 public boolean isPotentialEmergencyCallback() {
818 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
819 // is actually in emergency callback mode (ie data is disabled).
820 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
821 return true;
822 }
maxwelbb87334b2018-05-14 13:57:32 -0700823
824 // Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS is available starting in O
825 if (VERSION.SDK_INT < VERSION_CODES.O) {
826 long timestampMillis = FilteredNumbersUtil.getLastEmergencyCallTimeMillis(context);
827 return isInEmergencyCallbackWindow(timestampMillis);
828 }
829
Eric Erfanianccca3152017-02-22 16:32:36 -0800830 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
831 // as a potential emergency callback.
832 if (getExtras() != null
maxwelbc1401222018-05-03 11:51:02 -0700833 && getExtras().getLong(Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0) > 0) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800834 long lastEmergencyCallMillis =
maxwelbc1401222018-05-03 11:51:02 -0700835 getExtras().getLong(Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
Eric Erfanianccca3152017-02-22 16:32:36 -0800836 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
837 return true;
838 }
839 }
840 return false;
841 }
842
843 boolean isInEmergencyCallbackWindow(long timestampMillis) {
844 long emergencyCallbackWindowMillis =
zachh190343a2018-05-31 17:30:46 -0700845 ConfigProviderComponent.get(context)
846 .getConfigProvider()
Eric Erfanianccca3152017-02-22 16:32:36 -0800847 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
848 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
849 }
850
851 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800852 if (telecomCall != null && telecomCall.getParent() != null) {
wangqibb94ca62018-04-27 14:34:04 -0700853 return DialerCallState.CONFERENCED;
Eric Erfanianccca3152017-02-22 16:32:36 -0800854 } else {
linyuh183cb712017-12-27 17:02:37 -0800855 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800856 }
857 }
858
yueg265089a2017-10-06 14:35:15 -0700859 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800860 return state;
yueg265089a2017-10-06 14:35:15 -0700861 }
862
Eric Erfanianccca3152017-02-22 16:32:36 -0800863 public void setState(int state) {
wangqibb94ca62018-04-27 14:34:04 -0700864 if (state == DialerCallState.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800865 logState.isIncoming = true;
maxwelbd95d0f62018-05-01 17:50:55 -0700866 }
867 updateCallTiming(state);
868
869 this.state = state;
870 }
871
872 private void updateCallTiming(int newState) {
873 if (newState == DialerCallState.ACTIVE) {
874 if (this.state == DialerCallState.ACTIVE) {
875 LogUtil.i("DialerCall.updateCallTiming", "state is already active");
876 return;
877 }
878 logState.dialerConnectTimeMillis = clock.currentTimeMillis();
879 logState.dialerConnectTimeMillisElapsedRealtime = SystemClock.elapsedRealtime();
880 }
881
882 if (newState == DialerCallState.DISCONNECTED) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700883 long newDuration =
maxwelbd95d0f62018-05-01 17:50:55 -0700884 getConnectTimeMillis() == 0 ? 0 : clock.currentTimeMillis() - getConnectTimeMillis();
885 if (this.state == DialerCallState.DISCONNECTED) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700886 LogUtil.i(
887 "DialerCall.setState",
888 "ignoring state transition from DISCONNECTED to DISCONNECTED."
889 + " Duration would have changed from %s to %s",
maxwelbd95d0f62018-05-01 17:50:55 -0700890 logState.telecomDurationMillis,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700891 newDuration);
maxwelbd95d0f62018-05-01 17:50:55 -0700892 return;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700893 }
maxwelbd95d0f62018-05-01 17:50:55 -0700894 logState.telecomDurationMillis = newDuration;
895 logState.dialerDurationMillis =
896 logState.dialerConnectTimeMillis == 0
897 ? 0
898 : clock.currentTimeMillis() - logState.dialerConnectTimeMillis;
899 logState.dialerDurationMillisElapsedRealtime =
900 logState.dialerConnectTimeMillisElapsedRealtime == 0
901 ? 0
902 : SystemClock.elapsedRealtime() - logState.dialerConnectTimeMillisElapsedRealtime;
Eric Erfanianccca3152017-02-22 16:32:36 -0800903 }
maxwelbd95d0f62018-05-01 17:50:55 -0700904 }
905
906 @VisibleForTesting
907 void setClock(Clock clock) {
908 this.clock = clock;
Eric Erfanianccca3152017-02-22 16:32:36 -0800909 }
910
911 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800912 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800913 }
914
915 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800916 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800917 }
918
919 @Nullable
920 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800921 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800922 }
923
924 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800925 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800926 }
927
928 @Nullable
929 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800930 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800931 }
932
933 /** @return The child number for the call, or {@code null} if none specified. */
934 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800935 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800936 }
937
938 /** @return The last forwarded number for the call, or {@code null} if none specified. */
939 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800940 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800941 }
942
wangqif4ba3452018-01-09 11:26:29 -0800943 public boolean isCallForwarded() {
944 return isCallForwarded;
945 }
946
Eric Erfanianccca3152017-02-22 16:32:36 -0800947 /** @return The call subject, or {@code null} if none specified. */
948 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800949 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800950 }
951
952 /**
953 * @return {@code true} if the call's phone account supports call subjects, {@code false}
954 * otherwise.
955 */
956 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800957 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800958 }
959
960 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
961 public DisconnectCause getDisconnectCause() {
wangqibb94ca62018-04-27 14:34:04 -0700962 if (state == DialerCallState.DISCONNECTED || state == DialerCallState.IDLE) {
linyuh183cb712017-12-27 17:02:37 -0800963 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800964 }
965
966 return new DisconnectCause(DisconnectCause.UNKNOWN);
967 }
968
969 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800970 this.disconnectCause = disconnectCause;
971 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800972 }
973
974 /** Returns the possible text message responses. */
975 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800976 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800977 }
978
979 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
wangqi1b96ca52018-06-13 15:05:29 -0700980 @TargetApi(28)
Eric Erfanianccca3152017-02-22 16:32:36 -0800981 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800982 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800983
984 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
wangqi1b96ca52018-06-13 15:05:29 -0700985 boolean hasConferenceableCall = false;
986 // RTT call is not conferenceable, it's a bug (a bug) in Telecom and we work around it
987 // here before it's fixed in Telecom.
988 for (Call call : telecomCall.getConferenceableCalls()) {
989 if (!(BuildCompat.isAtLeastP() && call.isRttActive())) {
990 hasConferenceableCall = true;
991 break;
992 }
993 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800994 // We allow you to merge if the capabilities allow it or if it is a call with
995 // conferenceable calls.
wangqi1b96ca52018-06-13 15:05:29 -0700996 if (!hasConferenceableCall
Eric Erfanianccca3152017-02-22 16:32:36 -0800997 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
998 // Cannot merge calls if there are no calls to merge with.
999 return false;
1000 }
1001 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
1002 }
1003 return (capabilities == (capabilities & supportedCapabilities));
1004 }
1005
1006 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -08001007 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -08001008 }
1009
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001010 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -08001011 public String getUniqueCallId() {
1012 return uniqueCallId;
1013 }
1014
1015 /** Gets the time when the call first became active. */
1016 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -08001017 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -08001018 }
1019
wangqied677ef2018-04-04 12:03:00 -07001020 /**
1021 * Gets the time when the call is created (see {@link Details#getCreationTimeMillis()}). This is
1022 * the same time that is logged as the start time in the Call Log (see {@link
1023 * android.provider.CallLog.Calls#DATE}).
1024 */
1025 @TargetApi(26)
1026 public long getCreationTimeMillis() {
1027 return telecomCall.getDetails().getCreationTimeMillis();
1028 }
1029
Eric Erfanianccca3152017-02-22 16:32:36 -08001030 public boolean isConferenceCall() {
1031 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
1032 }
1033
1034 @Nullable
1035 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -08001036 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -08001037 }
1038
1039 @Nullable
1040 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -08001041 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -08001042 }
1043
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001044 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -08001045 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -08001046 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -08001047 }
1048
1049 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -08001050 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -08001051 }
1052
1053 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -08001054 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -08001055 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -08001056 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001057 }
1058 return null;
1059 }
1060
1061 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -08001062 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -08001063 }
1064
1065 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -07001066 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001067 }
1068
wangqi8d407a02018-02-15 15:32:52 -08001069 @TargetApi(28)
wangqif6be6172018-03-30 15:57:56 -07001070 public boolean isActiveRttCall() {
wangqi219b8702018-02-13 09:34:41 -08001071 if (BuildCompat.isAtLeastP()) {
1072 return getTelecomCall().isRttActive();
1073 } else {
1074 return false;
1075 }
1076 }
1077
wangqi153af2f2018-02-15 16:21:49 -08001078 @TargetApi(28)
wangqi49f90b32018-05-03 12:34:08 -07001079 @Nullable
wangqi153af2f2018-02-15 16:21:49 -08001080 public RttCall getRttCall() {
wangqif6be6172018-03-30 15:57:56 -07001081 if (!isActiveRttCall()) {
wangqi153af2f2018-02-15 16:21:49 -08001082 return null;
1083 }
1084 return getTelecomCall().getRttCall();
1085 }
1086
wangqif6be6172018-03-30 15:57:56 -07001087 @TargetApi(28)
wangqi810d43d2018-05-09 16:43:35 -07001088 public boolean isPhoneAccountRttCapable() {
wangqif6be6172018-03-30 15:57:56 -07001089 PhoneAccount phoneAccount = getPhoneAccount();
1090 if (phoneAccount == null) {
1091 return false;
1092 }
1093 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
1094 return false;
1095 }
wangqi810d43d2018-05-09 16:43:35 -07001096 return true;
1097 }
1098
1099 @TargetApi(28)
1100 public boolean canUpgradeToRttCall() {
1101 if (!isPhoneAccountRttCapable()) {
1102 return false;
1103 }
wangqif6be6172018-03-30 15:57:56 -07001104 if (isActiveRttCall()) {
1105 return false;
1106 }
1107 if (isVideoCall()) {
1108 return false;
1109 }
1110 if (isConferenceCall()) {
1111 return false;
1112 }
1113 if (CallList.getInstance().hasActiveRttCall()) {
1114 return false;
1115 }
1116 return true;
1117 }
1118
1119 @TargetApi(28)
1120 public void sendRttUpgradeRequest() {
1121 getTelecomCall().sendRttRequest();
1122 }
1123
wangqibc28ea72018-04-02 16:23:00 -07001124 @TargetApi(28)
1125 public void respondToRttRequest(boolean accept, int rttRequestId) {
wangqi81e373e2018-04-24 10:50:57 -07001126 Logger.get(context)
1127 .logCallImpression(
1128 accept
1129 ? DialerImpression.Type.RTT_MID_CALL_ACCEPTED
1130 : DialerImpression.Type.RTT_MID_CALL_REJECTED,
1131 getUniqueCallId(),
1132 getTimeAddedMs());
wangqibc28ea72018-04-02 16:23:00 -07001133 getTelecomCall().respondToRttRequest(rttRequestId, accept);
1134 }
1135
wangqibed9e2f2018-04-30 11:55:07 -07001136 @TargetApi(28)
1137 private void saveRttTranscript() {
1138 if (!BuildCompat.isAtLeastP()) {
1139 return;
1140 }
wangqi49f90b32018-05-03 12:34:08 -07001141 if (getRttCall() != null) {
1142 // Save any remaining text in the buffer that's not shown by UI yet.
1143 // This may happen when the call is switched to background before disconnect.
1144 try {
1145 String messageLeft = getRttCall().readImmediately();
1146 if (!TextUtils.isEmpty(messageLeft)) {
1147 rttTranscript =
1148 RttChatMessage.getRttTranscriptWithNewRemoteMessage(rttTranscript, messageLeft);
1149 }
1150 } catch (IOException e) {
1151 LogUtil.e("DialerCall.saveRttTranscript", "error when reading remaining message", e);
wangqibed9e2f2018-04-30 11:55:07 -07001152 }
wangqibed9e2f2018-04-30 11:55:07 -07001153 }
wangqi6e0d6392018-04-30 16:22:34 -07001154 // Don't save transcript if it's empty.
1155 if (rttTranscript.getMessagesCount() == 0) {
1156 return;
1157 }
wangqibed9e2f2018-04-30 11:55:07 -07001158 Futures.addCallback(
1159 RttTranscriptUtil.saveRttTranscript(context, rttTranscript),
1160 new DefaultFutureCallback<>(),
1161 MoreExecutors.directExecutor());
1162 }
1163
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001164 public boolean hasReceivedVideoUpgradeRequest() {
1165 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
1166 }
1167
1168 public boolean hasSentVideoUpgradeRequest() {
1169 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -08001170 }
1171
wangqi219b8702018-02-13 09:34:41 -08001172 public boolean hasSentRttUpgradeRequest() {
wangqi219b8702018-02-13 09:34:41 -08001173 return false;
1174 }
1175
Eric Erfanianccca3152017-02-22 16:32:36 -08001176 /**
1177 * Determines if the call handle is an emergency number or not and caches the result to avoid
1178 * repeated calls to isEmergencyNumber.
1179 */
1180 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -08001181 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -08001182 }
1183
Eric Erfanianccca3152017-02-22 16:32:36 -08001184 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -08001185 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -08001186 }
1187
1188 /**
1189 * Determines if the call is an external call.
1190 *
1191 * <p>An external call is one which does not exist locally for the {@link
1192 * android.telecom.ConnectionService} it is associated with.
1193 *
Eric Erfanianccca3152017-02-22 16:32:36 -08001194 * @return {@code true} if the call is an external call, {@code false} otherwise.
1195 */
linyuh437ae952018-03-26 12:46:18 -07001196 boolean isExternalCall() {
1197 return hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
Eric Erfanianccca3152017-02-22 16:32:36 -08001198 }
1199
1200 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001201 * Determines if answering this call will cause an ongoing video call to be dropped.
1202 *
1203 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
1204 * otherwise.
1205 */
1206 public boolean answeringDisconnectsForegroundVideoCall() {
1207 Bundle extras = getExtras();
1208 if (extras == null
1209 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
1210 return false;
1211 }
1212 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
1213 }
1214
1215 private void parseCallSpecificAppData() {
1216 if (isExternalCall()) {
1217 return;
1218 }
1219
linyuh183cb712017-12-27 17:02:37 -08001220 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
1221 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001222
linyuh183cb712017-12-27 17:02:37 -08001223 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -07001224 CallSpecificAppData.newBuilder()
1225 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
1226 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001227 }
wangqibb94ca62018-04-27 14:34:04 -07001228 if (getState() == DialerCallState.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -08001229 logState.callSpecificAppData =
1230 logState
Eric Erfanian8369df02017-05-03 10:27:13 -07001231 .callSpecificAppData
1232 .toBuilder()
1233 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
1234 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -08001235 }
1236 }
1237
1238 @Override
1239 public String toString() {
linyuh183cb712017-12-27 17:02:37 -08001240 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001241 // This should happen only in testing since otherwise we would never have a null
1242 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -08001243 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -08001244 }
1245
1246 return String.format(
1247 Locale.US,
1248 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001249 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -08001250 id,
wangqibb94ca62018-04-27 14:34:04 -07001251 DialerCallState.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -08001252 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
1253 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
1254 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -08001255 getParentId(),
linyuh183cb712017-12-27 17:02:37 -08001256 this.telecomCall.getConferenceableCalls(),
1257 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001258 getVideoTech().getSessionModificationState(),
1259 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001260 }
1261
1262 public String toSimpleString() {
1263 return super.toString();
1264 }
1265
1266 @CallHistoryStatus
1267 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001268 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001269 }
1270
1271 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001272 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001273 }
1274
1275 public boolean didShowCameraPermission() {
1276 return didShowCameraPermission;
1277 }
1278
1279 public void setDidShowCameraPermission(boolean didShow) {
1280 didShowCameraPermission = didShow;
1281 }
1282
wangqida410d32018-03-06 16:51:38 -08001283 public boolean didDismissVideoChargesAlertDialog() {
1284 return didDismissVideoChargesAlertDialog;
1285 }
1286
1287 public void setDidDismissVideoChargesAlertDialog(boolean didDismiss) {
1288 didDismissVideoChargesAlertDialog = didDismiss;
1289 }
1290
maxwelbb87334b2018-05-14 13:57:32 -07001291 public void setSpamStatus(@Nullable SpamStatus spamStatus) {
1292 this.spamStatus = spamStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001293 }
1294
maxwelbb87334b2018-05-14 13:57:32 -07001295 public Optional<SpamStatus> getSpamStatus() {
1296 return Optional.fromNullable(spamStatus);
Eric Erfanianccca3152017-02-22 16:32:36 -08001297 }
1298
1299 public boolean isSpam() {
maxwelbb87334b2018-05-14 13:57:32 -07001300 if (spamStatus == null || !spamStatus.isSpam()) {
1301 return false;
1302 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001303
maxwelbb87334b2018-05-14 13:57:32 -07001304 if (!isIncoming()) {
1305 return false;
1306 }
1307
1308 if (isPotentialEmergencyCallback()) {
1309 return false;
1310 }
1311
1312 return true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001313 }
1314
1315 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001316 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001317 }
1318
1319 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001320 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001321 }
1322
1323 public boolean isRemotelyHeld() {
1324 return isRemotelyHeld;
1325 }
1326
Eric Erfanian2ca43182017-08-31 06:57:16 -07001327 public boolean isMergeInProcess() {
1328 return isMergeInProcess;
1329 }
1330
Eric Erfanianccca3152017-02-22 16:32:36 -08001331 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001332 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001333 }
1334
erfanian2cf2c342017-12-21 12:01:33 -08001335 /**
1336 * Try and determine if the call used assisted dialing.
1337 *
1338 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1339 * implmentation is complete in P+.
1340 *
1341 * @return a boolean indicating assisted dialing may have been performed
1342 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001343 public boolean isAssistedDialed() {
1344 if (getIntentExtras() != null) {
erfaniand2e5d0b2018-03-02 14:54:35 -08001345 // P and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
erfanian2cf2c342017-12-21 12:01:33 -08001346 // was used. The Dialer client is responsible for performing assisted dialing before
1347 // placing the outgoing call.
1348 //
1349 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1350 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1351 && getAssistedDialingExtras() != null
1352 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1353 return true;
1354 }
1355 }
1356
Eric Erfanian2ca43182017-08-31 06:57:16 -07001357 return false;
1358 }
1359
erfanian2cf2c342017-12-21 12:01:33 -08001360 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001361 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001362 if (getIntentExtras() == null) {
1363 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001364 }
erfanian2cf2c342017-12-21 12:01:33 -08001365
1366 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1367 return null;
1368 }
1369
erfanianf2556612018-01-23 09:55:59 -08001370 // Used in N-OMR1
erfanian2cf2c342017-12-21 12:01:33 -08001371 return TransformationInfo.newInstanceFromBundle(
1372 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001373 }
1374
Eric Erfanianccca3152017-02-22 16:32:36 -08001375 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001376 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001377 }
1378
Eric Erfanian2ca43182017-08-31 06:57:16 -07001379 public int getAnswerAndReleaseButtonDisplayedTimes() {
1380 return answerAndReleaseButtonDisplayedTimes;
1381 }
1382
1383 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1384 answerAndReleaseButtonDisplayedTimes++;
1385 }
1386
1387 public boolean getReleasedByAnsweringSecondCall() {
1388 return releasedByAnsweringSecondCall;
1389 }
1390
1391 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1392 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1393 }
1394
1395 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1396 return secondCallWithoutAnswerAndReleasedButtonTimes;
1397 }
1398
1399 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1400 secondCallWithoutAnswerAndReleasedButtonTimes++;
1401 }
1402
Eric Erfanian8369df02017-05-03 10:27:13 -07001403 @Nullable
1404 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001405 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001406 }
1407
1408 public void setEnrichedCallCapabilities(
1409 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001410 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001411 }
1412
1413 @Nullable
1414 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001415 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001416 }
1417
1418 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001419 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001420 }
1421
Eric Erfanianccca3152017-02-22 16:32:36 -08001422 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001423 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001424 }
1425
Eric Erfanianccca3152017-02-22 16:32:36 -08001426 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1427 LogUtil.i(
1428 "DialerCall.phoneAccountSelected",
1429 "accountHandle: %s, setDefault: %b",
1430 accountHandle,
1431 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001432 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001433 }
1434
1435 public void disconnect() {
1436 LogUtil.i("DialerCall.disconnect", "");
wangqibb94ca62018-04-27 14:34:04 -07001437 setState(DialerCallState.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001438 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001439 listener.onDialerCallUpdate();
1440 }
linyuh183cb712017-12-27 17:02:37 -08001441 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001442 }
1443
1444 public void hold() {
1445 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001446 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001447 }
1448
1449 public void unhold() {
1450 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001451 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001452 }
1453
1454 public void splitFromConference() {
1455 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001456 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001457 }
1458
1459 public void answer(int videoState) {
1460 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001461 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001462 }
1463
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001464 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001465 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001466 }
1467
Eric Erfanianccca3152017-02-22 16:32:36 -08001468 public void reject(boolean rejectWithMessage, String message) {
1469 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001470 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001471 }
1472
1473 /** Return the string label to represent the call provider */
1474 public String getCallProviderLabel() {
1475 if (callProviderLabel == null) {
1476 PhoneAccount account = getPhoneAccount();
1477 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001478 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001479 callProviderLabel = account.getLabel().toString();
1480 }
1481 }
1482 if (callProviderLabel == null) {
1483 callProviderLabel = "";
1484 }
1485 }
1486 return callProviderLabel;
1487 }
1488
1489 private PhoneAccount getPhoneAccount() {
1490 PhoneAccountHandle accountHandle = getAccountHandle();
1491 if (accountHandle == null) {
1492 return null;
1493 }
linyuh183cb712017-12-27 17:02:37 -08001494 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001495 }
1496
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001497 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001498 if (videoTech == null) {
roldenburg6bd612f2018-01-18 12:57:19 -08001499 videoTech = videoTechManager.getVideoTech(getAccountHandle());
roldenburg7bb96232017-10-09 10:32:05 -07001500
1501 // Only store the first video tech type found to be available during the life of the call.
1502 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1503 // Update the video tech.
1504 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1505 }
1506 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001507 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001508 }
1509
Eric Erfanianccca3152017-02-22 16:32:36 -08001510 public String getCallbackNumber() {
1511 if (callbackNumber == null) {
1512 // Show the emergency callback number if either:
1513 // 1. This is an emergency call.
1514 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1515 // number.
1516 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1517
1518 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001519 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001520 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001521 }
1522
Eric Erfanianccca3152017-02-22 16:32:36 -08001523 if (callbackNumber == null) {
1524 callbackNumber = "";
1525 }
1526 }
1527 return callbackNumber;
1528 }
1529
wangqi97539352017-09-25 11:15:16 -07001530 public String getSimCountryIso() {
1531 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001532 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001533 .getSimCountryIso();
1534 if (!TextUtils.isEmpty(simCountryIso)) {
1535 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1536 }
1537 return simCountryIso;
1538 }
1539
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001540 @Override
1541 public void onVideoTechStateChanged() {
1542 update();
1543 }
1544
1545 @Override
1546 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001547 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001548 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001549 listener.onDialerCallSessionModificationStateChange();
1550 }
wangqi9982f0d2017-10-11 17:46:07 -07001551 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001552 }
1553
1554 @Override
1555 public void onCameraDimensionsChanged(int width, int height) {
1556 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1557 }
1558
1559 @Override
1560 public void onPeerDimensionsChanged(int width, int height) {
1561 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1562 }
1563
1564 @Override
1565 public void onVideoUpgradeRequestReceived() {
1566 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1567
linyuh183cb712017-12-27 17:02:37 -08001568 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001569 listener.onDialerCallUpgradeToVideo();
1570 }
1571
1572 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001573
linyuh183cb712017-12-27 17:02:37 -08001574 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001575 .logCallImpression(
1576 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1577 }
1578
1579 @Override
1580 public void onUpgradedToVideo(boolean switchToSpeaker) {
1581 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1582
1583 if (!switchToSpeaker) {
1584 return;
1585 }
1586
1587 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1588
1589 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1590 LogUtil.e(
1591 "DialerCall.onUpgradedToVideo",
1592 "toggling speakerphone not allowed when bluetooth supported.");
1593 return;
1594 }
1595
1596 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1597 return;
1598 }
1599
1600 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001601 }
1602
Eric Erfanian2ca43182017-08-31 06:57:16 -07001603 @Override
1604 public void onCapabilitiesUpdated() {
1605 if (getNumber() == null) {
1606 return;
1607 }
1608 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001609 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001610 if (capabilities != null) {
1611 setEnrichedCallCapabilities(capabilities);
1612 update();
1613 }
1614 }
1615
1616 @Override
1617 public void onEnrichedCallStateChanged() {
1618 updateEnrichedCallSession();
1619 }
1620
1621 @Override
1622 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001623 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001624 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1625 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001626 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001627 .logCallImpression(
1628 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1629 getUniqueCallId(),
1630 getTimeAddedMs());
1631 }
1632 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001633 }
1634
1635 private void updateEnrichedCallSession() {
1636 if (getNumber() == null) {
1637 return;
1638 }
1639 if (getEnrichedCallSession() != null) {
1640 // State changes to existing sessions are currently handled by the UI components (which have
1641 // their own listeners). Someday instead we could remove those and just call update() here and
1642 // have the usual onDialerCallUpdate update the UI.
1643 dispatchOnEnrichedCallSessionUpdate();
1644 return;
1645 }
1646
linyuh183cb712017-12-27 17:02:37 -08001647 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001648
1649 Filter filter =
1650 isIncoming()
1651 ? manager.createIncomingCallComposerFilter()
1652 : manager.createOutgoingCallComposerFilter();
1653
1654 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1655 if (session == null) {
1656 return;
1657 }
1658
1659 session.setUniqueDialerCallId(getUniqueCallId());
1660 setEnrichedCallSession(session);
1661
1662 LogUtil.i(
1663 "DialerCall.updateEnrichedCallSession",
1664 "setting session %d's dialer id to %s",
1665 session.getSessionId(),
1666 getUniqueCallId());
1667
1668 dispatchOnEnrichedCallSessionUpdate();
1669 }
1670
1671 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001672 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001673 listener.onEnrichedCallSessionUpdate();
1674 }
1675 }
1676
1677 void onRemovedFromCallList() {
wangqif1861052018-04-19 15:06:06 -07001678 LogUtil.enterBlock("DialerCall.onRemovedFromCallList");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001679 // Ensure we clean up when this call is removed.
wangqia0c49f62018-04-25 15:04:01 -07001680 if (videoTechManager != null) {
1681 videoTechManager.dispatchRemovedFromCallList();
1682 }
wangqibed9e2f2018-04-30 11:55:07 -07001683 // TODO(wangqi): Consider moving this to a DialerCallListener.
wangqia0c49f62018-04-25 15:04:01 -07001684 if (rttTranscript != null && !isCallRemoved) {
wangqibed9e2f2018-04-30 11:55:07 -07001685 saveRttTranscript();
wangqif1861052018-04-19 15:06:06 -07001686 }
wangqia0c49f62018-04-25 15:04:01 -07001687 isCallRemoved = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001688 }
1689
wangqi4d705e52017-09-28 12:23:35 -07001690 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1691 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001692 }
1693
Android Dialer974fc292018-02-01 16:12:25 -08001694 public void markFeedbackRequested() {
1695 feedbackRequested = true;
1696 }
1697
1698 public boolean isFeedbackRequested() {
1699 return feedbackRequested;
1700 }
1701
Eric Erfanianccca3152017-02-22 16:32:36 -08001702 /**
twyen73a74c32018-03-07 12:12:24 -08001703 * If the in call UI has shown the phone account selection dialog for the call, the {@link
1704 * PreferredAccountRecorder} to record the result from the dialog.
1705 */
1706 @Nullable
1707 public PreferredAccountRecorder getPreferredAccountRecorder() {
1708 return preferredAccountRecorder;
1709 }
1710
1711 public void setPreferredAccountRecorder(PreferredAccountRecorder preferredAccountRecorder) {
1712 this.preferredAccountRecorder = preferredAccountRecorder;
1713 }
1714
erfaniand05d8992018-03-20 19:42:26 -07001715 /** Indicates the call is eligible for SpeakEasy */
1716 public boolean isSpeakEasyEligible() {
erfanian61dc9392018-06-15 14:10:34 -07001717
1718 PhoneAccount phoneAccount = getPhoneAccount();
1719
1720 if (phoneAccount == null) {
1721 return false;
1722 }
1723
1724 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
1725 return false;
1726 }
1727
erfaniandcc84cd2018-04-17 12:41:05 -07001728 return !isPotentialEmergencyCallback()
1729 && !isEmergencyCall()
1730 && !isActiveRttCall()
1731 && !isConferenceCall()
1732 && !isVideoCall()
erfanian1f485742018-06-20 11:56:25 -07001733 && !isVoiceMailNumber()
1734 && !hasReceivedVideoUpgradeRequest();
erfaniand05d8992018-03-20 19:42:26 -07001735 }
1736
1737 /** Indicates the user has selected SpeakEasy */
1738 public boolean isSpeakEasyCall() {
1739 if (!isSpeakEasyEligible()) {
1740 return false;
1741 }
1742 return isSpeakEasyCall;
1743 }
1744
1745 /** Sets the user preference for SpeakEasy */
1746 public void setIsSpeakEasyCall(boolean isSpeakEasyCall) {
1747 this.isSpeakEasyCall = isSpeakEasyCall;
erfanian3887ab02018-05-11 11:02:53 -07001748 if (listeners != null) {
1749 for (DialerCallListener listener : listeners) {
1750 listener.onDialerCallSpeakEasyStateChange();
1751 }
1752 }
erfaniand05d8992018-03-20 19:42:26 -07001753 }
1754
twyen73a74c32018-03-07 12:12:24 -08001755 /**
Eric Erfanianccca3152017-02-22 16:32:36 -08001756 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1757 * means there is no result.
1758 */
1759 @IntDef({
1760 CALL_HISTORY_STATUS_UNKNOWN,
1761 CALL_HISTORY_STATUS_PRESENT,
1762 CALL_HISTORY_STATUS_NOT_PRESENT
1763 })
1764 @Retention(RetentionPolicy.SOURCE)
1765 public @interface CallHistoryStatus {}
1766
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001767 /** Camera direction constants */
1768 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001769 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1770 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1771 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001772 }
1773
1774 /**
1775 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1776 * existing call member variables, but this duplication helps to ensure that none of these logging
1777 * variables will interface with/and affect call logic.
1778 */
1779 public static class LogState {
1780
1781 public DisconnectCause disconnectCause;
1782 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001783 public ContactLookupResult.Type contactLookupResult =
1784 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001785 public CallSpecificAppData callSpecificAppData;
1786 // If this was a conference call, the total number of calls involved in the conference.
1787 public int conferencedCalls = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -08001788 public boolean isLogged = false;
1789
maxwelbd95d0f62018-05-01 17:50:55 -07001790 // Result of subtracting android.telecom.Call.Details#getConnectTimeMillis from the current time
1791 public long telecomDurationMillis = 0;
1792
1793 // Result of a call to System.currentTimeMillis when Dialer sees that a call
1794 // moves to the ACTIVE state
1795 long dialerConnectTimeMillis = 0;
1796
1797 // Same as dialer_connect_time_millis, using SystemClock.elapsedRealtime
1798 // instead
1799 long dialerConnectTimeMillisElapsedRealtime = 0;
1800
1801 // Result of subtracting dialer_connect_time_millis from System.currentTimeMillis
1802 public long dialerDurationMillis = 0;
1803
1804 // Same as dialerDurationMillis, using SystemClock.elapsedRealtime instead
1805 public long dialerDurationMillisElapsedRealtime = 0;
1806
Eric Erfanian8369df02017-05-03 10:27:13 -07001807 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001808 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001809 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001810 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001811 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001812 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001813 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001814 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001815 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001816 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001817 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001818 return "Voicemail";
1819 default:
1820 return "Not found";
1821 }
1822 }
1823
1824 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1825 if (callSpecificAppData == null) {
1826 return "null";
1827 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001828 switch (callSpecificAppData.getCallInitiationType()) {
1829 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001830 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001831 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001832 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001833 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001834 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001835 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001836 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001837 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001838 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001839 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001840 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001841 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001842 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001843 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001844 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001845 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001846 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001847 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001848 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001849 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001850 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001851 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001852 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001853 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001854 return "Launcher Shortcut";
1855 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001856 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001857 }
1858 }
1859
1860 @Override
1861 public String toString() {
1862 return String.format(
1863 Locale.US,
1864 "["
1865 + "%s, " // DisconnectCause toString already describes the object type
1866 + "isIncoming: %s, "
1867 + "contactLookup: %s, "
1868 + "callInitiation: %s, "
1869 + "duration: %s"
1870 + "]",
1871 disconnectCause,
1872 isIncoming,
1873 lookupToString(contactLookupResult),
1874 initiationToString(callSpecificAppData),
maxwelbd95d0f62018-05-01 17:50:55 -07001875 telecomDurationMillis);
Eric Erfanianccca3152017-02-22 16:32:36 -08001876 }
1877 }
1878
roldenburgd7490db2018-01-09 13:51:29 -08001879 /** Coordinates the available VideoTech implementations for a call. */
1880 @VisibleForTesting
1881 public static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001882 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001883 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
roldenburgd7490db2018-01-09 13:51:29 -08001884 private final VideoTech rcsVideoShare;
Eric Erfanian90508232017-03-24 09:31:16 -07001885 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001886 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001887
roldenburgd7490db2018-01-09 13:51:29 -08001888 @VisibleForTesting
1889 public VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001890 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001891
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001892 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001893 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001894 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001895
1896 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001897 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001898
linyuh183cb712017-12-27 17:02:37 -08001899 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001900
roldenburgd7490db2018-01-09 13:51:29 -08001901 rcsVideoShare =
linyuh183cb712017-12-27 17:02:37 -08001902 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001903 .getRcsVideoShareFactory()
1904 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001905 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001906 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001907 phoneNumber);
roldenburg3eca69f2018-01-16 12:07:04 -08001908 videoTechs.add(rcsVideoShare);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001909
1910 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001911 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001912 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
roldenburgd7490db2018-01-09 13:51:29 -08001913
1914 savedTech = emptyVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001915 }
1916
roldenburgd7490db2018-01-09 13:51:29 -08001917 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001918 public VideoTech getVideoTech(PhoneAccountHandle phoneAccountHandle) {
roldenburgd7490db2018-01-09 13:51:29 -08001919 if (savedTech == emptyVideoTech) {
1920 for (VideoTech tech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001921 if (tech.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001922 savedTech = tech;
1923 savedTech.becomePrimary();
1924 break;
1925 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001926 }
roldenburg6bd612f2018-01-18 12:57:19 -08001927 } else if (savedTech instanceof DuoVideoTech
1928 && rcsVideoShare.isAvailable(context, phoneAccountHandle)) {
roldenburgd7490db2018-01-09 13:51:29 -08001929 // RCS Video Share will become available after the capability exchange which is slower than
1930 // Duo reading local contacts for reachability. If Video Share becomes available and we are
1931 // not in the middle of any session changes, let it take over.
1932 savedTech = rcsVideoShare;
1933 rcsVideoShare.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001934 }
1935
roldenburgd7490db2018-01-09 13:51:29 -08001936 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001937 }
1938
roldenburgd7490db2018-01-09 13:51:29 -08001939 @VisibleForTesting
roldenburg6bd612f2018-01-18 12:57:19 -08001940 public void dispatchCallStateChanged(int newState, PhoneAccountHandle phoneAccountHandle) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001941 for (VideoTech videoTech : videoTechs) {
roldenburg6bd612f2018-01-18 12:57:19 -08001942 videoTech.onCallStateChanged(context, newState, phoneAccountHandle);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001943 }
1944 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001945
1946 void dispatchRemovedFromCallList() {
1947 for (VideoTech videoTech : videoTechs) {
1948 videoTech.onRemovedFromCallList();
1949 }
1950 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001951 }
1952
Eric Erfanianccca3152017-02-22 16:32:36 -08001953 /** Called when canned text responses have been loaded. */
1954 public interface CannedTextResponsesLoadedListener {
1955 void onCannedTextResponsesLoaded(DialerCall call);
1956 }
1957}