blob: 543cc3f0723aa6452dc8efa263bb080e440e8e46 [file] [log] [blame]
Eric Erfanianccca3152017-02-22 16:32:36 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.incallui.call;
18
wangqi9982f0d2017-10-11 17:46:07 -070019import android.Manifest.permission;
Eric Erfanianccca3152017-02-22 16:32:36 -080020import android.content.Context;
21import android.hardware.camera2.CameraCharacteristics;
22import android.net.Uri;
erfanian2cf2c342017-12-21 12:01:33 -080023import android.os.Build;
Eric Erfanianccca3152017-02-22 16:32:36 -080024import android.os.Build.VERSION;
25import android.os.Build.VERSION_CODES;
26import android.os.Bundle;
27import android.os.Trace;
28import android.support.annotation.IntDef;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070029import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080030import android.support.annotation.Nullable;
twyendde01c52017-09-22 10:07:31 -070031import android.support.annotation.VisibleForTesting;
Eric Erfanianccca3152017-02-22 16:32:36 -080032import android.telecom.Call;
33import android.telecom.Call.Details;
Eric Erfanian8369df02017-05-03 10:27:13 -070034import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080035import android.telecom.Connection;
36import android.telecom.DisconnectCause;
37import android.telecom.GatewayInfo;
38import android.telecom.InCallService.VideoCall;
39import android.telecom.PhoneAccount;
40import android.telecom.PhoneAccountHandle;
41import android.telecom.StatusHints;
42import android.telecom.TelecomManager;
43import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080044import android.text.TextUtils;
45import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080046import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
erfanian2cf2c342017-12-21 12:01:33 -080047import com.android.dialer.assisteddialing.ConcreteCreator;
erfaniand0f207f2017-10-11 12:23:29 -070048import com.android.dialer.assisteddialing.TransformationInfo;
Eric Erfanian8369df02017-05-03 10:27:13 -070049import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080050import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070051import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080052import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080053import com.android.dialer.common.LogUtil;
Eric Erfanian2ca43182017-08-31 06:57:16 -070054import com.android.dialer.compat.telephony.TelephonyManagerCompat;
55import com.android.dialer.configprovider.ConfigProviderBindings;
roldenburg4f026392017-10-13 18:42:20 -070056import com.android.dialer.duo.DuoComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070057import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070058import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070059import com.android.dialer.enrichedcall.EnrichedCallManager;
60import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
61import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
62import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070063import com.android.dialer.enrichedcall.Session;
wangqi9982f0d2017-10-11 17:46:07 -070064import com.android.dialer.location.GeoUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070065import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070066import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070067import com.android.dialer.logging.DialerImpression;
68import com.android.dialer.logging.Logger;
twyena4745bd2017-12-12 18:40:11 -080069import com.android.dialer.telecom.TelecomCallUtil;
wangqi9982f0d2017-10-11 17:46:07 -070070import com.android.dialer.telecom.TelecomUtil;
Eric Erfanianc857f902017-05-15 14:05:33 -070071import com.android.dialer.theme.R;
wangqi9982f0d2017-10-11 17:46:07 -070072import com.android.dialer.util.PermissionsUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070073import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080074import com.android.incallui.latencyreport.LatencyReport;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070075import com.android.incallui.videotech.VideoTech;
76import com.android.incallui.videotech.VideoTech.VideoTechListener;
roldenburg4f026392017-10-13 18:42:20 -070077import com.android.incallui.videotech.duo.DuoVideoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070078import com.android.incallui.videotech.empty.EmptyVideoTech;
79import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfanian90508232017-03-24 09:31:16 -070080import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080081import java.lang.annotation.Retention;
82import java.lang.annotation.RetentionPolicy;
83import java.util.ArrayList;
84import java.util.List;
85import java.util.Locale;
86import java.util.Objects;
87import java.util.UUID;
88import java.util.concurrent.CopyOnWriteArrayList;
89import java.util.concurrent.TimeUnit;
90
91/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -070092public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -080093
94 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
95 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
96 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070097
98 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
Eric Erfanian938468d2017-10-24 14:05:52 -070099 // TODO(a bug): Move it to Telecom in framework.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700100 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
101
Eric Erfanianccca3152017-02-22 16:32:36 -0800102 private static final String ID_PREFIX = "DialerCall_";
103 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
104 "emergency_callback_window_millis";
linyuh183cb712017-12-27 17:02:37 -0800105 private static int idCounter = 0;
Eric Erfanianccca3152017-02-22 16:32:36 -0800106
107 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700108 * A counter used to append to restricted/private/hidden calls so that users can identify them in
109 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
110 * are no live calls.
111 */
linyuh183cb712017-12-27 17:02:37 -0800112 private static int hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700113
114 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800115 * The unique call ID for every call. This will help us to identify each call and allow us the
116 * ability to stitch impressions to calls if needed.
117 */
118 private final String uniqueCallId = UUID.randomUUID().toString();
119
linyuh183cb712017-12-27 17:02:37 -0800120 private final Call telecomCall;
121 private final LatencyReport latencyReport;
122 private final String id;
123 private final int hiddenId;
124 private final List<String> childCallIds = new ArrayList<>();
125 private final LogState logState = new LogState();
126 private final Context context;
127 private final DialerCallDelegate dialerCallDelegate;
128 private final List<DialerCallListener> listeners = new CopyOnWriteArrayList<>();
129 private final List<CannedTextResponsesLoadedListener> cannedTextResponsesLoadedListeners =
Eric Erfanianccca3152017-02-22 16:32:36 -0800130 new CopyOnWriteArrayList<>();
linyuh183cb712017-12-27 17:02:37 -0800131 private final VideoTechManager videoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800132
linyuh183cb712017-12-27 17:02:37 -0800133 private boolean isEmergencyCall;
134 private Uri handle;
135 private int state = State.INVALID;
136 private DisconnectCause disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800137
138 private boolean hasShownWiFiToLteHandoverToast;
139 private boolean doNotShowDialogForHandoffToWifiFailure;
140
linyuh183cb712017-12-27 17:02:37 -0800141 private String childNumber;
142 private String lastForwardedNumber;
143 private String callSubject;
144 private PhoneAccountHandle phoneAccountHandle;
145 @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
146 private boolean isSpam;
147 private boolean isBlocked;
Eric Erfanian938468d2017-10-24 14:05:52 -0700148
149 @Nullable private Boolean isInUserSpamList;
150
151 @Nullable private Boolean isInUserWhiteList;
152
153 @Nullable private Boolean isInGlobalSpamList;
Eric Erfanianccca3152017-02-22 16:32:36 -0800154 private boolean didShowCameraPermission;
155 private String callProviderLabel;
156 private String callbackNumber;
linyuh183cb712017-12-27 17:02:37 -0800157 private int cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
158 private EnrichedCallCapabilities enrichedCallCapabilities;
159 private Session enrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800160
Eric Erfanian2ca43182017-08-31 06:57:16 -0700161 private int answerAndReleaseButtonDisplayedTimes = 0;
162 private boolean releasedByAnsweringSecondCall = false;
163 // Times when a second call is received but AnswerAndRelease button is not shown
164 // since it's not supported.
165 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
roldenburg7bb96232017-10-09 10:32:05 -0700166 private VideoTech videoTech;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700167
wangqi4d705e52017-09-28 12:23:35 -0700168 private com.android.dialer.logging.VideoTech.Type selectedAvailableVideoTechType =
169 com.android.dialer.logging.VideoTech.Type.NONE;
wangqi9982f0d2017-10-11 17:46:07 -0700170 private boolean isVoicemailNumber;
171 private List<PhoneAccountHandle> callCapableAccounts;
172 private String countryIso;
yueg457b3972017-09-18 15:11:47 -0700173
Eric Erfanianccca3152017-02-22 16:32:36 -0800174 public static String getNumberFromHandle(Uri handle) {
175 return handle == null ? "" : handle.getSchemeSpecificPart();
176 }
177
178 /**
179 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700180 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800181 */
182 private boolean isRemotelyHeld;
183
Eric Erfanian2ca43182017-08-31 06:57:16 -0700184 /** Indicates whether this call is currently in the process of being merged into a conference. */
185 private boolean isMergeInProcess;
186
Eric Erfanianccca3152017-02-22 16:32:36 -0800187 /**
188 * Indicates whether the phone account associated with this call supports specifying a call
189 * subject.
190 */
linyuh183cb712017-12-27 17:02:37 -0800191 private boolean isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800192
linyuh183cb712017-12-27 17:02:37 -0800193 private final Call.Callback telecomCallCallback =
Eric Erfanianccca3152017-02-22 16:32:36 -0800194 new Call.Callback() {
195 @Override
196 public void onStateChanged(Call call, int newState) {
197 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
198 update();
199 }
200
201 @Override
202 public void onParentChanged(Call call, Call newParent) {
203 LogUtil.v(
204 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
205 update();
206 }
207
208 @Override
209 public void onChildrenChanged(Call call, List<Call> children) {
210 update();
211 }
212
213 @Override
214 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700215 LogUtil.v(
216 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800217 update();
218 }
219
220 @Override
221 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
222 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700223 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800224 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
linyuh183cb712017-12-27 17:02:37 -0800225 for (CannedTextResponsesLoadedListener listener : cannedTextResponsesLoadedListeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800226 listener.onCannedTextResponsesLoaded(DialerCall.this);
227 }
228 }
229
230 @Override
231 public void onPostDialWait(Call call, String remainingPostDialSequence) {
232 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700233 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800234 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
235 update();
236 }
237
238 @Override
239 public void onVideoCallChanged(Call call, VideoCall videoCall) {
240 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700241 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800242 update();
243 }
244
245 @Override
246 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700247 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700248 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800249 }
250
251 @Override
252 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
253 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700254 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800255 "call %s, conferenceable calls: %d",
256 call,
257 conferenceableCalls.size());
258 update();
259 }
260
261 @Override
262 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
263 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700264 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800265 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
266 switch (event) {
267 // The Previous attempt to Merge two calls together has failed in Telecom. We must
268 // now update the UI to possibly re-enable the Merge button based on the number of
269 // currently conferenceable calls available or Connection Capabilities.
270 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
271 update();
272 break;
273 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
274 notifyWiFiToLteHandover();
275 break;
276 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
277 notifyHandoverToWifiFailed();
278 break;
279 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
280 isRemotelyHeld = true;
281 update();
282 break;
283 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
284 isRemotelyHeld = false;
285 update();
286 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700287 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
288 notifyInternationalCallOnWifi();
289 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700290 case TelephonyManagerCompat.EVENT_MERGE_START:
291 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
292 isMergeInProcess = true;
293 break;
294 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
295 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
296 isMergeInProcess = false;
297 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800298 default:
299 break;
300 }
301 }
302 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700303
linyuh183cb712017-12-27 17:02:37 -0800304 private long timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800305
306 public DialerCall(
307 Context context,
308 DialerCallDelegate dialerCallDelegate,
309 Call telecomCall,
310 LatencyReport latencyReport,
311 boolean registerCallback) {
312 Assert.isNotNull(context);
linyuh183cb712017-12-27 17:02:37 -0800313 this.context = context;
314 this.dialerCallDelegate = dialerCallDelegate;
315 this.telecomCall = telecomCall;
316 this.latencyReport = latencyReport;
317 id = ID_PREFIX + Integer.toString(idCounter++);
Eric Erfanianccca3152017-02-22 16:32:36 -0800318
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700319 // Must be after assigning mTelecomCall
linyuh183cb712017-12-27 17:02:37 -0800320 videoTechManager = new VideoTechManager(this);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700321
322 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700323 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
linyuh183cb712017-12-27 17:02:37 -0800324 hiddenId = ++hiddenCounter;
Eric Erfanianc857f902017-05-15 14:05:33 -0700325 } else {
linyuh183cb712017-12-27 17:02:37 -0800326 hiddenId = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700327 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800328
329 if (registerCallback) {
linyuh183cb712017-12-27 17:02:37 -0800330 this.telecomCall.registerCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -0800331 }
332
linyuh183cb712017-12-27 17:02:37 -0800333 timeAddedMs = System.currentTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800334 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700335
336 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800337 }
338
twyendde01c52017-09-22 10:07:31 -0700339 /** Test only constructor to avoid initializing dependencies. */
340 @VisibleForTesting
341 DialerCall(Context context) {
linyuh183cb712017-12-27 17:02:37 -0800342 this.context = context;
343 telecomCall = null;
344 latencyReport = null;
345 id = null;
346 hiddenId = 0;
347 dialerCallDelegate = null;
348 videoTechManager = null;
twyendde01c52017-09-22 10:07:31 -0700349 }
350
Eric Erfanianccca3152017-02-22 16:32:36 -0800351 private static int translateState(int state) {
352 switch (state) {
353 case Call.STATE_NEW:
354 case Call.STATE_CONNECTING:
355 return DialerCall.State.CONNECTING;
356 case Call.STATE_SELECT_PHONE_ACCOUNT:
357 return DialerCall.State.SELECT_PHONE_ACCOUNT;
358 case Call.STATE_DIALING:
359 return DialerCall.State.DIALING;
360 case Call.STATE_PULLING_CALL:
361 return DialerCall.State.PULLING;
362 case Call.STATE_RINGING:
363 return DialerCall.State.INCOMING;
364 case Call.STATE_ACTIVE:
365 return DialerCall.State.ACTIVE;
366 case Call.STATE_HOLDING:
367 return DialerCall.State.ONHOLD;
368 case Call.STATE_DISCONNECTED:
369 return DialerCall.State.DISCONNECTED;
370 case Call.STATE_DISCONNECTING:
371 return DialerCall.State.DISCONNECTING;
372 default:
373 return DialerCall.State.INVALID;
374 }
375 }
376
377 public static boolean areSame(DialerCall call1, DialerCall call2) {
378 if (call1 == null && call2 == null) {
379 return true;
380 } else if (call1 == null || call2 == null) {
381 return false;
382 }
383
384 // otherwise compare call Ids
385 return call1.getId().equals(call2.getId());
386 }
387
388 public static boolean areSameNumber(DialerCall call1, DialerCall call2) {
389 if (call1 == null && call2 == null) {
390 return true;
391 } else if (call1 == null || call2 == null) {
392 return false;
393 }
394
395 // otherwise compare call Numbers
396 return TextUtils.equals(call1.getNumber(), call2.getNumber());
397 }
398
399 public void addListener(DialerCallListener listener) {
400 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800401 listeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800402 }
403
404 public void removeListener(DialerCallListener listener) {
405 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800406 listeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800407 }
408
409 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
410 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800411 cannedTextResponsesLoadedListeners.add(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800412 }
413
414 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
415 Assert.isMainThread();
linyuh183cb712017-12-27 17:02:37 -0800416 cannedTextResponsesLoadedListeners.remove(listener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800417 }
418
419 public void notifyWiFiToLteHandover() {
420 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
linyuh183cb712017-12-27 17:02:37 -0800421 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800422 listener.onWiFiToLteHandover();
423 }
424 }
425
426 public void notifyHandoverToWifiFailed() {
427 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
linyuh183cb712017-12-27 17:02:37 -0800428 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800429 listener.onHandoverToWifiFailure();
430 }
431 }
432
Eric Erfanianc857f902017-05-15 14:05:33 -0700433 public void notifyInternationalCallOnWifi() {
434 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
linyuh183cb712017-12-27 17:02:37 -0800435 for (DialerCallListener dialerCallListener : listeners) {
Eric Erfanianc857f902017-05-15 14:05:33 -0700436 dialerCallListener.onInternationalCallOnWifi();
437 }
438 }
439
Eric Erfanianccca3152017-02-22 16:32:36 -0800440 /* package-private */ Call getTelecomCall() {
linyuh183cb712017-12-27 17:02:37 -0800441 return telecomCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800442 }
443
444 public StatusHints getStatusHints() {
linyuh183cb712017-12-27 17:02:37 -0800445 return telecomCall.getDetails().getStatusHints();
Eric Erfanianccca3152017-02-22 16:32:36 -0800446 }
447
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700448 public int getCameraDir() {
linyuh183cb712017-12-27 17:02:37 -0800449 return cameraDirection;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700450 }
451
452 public void setCameraDir(int cameraDir) {
453 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
454 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
linyuh183cb712017-12-27 17:02:37 -0800455 cameraDirection = cameraDir;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700456 } else {
linyuh183cb712017-12-27 17:02:37 -0800457 cameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700458 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800459 }
460
Eric Erfanian2ca43182017-08-31 06:57:16 -0700461 public boolean wasParentCall() {
linyuh183cb712017-12-27 17:02:37 -0800462 return logState.conferencedCalls != 0;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700463 }
464
wangqi9982f0d2017-10-11 17:46:07 -0700465 public boolean isVoiceMailNumber() {
466 return isVoicemailNumber;
467 }
468
469 public List<PhoneAccountHandle> getCallCapableAccounts() {
470 return callCapableAccounts;
471 }
472
473 public String getCountryIso() {
474 return countryIso;
475 }
476
477 private void updateIsVoiceMailNumber() {
478 if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
479 isVoicemailNumber = true;
480 }
481
linyuh183cb712017-12-27 17:02:37 -0800482 if (!PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700483 isVoicemailNumber = false;
484 }
485
linyuh183cb712017-12-27 17:02:37 -0800486 isVoicemailNumber = TelecomUtil.isVoicemailNumber(context, getAccountHandle(), getNumber());
wangqi9982f0d2017-10-11 17:46:07 -0700487 }
488
Eric Erfanianccca3152017-02-22 16:32:36 -0800489 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700490 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800491 int oldState = getState();
roldenburg7bb96232017-10-09 10:32:05 -0700492 // Clear any cache here that could potentially change on update.
493 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800494 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700495 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800496 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
linyuh183cb712017-12-27 17:02:37 -0800497 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800498 listener.onDialerCallDisconnect();
499 }
linyuh183cb712017-12-27 17:02:37 -0800500 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700501 .getEnrichedCallManager()
502 .unregisterCapabilitiesListener(this);
linyuh183cb712017-12-27 17:02:37 -0800503 EnrichedCallComponent.get(context)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700504 .getEnrichedCallManager()
505 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800506 } else {
linyuh183cb712017-12-27 17:02:37 -0800507 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800508 listener.onDialerCallUpdate();
509 }
510 }
511 Trace.endSection();
512 }
513
wangqi9982f0d2017-10-11 17:46:07 -0700514 @SuppressWarnings("MissingPermission")
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700515 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700516 Trace.beginSection("DialerCall.updateFromTelecomCall");
linyuh183cb712017-12-27 17:02:37 -0800517 LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700518
linyuh183cb712017-12-27 17:02:37 -0800519 videoTechManager.dispatchCallStateChanged(telecomCall.getState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700520
linyuh183cb712017-12-27 17:02:37 -0800521 final int translatedState = translateState(telecomCall.getState());
522 if (state != State.BLOCKED) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800523 setState(translatedState);
linyuh183cb712017-12-27 17:02:37 -0800524 setDisconnectCause(telecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800525 }
526
linyuh183cb712017-12-27 17:02:37 -0800527 childCallIds.clear();
528 final int numChildCalls = telecomCall.getChildren().size();
Eric Erfanianccca3152017-02-22 16:32:36 -0800529 for (int i = 0; i < numChildCalls; i++) {
linyuh183cb712017-12-27 17:02:37 -0800530 childCallIds.add(
531 dialerCallDelegate
532 .getDialerCallFromTelecomCall(telecomCall.getChildren().get(i))
Eric Erfanianccca3152017-02-22 16:32:36 -0800533 .getId());
534 }
535
536 // The number of conferenced calls can change over the course of the call, so use the
537 // maximum number of conferenced child calls as the metric for conference call usage.
linyuh183cb712017-12-27 17:02:37 -0800538 logState.conferencedCalls = Math.max(numChildCalls, logState.conferencedCalls);
Eric Erfanianccca3152017-02-22 16:32:36 -0800539
linyuh183cb712017-12-27 17:02:37 -0800540 updateFromCallExtras(telecomCall.getDetails().getExtras());
Eric Erfanianccca3152017-02-22 16:32:36 -0800541
542 // If the handle of the call has changed, update state for the call determining if it is an
543 // emergency call.
linyuh183cb712017-12-27 17:02:37 -0800544 Uri newHandle = telecomCall.getDetails().getHandle();
545 if (!Objects.equals(handle, newHandle)) {
546 handle = newHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800547 updateEmergencyCallState();
548 }
549
linyuh183cb712017-12-27 17:02:37 -0800550 TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
Eric Erfanianccca3152017-02-22 16:32:36 -0800551 // If the phone account handle of the call is set, cache capability bit indicating whether
552 // the phone account supports call subjects.
linyuh183cb712017-12-27 17:02:37 -0800553 PhoneAccountHandle newPhoneAccountHandle = telecomCall.getDetails().getAccountHandle();
554 if (!Objects.equals(phoneAccountHandle, newPhoneAccountHandle)) {
555 phoneAccountHandle = newPhoneAccountHandle;
Eric Erfanianccca3152017-02-22 16:32:36 -0800556
linyuh183cb712017-12-27 17:02:37 -0800557 if (phoneAccountHandle != null) {
558 PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800559 if (phoneAccount != null) {
linyuh183cb712017-12-27 17:02:37 -0800560 isCallSubjectSupported =
Eric Erfanianccca3152017-02-22 16:32:36 -0800561 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
562 }
563 }
564 }
linyuh183cb712017-12-27 17:02:37 -0800565 if (PermissionsUtil.hasPermission(context, permission.READ_PHONE_STATE)) {
wangqi9982f0d2017-10-11 17:46:07 -0700566 updateIsVoiceMailNumber();
567 callCapableAccounts = telecomManager.getCallCapablePhoneAccounts();
linyuh183cb712017-12-27 17:02:37 -0800568 countryIso = GeoUtil.getCurrentCountryIso(context);
wangqi9982f0d2017-10-11 17:46:07 -0700569 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700570 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800571 }
572
573 /**
574 * Tests corruption of the {@code callExtras} bundle by calling {@link
575 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
576 * be thrown and caught by this function.
577 *
578 * @param callExtras the bundle to verify
579 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
580 */
581 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
582 /**
Eric Erfanian938468d2017-10-24 14:05:52 -0700583 * There's currently a bug in Telephony service (a bug) that could corrupt the extras
Eric Erfanianccca3152017-02-22 16:32:36 -0800584 * bundle, resulting in a IllegalArgumentException while validating data under {@link
585 * Bundle#containsKey(String)}.
586 */
587 try {
588 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
589 return false;
590 } catch (IllegalArgumentException e) {
591 LogUtil.e(
592 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
593 return true;
594 }
595 }
596
597 protected void updateFromCallExtras(Bundle callExtras) {
598 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
599 /**
600 * If the bundle is corrupted, abandon information update as a work around. These are not
601 * critical for the dialer to function.
602 */
603 return;
604 }
605 // Check for a change in the child address and notify any listeners.
606 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
607 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
linyuh183cb712017-12-27 17:02:37 -0800608 if (!Objects.equals(childNumber, this.childNumber)) {
609 this.childNumber = childNumber;
610 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800611 listener.onDialerCallChildNumberChange();
612 }
613 }
614 }
615
616 // Last forwarded number comes in as an array of strings. We want to choose the
617 // last item in the array. The forwarding numbers arrive independently of when the
618 // call is originally set up, so we need to notify the the UI of the change.
619 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
620 ArrayList<String> lastForwardedNumbers =
621 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
622
623 if (lastForwardedNumbers != null) {
624 String lastForwardedNumber = null;
625 if (!lastForwardedNumbers.isEmpty()) {
626 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
627 }
628
linyuh183cb712017-12-27 17:02:37 -0800629 if (!Objects.equals(lastForwardedNumber, this.lastForwardedNumber)) {
630 this.lastForwardedNumber = lastForwardedNumber;
631 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800632 listener.onDialerCallLastForwardedNumberChange();
633 }
634 }
635 }
636 }
637
638 // DialerCall subject is present in the extras at the start of call, so we do not need to
639 // notify any other listeners of this.
640 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
641 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
linyuh183cb712017-12-27 17:02:37 -0800642 if (!Objects.equals(this.callSubject, callSubject)) {
643 this.callSubject = callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800644 }
645 }
646 }
647
Eric Erfanianccca3152017-02-22 16:32:36 -0800648 public String getId() {
linyuh183cb712017-12-27 17:02:37 -0800649 return id;
Eric Erfanianccca3152017-02-22 16:32:36 -0800650 }
651
Eric Erfanianc857f902017-05-15 14:05:33 -0700652 /**
653 * @return name appended with a number if the number is restricted/unknown and the user has
654 * received more than one restricted/unknown call.
655 */
656 @Nullable
657 public String updateNameIfRestricted(@Nullable String name) {
linyuh183cb712017-12-27 17:02:37 -0800658 if (name != null && isHiddenNumber() && hiddenId != 0 && hiddenCounter > 1) {
659 return context.getString(R.string.unknown_counter, name, hiddenId);
Eric Erfanianc857f902017-05-15 14:05:33 -0700660 }
661 return name;
662 }
663
664 public static void clearRestrictedCount() {
linyuh183cb712017-12-27 17:02:37 -0800665 hiddenCounter = 0;
Eric Erfanianc857f902017-05-15 14:05:33 -0700666 }
667
668 private boolean isHiddenNumber() {
669 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
670 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
671 }
672
Eric Erfanianccca3152017-02-22 16:32:36 -0800673 public boolean hasShownWiFiToLteHandoverToast() {
674 return hasShownWiFiToLteHandoverToast;
675 }
676
677 public void setHasShownWiFiToLteHandoverToast() {
678 hasShownWiFiToLteHandoverToast = true;
679 }
680
681 public boolean showWifiHandoverAlertAsToast() {
682 return doNotShowDialogForHandoffToWifiFailure;
683 }
684
685 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
686 doNotShowDialogForHandoffToWifiFailure = bool;
687 }
688
689 public long getTimeAddedMs() {
linyuh183cb712017-12-27 17:02:37 -0800690 return timeAddedMs;
Eric Erfanianccca3152017-02-22 16:32:36 -0800691 }
692
693 @Nullable
694 public String getNumber() {
linyuh183cb712017-12-27 17:02:37 -0800695 return TelecomCallUtil.getNumber(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800696 }
697
698 public void blockCall() {
linyuh183cb712017-12-27 17:02:37 -0800699 telecomCall.reject(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800700 setState(State.BLOCKED);
701 }
702
703 @Nullable
704 public Uri getHandle() {
linyuh183cb712017-12-27 17:02:37 -0800705 return telecomCall == null ? null : telecomCall.getDetails().getHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800706 }
707
708 public boolean isEmergencyCall() {
linyuh183cb712017-12-27 17:02:37 -0800709 return isEmergencyCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800710 }
711
712 public boolean isPotentialEmergencyCallback() {
713 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
714 // is actually in emergency callback mode (ie data is disabled).
715 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
716 return true;
717 }
718 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
719 // as a potential emergency callback.
720 if (getExtras() != null
721 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
722 > 0) {
723 long lastEmergencyCallMillis =
724 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
725 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
726 return true;
727 }
728 }
729 return false;
730 }
731
732 boolean isInEmergencyCallbackWindow(long timestampMillis) {
733 long emergencyCallbackWindowMillis =
linyuh183cb712017-12-27 17:02:37 -0800734 ConfigProviderBindings.get(context)
Eric Erfanianccca3152017-02-22 16:32:36 -0800735 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
736 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
737 }
738
739 public int getState() {
linyuh183cb712017-12-27 17:02:37 -0800740 if (telecomCall != null && telecomCall.getParent() != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800741 return State.CONFERENCED;
742 } else {
linyuh183cb712017-12-27 17:02:37 -0800743 return state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800744 }
745 }
746
yueg265089a2017-10-06 14:35:15 -0700747 public int getNonConferenceState() {
linyuh183cb712017-12-27 17:02:37 -0800748 return state;
yueg265089a2017-10-06 14:35:15 -0700749 }
750
Eric Erfanianccca3152017-02-22 16:32:36 -0800751 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700752 if (state == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800753 logState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700754 } else if (state == State.DISCONNECTED) {
755 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800756 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
linyuh183cb712017-12-27 17:02:37 -0800757 if (this.state != state) {
758 logState.duration = newDuration;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700759 } else {
760 LogUtil.i(
761 "DialerCall.setState",
762 "ignoring state transition from DISCONNECTED to DISCONNECTED."
763 + " Duration would have changed from %s to %s",
linyuh183cb712017-12-27 17:02:37 -0800764 logState.duration,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700765 newDuration);
766 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800767 }
linyuh183cb712017-12-27 17:02:37 -0800768 this.state = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800769 }
770
771 public int getNumberPresentation() {
linyuh183cb712017-12-27 17:02:37 -0800772 return telecomCall == null ? -1 : telecomCall.getDetails().getHandlePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800773 }
774
775 public int getCnapNamePresentation() {
linyuh183cb712017-12-27 17:02:37 -0800776 return telecomCall == null ? -1 : telecomCall.getDetails().getCallerDisplayNamePresentation();
Eric Erfanianccca3152017-02-22 16:32:36 -0800777 }
778
779 @Nullable
780 public String getCnapName() {
linyuh183cb712017-12-27 17:02:37 -0800781 return telecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
Eric Erfanianccca3152017-02-22 16:32:36 -0800782 }
783
784 public Bundle getIntentExtras() {
linyuh183cb712017-12-27 17:02:37 -0800785 return telecomCall.getDetails().getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800786 }
787
788 @Nullable
789 public Bundle getExtras() {
linyuh183cb712017-12-27 17:02:37 -0800790 return telecomCall == null ? null : telecomCall.getDetails().getExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800791 }
792
793 /** @return The child number for the call, or {@code null} if none specified. */
794 public String getChildNumber() {
linyuh183cb712017-12-27 17:02:37 -0800795 return childNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800796 }
797
798 /** @return The last forwarded number for the call, or {@code null} if none specified. */
799 public String getLastForwardedNumber() {
linyuh183cb712017-12-27 17:02:37 -0800800 return lastForwardedNumber;
Eric Erfanianccca3152017-02-22 16:32:36 -0800801 }
802
803 /** @return The call subject, or {@code null} if none specified. */
804 public String getCallSubject() {
linyuh183cb712017-12-27 17:02:37 -0800805 return callSubject;
Eric Erfanianccca3152017-02-22 16:32:36 -0800806 }
807
808 /**
809 * @return {@code true} if the call's phone account supports call subjects, {@code false}
810 * otherwise.
811 */
812 public boolean isCallSubjectSupported() {
linyuh183cb712017-12-27 17:02:37 -0800813 return isCallSubjectSupported;
Eric Erfanianccca3152017-02-22 16:32:36 -0800814 }
815
816 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
817 public DisconnectCause getDisconnectCause() {
linyuh183cb712017-12-27 17:02:37 -0800818 if (state == State.DISCONNECTED || state == State.IDLE) {
819 return disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800820 }
821
822 return new DisconnectCause(DisconnectCause.UNKNOWN);
823 }
824
825 public void setDisconnectCause(DisconnectCause disconnectCause) {
linyuh183cb712017-12-27 17:02:37 -0800826 this.disconnectCause = disconnectCause;
827 logState.disconnectCause = this.disconnectCause;
Eric Erfanianccca3152017-02-22 16:32:36 -0800828 }
829
830 /** Returns the possible text message responses. */
831 public List<String> getCannedSmsResponses() {
linyuh183cb712017-12-27 17:02:37 -0800832 return telecomCall.getCannedTextResponses();
Eric Erfanianccca3152017-02-22 16:32:36 -0800833 }
834
835 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
836 public boolean can(int capabilities) {
linyuh183cb712017-12-27 17:02:37 -0800837 int supportedCapabilities = telecomCall.getDetails().getCallCapabilities();
Eric Erfanianccca3152017-02-22 16:32:36 -0800838
839 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
840 // We allow you to merge if the capabilities allow it or if it is a call with
841 // conferenceable calls.
linyuh183cb712017-12-27 17:02:37 -0800842 if (telecomCall.getConferenceableCalls().isEmpty()
Eric Erfanianccca3152017-02-22 16:32:36 -0800843 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
844 // Cannot merge calls if there are no calls to merge with.
845 return false;
846 }
847 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
848 }
849 return (capabilities == (capabilities & supportedCapabilities));
850 }
851
852 public boolean hasProperty(int property) {
linyuh183cb712017-12-27 17:02:37 -0800853 return telecomCall.getDetails().hasProperty(property);
Eric Erfanianccca3152017-02-22 16:32:36 -0800854 }
855
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700856 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800857 public String getUniqueCallId() {
858 return uniqueCallId;
859 }
860
861 /** Gets the time when the call first became active. */
862 public long getConnectTimeMillis() {
linyuh183cb712017-12-27 17:02:37 -0800863 return telecomCall.getDetails().getConnectTimeMillis();
Eric Erfanianccca3152017-02-22 16:32:36 -0800864 }
865
866 public boolean isConferenceCall() {
867 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
868 }
869
870 @Nullable
871 public GatewayInfo getGatewayInfo() {
linyuh183cb712017-12-27 17:02:37 -0800872 return telecomCall == null ? null : telecomCall.getDetails().getGatewayInfo();
Eric Erfanianccca3152017-02-22 16:32:36 -0800873 }
874
875 @Nullable
876 public PhoneAccountHandle getAccountHandle() {
linyuh183cb712017-12-27 17:02:37 -0800877 return telecomCall == null ? null : telecomCall.getDetails().getAccountHandle();
Eric Erfanianccca3152017-02-22 16:32:36 -0800878 }
879
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700880 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -0800881 public VideoCall getVideoCall() {
linyuh183cb712017-12-27 17:02:37 -0800882 return telecomCall == null ? null : telecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800883 }
884
885 public List<String> getChildCallIds() {
linyuh183cb712017-12-27 17:02:37 -0800886 return childCallIds;
Eric Erfanianccca3152017-02-22 16:32:36 -0800887 }
888
889 public String getParentId() {
linyuh183cb712017-12-27 17:02:37 -0800890 Call parentCall = telecomCall.getParent();
Eric Erfanianccca3152017-02-22 16:32:36 -0800891 if (parentCall != null) {
linyuh183cb712017-12-27 17:02:37 -0800892 return dialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
Eric Erfanianccca3152017-02-22 16:32:36 -0800893 }
894 return null;
895 }
896
897 public int getVideoState() {
linyuh183cb712017-12-27 17:02:37 -0800898 return telecomCall.getDetails().getVideoState();
Eric Erfanianccca3152017-02-22 16:32:36 -0800899 }
900
901 public boolean isVideoCall() {
roldenburg2cec3802017-10-11 16:26:51 -0700902 return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700903 }
904
905 public boolean hasReceivedVideoUpgradeRequest() {
906 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
907 }
908
909 public boolean hasSentVideoUpgradeRequest() {
910 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -0800911 }
912
913 /**
914 * Determines if the call handle is an emergency number or not and caches the result to avoid
915 * repeated calls to isEmergencyNumber.
916 */
917 private void updateEmergencyCallState() {
linyuh183cb712017-12-27 17:02:37 -0800918 isEmergencyCall = TelecomCallUtil.isEmergencyCall(telecomCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800919 }
920
Eric Erfanianccca3152017-02-22 16:32:36 -0800921 public LogState getLogState() {
linyuh183cb712017-12-27 17:02:37 -0800922 return logState;
Eric Erfanianccca3152017-02-22 16:32:36 -0800923 }
924
925 /**
926 * Determines if the call is an external call.
927 *
928 * <p>An external call is one which does not exist locally for the {@link
929 * android.telecom.ConnectionService} it is associated with.
930 *
931 * <p>External calls are only supported in N and higher.
932 *
933 * @return {@code true} if the call is an external call, {@code false} otherwise.
934 */
935 public boolean isExternalCall() {
936 return VERSION.SDK_INT >= VERSION_CODES.N
937 && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
938 }
939
940 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800941 * Determines if answering this call will cause an ongoing video call to be dropped.
942 *
943 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
944 * otherwise.
945 */
946 public boolean answeringDisconnectsForegroundVideoCall() {
947 Bundle extras = getExtras();
948 if (extras == null
949 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
950 return false;
951 }
952 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
953 }
954
955 private void parseCallSpecificAppData() {
956 if (isExternalCall()) {
957 return;
958 }
959
linyuh183cb712017-12-27 17:02:37 -0800960 logState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
961 if (logState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700962
linyuh183cb712017-12-27 17:02:37 -0800963 logState.callSpecificAppData =
Eric Erfanian8369df02017-05-03 10:27:13 -0700964 CallSpecificAppData.newBuilder()
965 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
966 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800967 }
968 if (getState() == State.INCOMING) {
linyuh183cb712017-12-27 17:02:37 -0800969 logState.callSpecificAppData =
970 logState
Eric Erfanian8369df02017-05-03 10:27:13 -0700971 .callSpecificAppData
972 .toBuilder()
973 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
974 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800975 }
976 }
977
978 @Override
979 public String toString() {
linyuh183cb712017-12-27 17:02:37 -0800980 if (telecomCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800981 // This should happen only in testing since otherwise we would never have a null
982 // Telecom call.
linyuh183cb712017-12-27 17:02:37 -0800983 return String.valueOf(id);
Eric Erfanianccca3152017-02-22 16:32:36 -0800984 }
985
986 return String.format(
987 Locale.US,
988 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700989 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
linyuh183cb712017-12-27 17:02:37 -0800990 id,
Eric Erfanianccca3152017-02-22 16:32:36 -0800991 State.toString(getState()),
linyuh183cb712017-12-27 17:02:37 -0800992 Details.capabilitiesToString(telecomCall.getDetails().getCallCapabilities()),
993 Details.propertiesToString(telecomCall.getDetails().getCallProperties()),
994 childCallIds,
Eric Erfanianccca3152017-02-22 16:32:36 -0800995 getParentId(),
linyuh183cb712017-12-27 17:02:37 -0800996 this.telecomCall.getConferenceableCalls(),
997 VideoProfile.videoStateToString(telecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700998 getVideoTech().getSessionModificationState(),
999 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -08001000 }
1001
1002 public String toSimpleString() {
1003 return super.toString();
1004 }
1005
1006 @CallHistoryStatus
1007 public int getCallHistoryStatus() {
linyuh183cb712017-12-27 17:02:37 -08001008 return callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001009 }
1010
1011 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
linyuh183cb712017-12-27 17:02:37 -08001012 this.callHistoryStatus = callHistoryStatus;
Eric Erfanianccca3152017-02-22 16:32:36 -08001013 }
1014
1015 public boolean didShowCameraPermission() {
1016 return didShowCameraPermission;
1017 }
1018
1019 public void setDidShowCameraPermission(boolean didShow) {
1020 didShowCameraPermission = didShow;
1021 }
1022
Eric Erfanian938468d2017-10-24 14:05:52 -07001023 @Nullable
1024 public Boolean isInGlobalSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001025 return isInGlobalSpamList;
1026 }
1027
1028 public void setIsInGlobalSpamList(boolean inSpamList) {
1029 isInGlobalSpamList = inSpamList;
1030 }
1031
Eric Erfanian938468d2017-10-24 14:05:52 -07001032 @Nullable
1033 public Boolean isInUserSpamList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001034 return isInUserSpamList;
1035 }
1036
1037 public void setIsInUserSpamList(boolean inSpamList) {
1038 isInUserSpamList = inSpamList;
1039 }
1040
Eric Erfanian938468d2017-10-24 14:05:52 -07001041 @Nullable
1042 public Boolean isInUserWhiteList() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001043 return isInUserWhiteList;
1044 }
1045
1046 public void setIsInUserWhiteList(boolean inWhiteList) {
1047 isInUserWhiteList = inWhiteList;
1048 }
1049
1050 public boolean isSpam() {
linyuh183cb712017-12-27 17:02:37 -08001051 return isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001052 }
1053
1054 public void setSpam(boolean isSpam) {
linyuh183cb712017-12-27 17:02:37 -08001055 this.isSpam = isSpam;
Eric Erfanianccca3152017-02-22 16:32:36 -08001056 }
1057
1058 public boolean isBlocked() {
linyuh183cb712017-12-27 17:02:37 -08001059 return isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001060 }
1061
1062 public void setBlockedStatus(boolean isBlocked) {
linyuh183cb712017-12-27 17:02:37 -08001063 this.isBlocked = isBlocked;
Eric Erfanianccca3152017-02-22 16:32:36 -08001064 }
1065
1066 public boolean isRemotelyHeld() {
1067 return isRemotelyHeld;
1068 }
1069
Eric Erfanian2ca43182017-08-31 06:57:16 -07001070 public boolean isMergeInProcess() {
1071 return isMergeInProcess;
1072 }
1073
Eric Erfanianccca3152017-02-22 16:32:36 -08001074 public boolean isIncoming() {
linyuh183cb712017-12-27 17:02:37 -08001075 return logState.isIncoming;
Eric Erfanianccca3152017-02-22 16:32:36 -08001076 }
1077
erfanian2cf2c342017-12-21 12:01:33 -08001078 /**
1079 * Try and determine if the call used assisted dialing.
1080 *
1081 * <p>We will not be able to verify a call underwent assisted dialing until the Platform
1082 * implmentation is complete in P+.
1083 *
1084 * @return a boolean indicating assisted dialing may have been performed
1085 */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001086 public boolean isAssistedDialed() {
1087 if (getIntentExtras() != null) {
erfanian2cf2c342017-12-21 12:01:33 -08001088 // O_MR1 and below uses the existence of USE_ASSISTED_DIALING to indicate assisted dialing
1089 // was used. The Dialer client is responsible for performing assisted dialing before
1090 // placing the outgoing call.
1091 //
1092 // The existence of the assisted dialing extras indicates that assisted dialing took place.
1093 if (getIntentExtras().getBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, false)
1094 && getAssistedDialingExtras() != null
1095 && Build.VERSION.SDK_INT <= ConcreteCreator.BUILD_CODE_CEILING) {
1096 return true;
1097 }
1098 }
1099
1100 // Starting in P+ USE_ASSISTED_DIALING indicates that the client requested the platform
1101 // perform assisted dialing. PROPERTY_ASSISTED_DIALING_USED indicates assisted dialing took
1102 // place.
1103 if (hasProperty(TelephonyManagerCompat.PROPERTY_ASSISTED_DIALING_USED)
1104 && Build.VERSION.SDK_INT > ConcreteCreator.BUILD_CODE_CEILING) {
1105 return true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001106 }
1107 return false;
1108 }
1109
erfanian2cf2c342017-12-21 12:01:33 -08001110 @Nullable
erfaniand0f207f2017-10-11 12:23:29 -07001111 public TransformationInfo getAssistedDialingExtras() {
erfanian2cf2c342017-12-21 12:01:33 -08001112 if (getIntentExtras() == null) {
1113 return null;
erfaniand0f207f2017-10-11 12:23:29 -07001114 }
erfanian2cf2c342017-12-21 12:01:33 -08001115
1116 if (getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS) == null) {
1117 return null;
1118 }
1119
1120 return TransformationInfo.newInstanceFromBundle(
1121 getIntentExtras().getBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS));
erfaniand0f207f2017-10-11 12:23:29 -07001122 }
1123
Eric Erfanianccca3152017-02-22 16:32:36 -08001124 public LatencyReport getLatencyReport() {
linyuh183cb712017-12-27 17:02:37 -08001125 return latencyReport;
Eric Erfanianccca3152017-02-22 16:32:36 -08001126 }
1127
Eric Erfanian2ca43182017-08-31 06:57:16 -07001128 public int getAnswerAndReleaseButtonDisplayedTimes() {
1129 return answerAndReleaseButtonDisplayedTimes;
1130 }
1131
1132 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1133 answerAndReleaseButtonDisplayedTimes++;
1134 }
1135
1136 public boolean getReleasedByAnsweringSecondCall() {
1137 return releasedByAnsweringSecondCall;
1138 }
1139
1140 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1141 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1142 }
1143
1144 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1145 return secondCallWithoutAnswerAndReleasedButtonTimes;
1146 }
1147
1148 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1149 secondCallWithoutAnswerAndReleasedButtonTimes++;
1150 }
1151
Eric Erfanian8369df02017-05-03 10:27:13 -07001152 @Nullable
1153 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
linyuh183cb712017-12-27 17:02:37 -08001154 return enrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001155 }
1156
1157 public void setEnrichedCallCapabilities(
1158 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
linyuh183cb712017-12-27 17:02:37 -08001159 this.enrichedCallCapabilities = mEnrichedCallCapabilities;
Eric Erfanian8369df02017-05-03 10:27:13 -07001160 }
1161
1162 @Nullable
1163 public Session getEnrichedCallSession() {
linyuh183cb712017-12-27 17:02:37 -08001164 return enrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001165 }
1166
1167 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
linyuh183cb712017-12-27 17:02:37 -08001168 this.enrichedCallSession = mEnrichedCallSession;
Eric Erfanian8369df02017-05-03 10:27:13 -07001169 }
1170
Eric Erfanianccca3152017-02-22 16:32:36 -08001171 public void unregisterCallback() {
linyuh183cb712017-12-27 17:02:37 -08001172 telecomCall.unregisterCallback(telecomCallCallback);
Eric Erfanianccca3152017-02-22 16:32:36 -08001173 }
1174
Eric Erfanianccca3152017-02-22 16:32:36 -08001175 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1176 LogUtil.i(
1177 "DialerCall.phoneAccountSelected",
1178 "accountHandle: %s, setDefault: %b",
1179 accountHandle,
1180 setDefault);
linyuh183cb712017-12-27 17:02:37 -08001181 telecomCall.phoneAccountSelected(accountHandle, setDefault);
Eric Erfanianccca3152017-02-22 16:32:36 -08001182 }
1183
1184 public void disconnect() {
1185 LogUtil.i("DialerCall.disconnect", "");
1186 setState(DialerCall.State.DISCONNECTING);
linyuh183cb712017-12-27 17:02:37 -08001187 for (DialerCallListener listener : listeners) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001188 listener.onDialerCallUpdate();
1189 }
linyuh183cb712017-12-27 17:02:37 -08001190 telecomCall.disconnect();
Eric Erfanianccca3152017-02-22 16:32:36 -08001191 }
1192
1193 public void hold() {
1194 LogUtil.i("DialerCall.hold", "");
linyuh183cb712017-12-27 17:02:37 -08001195 telecomCall.hold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001196 }
1197
1198 public void unhold() {
1199 LogUtil.i("DialerCall.unhold", "");
linyuh183cb712017-12-27 17:02:37 -08001200 telecomCall.unhold();
Eric Erfanianccca3152017-02-22 16:32:36 -08001201 }
1202
1203 public void splitFromConference() {
1204 LogUtil.i("DialerCall.splitFromConference", "");
linyuh183cb712017-12-27 17:02:37 -08001205 telecomCall.splitFromConference();
Eric Erfanianccca3152017-02-22 16:32:36 -08001206 }
1207
1208 public void answer(int videoState) {
1209 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
linyuh183cb712017-12-27 17:02:37 -08001210 telecomCall.answer(videoState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001211 }
1212
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001213 public void answer() {
linyuh183cb712017-12-27 17:02:37 -08001214 answer(telecomCall.getDetails().getVideoState());
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001215 }
1216
Eric Erfanianccca3152017-02-22 16:32:36 -08001217 public void reject(boolean rejectWithMessage, String message) {
1218 LogUtil.i("DialerCall.reject", "");
linyuh183cb712017-12-27 17:02:37 -08001219 telecomCall.reject(rejectWithMessage, message);
Eric Erfanianccca3152017-02-22 16:32:36 -08001220 }
1221
1222 /** Return the string label to represent the call provider */
1223 public String getCallProviderLabel() {
1224 if (callProviderLabel == null) {
1225 PhoneAccount account = getPhoneAccount();
1226 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
wangqi9982f0d2017-10-11 17:46:07 -07001227 if (callCapableAccounts != null && callCapableAccounts.size() > 1) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001228 callProviderLabel = account.getLabel().toString();
1229 }
1230 }
1231 if (callProviderLabel == null) {
1232 callProviderLabel = "";
1233 }
1234 }
1235 return callProviderLabel;
1236 }
1237
1238 private PhoneAccount getPhoneAccount() {
1239 PhoneAccountHandle accountHandle = getAccountHandle();
1240 if (accountHandle == null) {
1241 return null;
1242 }
linyuh183cb712017-12-27 17:02:37 -08001243 return context.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
Eric Erfanianccca3152017-02-22 16:32:36 -08001244 }
1245
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001246 public VideoTech getVideoTech() {
roldenburg7bb96232017-10-09 10:32:05 -07001247 if (videoTech == null) {
linyuh183cb712017-12-27 17:02:37 -08001248 videoTech = videoTechManager.getVideoTech();
roldenburg7bb96232017-10-09 10:32:05 -07001249
1250 // Only store the first video tech type found to be available during the life of the call.
1251 if (selectedAvailableVideoTechType == com.android.dialer.logging.VideoTech.Type.NONE) {
1252 // Update the video tech.
1253 selectedAvailableVideoTechType = videoTech.getVideoTechType();
1254 }
1255 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001256 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001257 }
1258
Eric Erfanianccca3152017-02-22 16:32:36 -08001259 public String getCallbackNumber() {
1260 if (callbackNumber == null) {
1261 // Show the emergency callback number if either:
1262 // 1. This is an emergency call.
1263 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1264 // number.
1265 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1266
1267 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001268 callbackNumber =
linyuh183cb712017-12-27 17:02:37 -08001269 context.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001270 }
1271
Eric Erfanianccca3152017-02-22 16:32:36 -08001272 if (callbackNumber == null) {
1273 callbackNumber = "";
1274 }
1275 }
1276 return callbackNumber;
1277 }
1278
wangqi97539352017-09-25 11:15:16 -07001279 public String getSimCountryIso() {
1280 String simCountryIso =
linyuh183cb712017-12-27 17:02:37 -08001281 TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle(context, getAccountHandle())
wangqi97539352017-09-25 11:15:16 -07001282 .getSimCountryIso();
1283 if (!TextUtils.isEmpty(simCountryIso)) {
1284 simCountryIso = simCountryIso.toUpperCase(Locale.US);
1285 }
1286 return simCountryIso;
1287 }
1288
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001289 @Override
1290 public void onVideoTechStateChanged() {
1291 update();
1292 }
1293
1294 @Override
1295 public void onSessionModificationStateChanged() {
wangqi9982f0d2017-10-11 17:46:07 -07001296 Trace.beginSection("DialerCall.onSessionModificationStateChanged");
linyuh183cb712017-12-27 17:02:37 -08001297 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001298 listener.onDialerCallSessionModificationStateChange();
1299 }
wangqi9982f0d2017-10-11 17:46:07 -07001300 Trace.endSection();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001301 }
1302
1303 @Override
1304 public void onCameraDimensionsChanged(int width, int height) {
1305 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1306 }
1307
1308 @Override
1309 public void onPeerDimensionsChanged(int width, int height) {
1310 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1311 }
1312
1313 @Override
1314 public void onVideoUpgradeRequestReceived() {
1315 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1316
linyuh183cb712017-12-27 17:02:37 -08001317 for (DialerCallListener listener : listeners) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001318 listener.onDialerCallUpgradeToVideo();
1319 }
1320
1321 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001322
linyuh183cb712017-12-27 17:02:37 -08001323 Logger.get(context)
Eric Erfanian8369df02017-05-03 10:27:13 -07001324 .logCallImpression(
1325 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1326 }
1327
1328 @Override
1329 public void onUpgradedToVideo(boolean switchToSpeaker) {
1330 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1331
1332 if (!switchToSpeaker) {
1333 return;
1334 }
1335
1336 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1337
1338 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1339 LogUtil.e(
1340 "DialerCall.onUpgradedToVideo",
1341 "toggling speakerphone not allowed when bluetooth supported.");
1342 return;
1343 }
1344
1345 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1346 return;
1347 }
1348
1349 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001350 }
1351
Eric Erfanian2ca43182017-08-31 06:57:16 -07001352 @Override
1353 public void onCapabilitiesUpdated() {
1354 if (getNumber() == null) {
1355 return;
1356 }
1357 EnrichedCallCapabilities capabilities =
linyuh183cb712017-12-27 17:02:37 -08001358 EnrichedCallComponent.get(context).getEnrichedCallManager().getCapabilities(getNumber());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001359 if (capabilities != null) {
1360 setEnrichedCallCapabilities(capabilities);
1361 update();
1362 }
1363 }
1364
1365 @Override
1366 public void onEnrichedCallStateChanged() {
1367 updateEnrichedCallSession();
1368 }
1369
1370 @Override
1371 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
linyuh183cb712017-12-27 17:02:37 -08001372 Logger.get(context).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001373 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1374 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
linyuh183cb712017-12-27 17:02:37 -08001375 Logger.get(context)
twyendde01c52017-09-22 10:07:31 -07001376 .logCallImpression(
1377 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1378 getUniqueCallId(),
1379 getTimeAddedMs());
1380 }
1381 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001382 }
1383
1384 private void updateEnrichedCallSession() {
1385 if (getNumber() == null) {
1386 return;
1387 }
1388 if (getEnrichedCallSession() != null) {
1389 // State changes to existing sessions are currently handled by the UI components (which have
1390 // their own listeners). Someday instead we could remove those and just call update() here and
1391 // have the usual onDialerCallUpdate update the UI.
1392 dispatchOnEnrichedCallSessionUpdate();
1393 return;
1394 }
1395
linyuh183cb712017-12-27 17:02:37 -08001396 EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001397
1398 Filter filter =
1399 isIncoming()
1400 ? manager.createIncomingCallComposerFilter()
1401 : manager.createOutgoingCallComposerFilter();
1402
1403 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1404 if (session == null) {
1405 return;
1406 }
1407
1408 session.setUniqueDialerCallId(getUniqueCallId());
1409 setEnrichedCallSession(session);
1410
1411 LogUtil.i(
1412 "DialerCall.updateEnrichedCallSession",
1413 "setting session %d's dialer id to %s",
1414 session.getSessionId(),
1415 getUniqueCallId());
1416
1417 dispatchOnEnrichedCallSessionUpdate();
1418 }
1419
1420 private void dispatchOnEnrichedCallSessionUpdate() {
linyuh183cb712017-12-27 17:02:37 -08001421 for (DialerCallListener listener : listeners) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001422 listener.onEnrichedCallSessionUpdate();
1423 }
1424 }
1425
1426 void onRemovedFromCallList() {
1427 // Ensure we clean up when this call is removed.
linyuh183cb712017-12-27 17:02:37 -08001428 videoTechManager.dispatchRemovedFromCallList();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001429 }
1430
wangqi4d705e52017-09-28 12:23:35 -07001431 public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() {
1432 return selectedAvailableVideoTechType;
yueg457b3972017-09-18 15:11:47 -07001433 }
1434
Eric Erfanianccca3152017-02-22 16:32:36 -08001435 /**
1436 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1437 * means there is no result.
1438 */
1439 @IntDef({
1440 CALL_HISTORY_STATUS_UNKNOWN,
1441 CALL_HISTORY_STATUS_PRESENT,
1442 CALL_HISTORY_STATUS_NOT_PRESENT
1443 })
1444 @Retention(RetentionPolicy.SOURCE)
1445 public @interface CallHistoryStatus {}
1446
1447 /* Defines different states of this call */
1448 public static class State {
1449
1450 public static final int INVALID = 0;
1451 public static final int NEW = 1; /* The call is new. */
1452 public static final int IDLE = 2; /* The call is idle. Nothing active */
1453 public static final int ACTIVE = 3; /* There is an active call */
1454 public static final int INCOMING = 4; /* A normal incoming phone call */
1455 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1456 public static final int DIALING = 6; /* An outgoing call during dial phase */
1457 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1458 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1459 public static final int DISCONNECTING = 9; /* A call is being ended. */
1460 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1461 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1462 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1463 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1464 public static final int BLOCKED = 14; /* The number was found on the block list */
1465 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001466 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001467
1468 public static boolean isConnectingOrConnected(int state) {
1469 switch (state) {
1470 case ACTIVE:
1471 case INCOMING:
1472 case CALL_WAITING:
1473 case CONNECTING:
1474 case DIALING:
1475 case PULLING:
1476 case REDIALING:
1477 case ONHOLD:
1478 case CONFERENCED:
1479 return true;
1480 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001481 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001482 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001483 }
1484
1485 public static boolean isDialing(int state) {
1486 return state == DIALING || state == PULLING || state == REDIALING;
1487 }
1488
1489 public static String toString(int state) {
1490 switch (state) {
1491 case INVALID:
1492 return "INVALID";
1493 case NEW:
1494 return "NEW";
1495 case IDLE:
1496 return "IDLE";
1497 case ACTIVE:
1498 return "ACTIVE";
1499 case INCOMING:
1500 return "INCOMING";
1501 case CALL_WAITING:
1502 return "CALL_WAITING";
1503 case DIALING:
1504 return "DIALING";
1505 case PULLING:
1506 return "PULLING";
1507 case REDIALING:
1508 return "REDIALING";
1509 case ONHOLD:
1510 return "ONHOLD";
1511 case DISCONNECTING:
1512 return "DISCONNECTING";
1513 case DISCONNECTED:
1514 return "DISCONNECTED";
1515 case CONFERENCED:
1516 return "CONFERENCED";
1517 case SELECT_PHONE_ACCOUNT:
1518 return "SELECT_PHONE_ACCOUNT";
1519 case CONNECTING:
1520 return "CONNECTING";
1521 case BLOCKED:
1522 return "BLOCKED";
1523 default:
1524 return "UNKNOWN";
1525 }
1526 }
1527 }
1528
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001529 /** Camera direction constants */
1530 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001531 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1532 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1533 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001534 }
1535
1536 /**
1537 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1538 * existing call member variables, but this duplication helps to ensure that none of these logging
1539 * variables will interface with/and affect call logic.
1540 */
1541 public static class LogState {
1542
1543 public DisconnectCause disconnectCause;
1544 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001545 public ContactLookupResult.Type contactLookupResult =
1546 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001547 public CallSpecificAppData callSpecificAppData;
1548 // If this was a conference call, the total number of calls involved in the conference.
1549 public int conferencedCalls = 0;
1550 public long duration = 0;
1551 public boolean isLogged = false;
1552
Eric Erfanian8369df02017-05-03 10:27:13 -07001553 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001554 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001555 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001556 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001557 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001558 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001559 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001560 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001561 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001562 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001563 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001564 return "Voicemail";
1565 default:
1566 return "Not found";
1567 }
1568 }
1569
1570 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1571 if (callSpecificAppData == null) {
1572 return "null";
1573 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001574 switch (callSpecificAppData.getCallInitiationType()) {
1575 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001576 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001577 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001578 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001579 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001580 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001581 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001582 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001583 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001584 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001585 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001586 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001587 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001588 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001589 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001590 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001591 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001592 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001593 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001594 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001595 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001596 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001597 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001598 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001599 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001600 return "Launcher Shortcut";
1601 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001602 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001603 }
1604 }
1605
1606 @Override
1607 public String toString() {
1608 return String.format(
1609 Locale.US,
1610 "["
1611 + "%s, " // DisconnectCause toString already describes the object type
1612 + "isIncoming: %s, "
1613 + "contactLookup: %s, "
1614 + "callInitiation: %s, "
1615 + "duration: %s"
1616 + "]",
1617 disconnectCause,
1618 isIncoming,
1619 lookupToString(contactLookupResult),
1620 initiationToString(callSpecificAppData),
1621 duration);
1622 }
1623 }
1624
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001625 private static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001626 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001627 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
Eric Erfanian90508232017-03-24 09:31:16 -07001628 private final List<VideoTech> videoTechs;
roldenburg7bb96232017-10-09 10:32:05 -07001629 private VideoTech savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001630
1631 VideoTechManager(DialerCall call) {
linyuh183cb712017-12-27 17:02:37 -08001632 this.context = call.context;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001633
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001634 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001635 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001636 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001637
1638 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001639 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001640
linyuh183cb712017-12-27 17:02:37 -08001641 videoTechs.add(new ImsVideoTech(Logger.get(call.context), call, call.telecomCall));
Eric Erfanian90508232017-03-24 09:31:16 -07001642
1643 VideoTech rcsVideoTech =
linyuh183cb712017-12-27 17:02:37 -08001644 EnrichedCallComponent.get(call.context)
Eric Erfanian90508232017-03-24 09:31:16 -07001645 .getRcsVideoShareFactory()
1646 .newRcsVideoShare(
linyuh183cb712017-12-27 17:02:37 -08001647 EnrichedCallComponent.get(call.context).getEnrichedCallManager(),
Eric Erfanian90508232017-03-24 09:31:16 -07001648 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001649 phoneNumber);
Eric Erfanian90508232017-03-24 09:31:16 -07001650 if (rcsVideoTech != null) {
1651 videoTechs.add(rcsVideoTech);
1652 }
Eric Erfaniand8046e52017-04-06 09:41:50 -07001653
1654 videoTechs.add(
roldenburg4f026392017-10-13 18:42:20 -07001655 new DuoVideoTech(
linyuh183cb712017-12-27 17:02:37 -08001656 DuoComponent.get(call.context).getDuo(), call, call.telecomCall, phoneNumber));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001657 }
1658
roldenburg7bb96232017-10-09 10:32:05 -07001659 VideoTech getVideoTech() {
1660 if (savedTech != null) {
1661 return savedTech;
1662 }
1663
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001664 for (VideoTech tech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001665 if (tech.isAvailable(context)) {
roldenburg7bb96232017-10-09 10:32:05 -07001666 // Remember the first VideoTech that becomes available and always use it
1667 savedTech = tech;
1668 savedTech.becomePrimary();
1669 return savedTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001670 }
1671 }
1672
1673 return emptyVideoTech;
1674 }
1675
1676 void dispatchCallStateChanged(int newState) {
1677 for (VideoTech videoTech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001678 videoTech.onCallStateChanged(context, newState);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001679 }
1680 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001681
1682 void dispatchRemovedFromCallList() {
1683 for (VideoTech videoTech : videoTechs) {
1684 videoTech.onRemovedFromCallList();
1685 }
1686 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001687 }
1688
Eric Erfanianccca3152017-02-22 16:32:36 -08001689 /** Called when canned text responses have been loaded. */
1690 public interface CannedTextResponsesLoadedListener {
1691 void onCannedTextResponsesLoaded(DialerCall call);
1692 }
1693}