blob: a9bad5b582dca2ba2e5cd4885947c15f2027ed23 [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
19import android.content.Context;
20import android.hardware.camera2.CameraCharacteristics;
21import android.net.Uri;
22import android.os.Build.VERSION;
23import android.os.Build.VERSION_CODES;
24import android.os.Bundle;
25import android.os.Trace;
26import android.support.annotation.IntDef;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070027import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080028import android.support.annotation.Nullable;
twyendde01c52017-09-22 10:07:31 -070029import android.support.annotation.VisibleForTesting;
Eric Erfanianccca3152017-02-22 16:32:36 -080030import android.telecom.Call;
31import android.telecom.Call.Details;
Eric Erfanian8369df02017-05-03 10:27:13 -070032import android.telecom.CallAudioState;
Eric Erfanianccca3152017-02-22 16:32:36 -080033import android.telecom.Connection;
34import android.telecom.DisconnectCause;
35import android.telecom.GatewayInfo;
36import android.telecom.InCallService.VideoCall;
37import android.telecom.PhoneAccount;
38import android.telecom.PhoneAccountHandle;
39import android.telecom.StatusHints;
40import android.telecom.TelecomManager;
41import android.telecom.VideoProfile;
Eric Erfanianccca3152017-02-22 16:32:36 -080042import android.text.TextUtils;
43import com.android.contacts.common.compat.CallCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080044import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
Eric Erfanian8369df02017-05-03 10:27:13 -070045import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080046import com.android.dialer.callintent.CallIntentParser;
Eric Erfanian8369df02017-05-03 10:27:13 -070047import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080048import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080049import com.android.dialer.common.LogUtil;
Eric Erfanian2ca43182017-08-31 06:57:16 -070050import com.android.dialer.compat.telephony.TelephonyManagerCompat;
51import com.android.dialer.configprovider.ConfigProviderBindings;
Eric Erfanian8369df02017-05-03 10:27:13 -070052import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070053import com.android.dialer.enrichedcall.EnrichedCallComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -070054import com.android.dialer.enrichedcall.EnrichedCallManager;
55import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
56import com.android.dialer.enrichedcall.EnrichedCallManager.Filter;
57import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
Eric Erfanian8369df02017-05-03 10:27:13 -070058import com.android.dialer.enrichedcall.Session;
Eric Erfaniand8046e52017-04-06 09:41:50 -070059import com.android.dialer.lightbringer.LightbringerComponent;
Eric Erfanian8369df02017-05-03 10:27:13 -070060import com.android.dialer.logging.ContactLookupResult;
twyendde01c52017-09-22 10:07:31 -070061import com.android.dialer.logging.ContactLookupResult.Type;
Eric Erfanian8369df02017-05-03 10:27:13 -070062import com.android.dialer.logging.DialerImpression;
63import com.android.dialer.logging.Logger;
Eric Erfanianc857f902017-05-15 14:05:33 -070064import com.android.dialer.theme.R;
Eric Erfanian8369df02017-05-03 10:27:13 -070065import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080066import com.android.incallui.latencyreport.LatencyReport;
67import com.android.incallui.util.TelecomCallUtil;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070068import com.android.incallui.videotech.VideoTech;
69import com.android.incallui.videotech.VideoTech.VideoTechListener;
70import com.android.incallui.videotech.empty.EmptyVideoTech;
71import com.android.incallui.videotech.ims.ImsVideoTech;
Eric Erfaniand8046e52017-04-06 09:41:50 -070072import com.android.incallui.videotech.lightbringer.LightbringerTech;
Eric Erfanian90508232017-03-24 09:31:16 -070073import com.android.incallui.videotech.utils.VideoUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -080074import java.lang.annotation.Retention;
75import java.lang.annotation.RetentionPolicy;
76import java.util.ArrayList;
77import java.util.List;
78import java.util.Locale;
79import java.util.Objects;
80import java.util.UUID;
81import java.util.concurrent.CopyOnWriteArrayList;
82import java.util.concurrent.TimeUnit;
83
84/** Describes a single call and its state. */
Eric Erfanian2ca43182017-08-31 06:57:16 -070085public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener {
Eric Erfanianccca3152017-02-22 16:32:36 -080086
87 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
88 public static final int CALL_HISTORY_STATUS_PRESENT = 1;
89 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070090
91 // Hard coded property for {@code Call}. Upstreamed change from Motorola.
92 // TODO(b/35359461): Move it to Telecom in framework.
93 public static final int PROPERTY_CODEC_KNOWN = 0x04000000;
94
Eric Erfanianccca3152017-02-22 16:32:36 -080095 private static final String ID_PREFIX = "DialerCall_";
96 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
97 "emergency_callback_window_millis";
98 private static int sIdCounter = 0;
99
100 /**
Eric Erfanianc857f902017-05-15 14:05:33 -0700101 * A counter used to append to restricted/private/hidden calls so that users can identify them in
102 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there
103 * are no live calls.
104 */
105 private static int sHiddenCounter;
106
107 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800108 * The unique call ID for every call. This will help us to identify each call and allow us the
109 * ability to stitch impressions to calls if needed.
110 */
111 private final String uniqueCallId = UUID.randomUUID().toString();
112
113 private final Call mTelecomCall;
114 private final LatencyReport mLatencyReport;
115 private final String mId;
Eric Erfanianc857f902017-05-15 14:05:33 -0700116 private final int mHiddenId;
Eric Erfanianccca3152017-02-22 16:32:36 -0800117 private final List<String> mChildCallIds = new ArrayList<>();
Eric Erfanianccca3152017-02-22 16:32:36 -0800118 private final LogState mLogState = new LogState();
119 private final Context mContext;
120 private final DialerCallDelegate mDialerCallDelegate;
121 private final List<DialerCallListener> mListeners = new CopyOnWriteArrayList<>();
122 private final List<CannedTextResponsesLoadedListener> mCannedTextResponsesLoadedListeners =
123 new CopyOnWriteArrayList<>();
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700124 private final VideoTechManager mVideoTechManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800125
126 private boolean mIsEmergencyCall;
127 private Uri mHandle;
128 private int mState = State.INVALID;
129 private DisconnectCause mDisconnectCause;
130
131 private boolean hasShownWiFiToLteHandoverToast;
132 private boolean doNotShowDialogForHandoffToWifiFailure;
133
Eric Erfanianccca3152017-02-22 16:32:36 -0800134 private String mChildNumber;
135 private String mLastForwardedNumber;
136 private String mCallSubject;
137 private PhoneAccountHandle mPhoneAccountHandle;
138 @CallHistoryStatus private int mCallHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
139 private boolean mIsSpam;
140 private boolean mIsBlocked;
141 private boolean isInUserSpamList;
142 private boolean isInUserWhiteList;
143 private boolean isInGlobalSpamList;
144 private boolean didShowCameraPermission;
145 private String callProviderLabel;
146 private String callbackNumber;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700147 private int mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
Eric Erfanian8369df02017-05-03 10:27:13 -0700148 private EnrichedCallCapabilities mEnrichedCallCapabilities;
149 private Session mEnrichedCallSession;
Eric Erfanianccca3152017-02-22 16:32:36 -0800150
Eric Erfanian2ca43182017-08-31 06:57:16 -0700151 private int answerAndReleaseButtonDisplayedTimes = 0;
152 private boolean releasedByAnsweringSecondCall = false;
153 // Times when a second call is received but AnswerAndRelease button is not shown
154 // since it's not supported.
155 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0;
156 private VideoTech videoTech;
157
yueg457b3972017-09-18 15:11:47 -0700158 private boolean isImsReachable;
159
Eric Erfanianccca3152017-02-22 16:32:36 -0800160 public static String getNumberFromHandle(Uri handle) {
161 return handle == null ? "" : handle.getSchemeSpecificPart();
162 }
163
164 /**
165 * Whether the call is put on hold by remote party. This is different than the {@link
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700166 * State#ONHOLD} state which indicates that the call is being held locally on the device.
Eric Erfanianccca3152017-02-22 16:32:36 -0800167 */
168 private boolean isRemotelyHeld;
169
Eric Erfanian2ca43182017-08-31 06:57:16 -0700170 /** Indicates whether this call is currently in the process of being merged into a conference. */
171 private boolean isMergeInProcess;
172
Eric Erfanianccca3152017-02-22 16:32:36 -0800173 /**
174 * Indicates whether the phone account associated with this call supports specifying a call
175 * subject.
176 */
177 private boolean mIsCallSubjectSupported;
178
179 private final Call.Callback mTelecomCallCallback =
180 new Call.Callback() {
181 @Override
182 public void onStateChanged(Call call, int newState) {
183 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
184 update();
185 }
186
187 @Override
188 public void onParentChanged(Call call, Call newParent) {
189 LogUtil.v(
190 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
191 update();
192 }
193
194 @Override
195 public void onChildrenChanged(Call call, List<Call> children) {
196 update();
197 }
198
199 @Override
200 public void onDetailsChanged(Call call, Call.Details details) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700201 LogUtil.v(
202 "TelecomCallCallback.onDetailsChanged", " call=" + call + " details=" + details);
Eric Erfanianccca3152017-02-22 16:32:36 -0800203 update();
204 }
205
206 @Override
207 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
208 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700209 "TelecomCallCallback.onCannedTextResponsesLoaded",
Eric Erfanianccca3152017-02-22 16:32:36 -0800210 "call=" + call + " cannedTextResponses=" + cannedTextResponses);
211 for (CannedTextResponsesLoadedListener listener : mCannedTextResponsesLoadedListeners) {
212 listener.onCannedTextResponsesLoaded(DialerCall.this);
213 }
214 }
215
216 @Override
217 public void onPostDialWait(Call call, String remainingPostDialSequence) {
218 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700219 "TelecomCallCallback.onPostDialWait",
Eric Erfanianccca3152017-02-22 16:32:36 -0800220 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
221 update();
222 }
223
224 @Override
225 public void onVideoCallChanged(Call call, VideoCall videoCall) {
226 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700227 "TelecomCallCallback.onVideoCallChanged", "call=" + call + " videoCall=" + videoCall);
Eric Erfanianccca3152017-02-22 16:32:36 -0800228 update();
229 }
230
231 @Override
232 public void onCallDestroyed(Call call) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700233 LogUtil.v("TelecomCallCallback.onCallDestroyed", "call=" + call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700234 unregisterCallback();
Eric Erfanianccca3152017-02-22 16:32:36 -0800235 }
236
237 @Override
238 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
239 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700240 "TelecomCallCallback.onConferenceableCallsChanged",
Eric Erfanianccca3152017-02-22 16:32:36 -0800241 "call %s, conferenceable calls: %d",
242 call,
243 conferenceableCalls.size());
244 update();
245 }
246
247 @Override
248 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
249 LogUtil.v(
Eric Erfanian2ca43182017-08-31 06:57:16 -0700250 "TelecomCallCallback.onConnectionEvent",
Eric Erfanianccca3152017-02-22 16:32:36 -0800251 "Call: " + call + ", Event: " + event + ", Extras: " + extras);
252 switch (event) {
253 // The Previous attempt to Merge two calls together has failed in Telecom. We must
254 // now update the UI to possibly re-enable the Merge button based on the number of
255 // currently conferenceable calls available or Connection Capabilities.
256 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
257 update();
258 break;
259 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
260 notifyWiFiToLteHandover();
261 break;
262 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
263 notifyHandoverToWifiFailed();
264 break;
265 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
266 isRemotelyHeld = true;
267 update();
268 break;
269 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
270 isRemotelyHeld = false;
271 update();
272 break;
Eric Erfanianc857f902017-05-15 14:05:33 -0700273 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC:
274 notifyInternationalCallOnWifi();
275 break;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700276 case TelephonyManagerCompat.EVENT_MERGE_START:
277 LogUtil.i("DialerCall.onConnectionEvent", "merge start");
278 isMergeInProcess = true;
279 break;
280 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE:
281 LogUtil.i("DialerCall.onConnectionEvent", "merge complete");
282 isMergeInProcess = false;
283 break;
Eric Erfanianccca3152017-02-22 16:32:36 -0800284 default:
285 break;
286 }
287 }
288 };
Eric Erfanianc857f902017-05-15 14:05:33 -0700289
Eric Erfanianccca3152017-02-22 16:32:36 -0800290 private long mTimeAddedMs;
291
292 public DialerCall(
293 Context context,
294 DialerCallDelegate dialerCallDelegate,
295 Call telecomCall,
296 LatencyReport latencyReport,
297 boolean registerCallback) {
298 Assert.isNotNull(context);
299 mContext = context;
300 mDialerCallDelegate = dialerCallDelegate;
301 mTelecomCall = telecomCall;
302 mLatencyReport = latencyReport;
303 mId = ID_PREFIX + Integer.toString(sIdCounter++);
304
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700305 // Must be after assigning mTelecomCall
306 mVideoTechManager = new VideoTechManager(this);
307
308 updateFromTelecomCall();
Eric Erfanianc857f902017-05-15 14:05:33 -0700309 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) {
310 mHiddenId = ++sHiddenCounter;
311 } else {
312 mHiddenId = 0;
313 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800314
315 if (registerCallback) {
316 mTelecomCall.registerCallback(mTelecomCallCallback);
317 }
318
319 mTimeAddedMs = System.currentTimeMillis();
320 parseCallSpecificAppData();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700321
322 updateEnrichedCallSession();
Eric Erfanianccca3152017-02-22 16:32:36 -0800323 }
324
twyendde01c52017-09-22 10:07:31 -0700325 /** Test only constructor to avoid initializing dependencies. */
326 @VisibleForTesting
327 DialerCall(Context context) {
328 mContext = context;
329 mTelecomCall = null;
330 mLatencyReport = null;
331 mId = null;
332 mHiddenId = 0;
333 mDialerCallDelegate = null;
334 mVideoTechManager = null;
335 }
336
Eric Erfanianccca3152017-02-22 16:32:36 -0800337 private static int translateState(int state) {
338 switch (state) {
339 case Call.STATE_NEW:
340 case Call.STATE_CONNECTING:
341 return DialerCall.State.CONNECTING;
342 case Call.STATE_SELECT_PHONE_ACCOUNT:
343 return DialerCall.State.SELECT_PHONE_ACCOUNT;
344 case Call.STATE_DIALING:
345 return DialerCall.State.DIALING;
346 case Call.STATE_PULLING_CALL:
347 return DialerCall.State.PULLING;
348 case Call.STATE_RINGING:
349 return DialerCall.State.INCOMING;
350 case Call.STATE_ACTIVE:
351 return DialerCall.State.ACTIVE;
352 case Call.STATE_HOLDING:
353 return DialerCall.State.ONHOLD;
354 case Call.STATE_DISCONNECTED:
355 return DialerCall.State.DISCONNECTED;
356 case Call.STATE_DISCONNECTING:
357 return DialerCall.State.DISCONNECTING;
358 default:
359 return DialerCall.State.INVALID;
360 }
361 }
362
363 public static boolean areSame(DialerCall call1, DialerCall call2) {
364 if (call1 == null && call2 == null) {
365 return true;
366 } else if (call1 == null || call2 == null) {
367 return false;
368 }
369
370 // otherwise compare call Ids
371 return call1.getId().equals(call2.getId());
372 }
373
374 public static boolean areSameNumber(DialerCall call1, DialerCall call2) {
375 if (call1 == null && call2 == null) {
376 return true;
377 } else if (call1 == null || call2 == null) {
378 return false;
379 }
380
381 // otherwise compare call Numbers
382 return TextUtils.equals(call1.getNumber(), call2.getNumber());
383 }
384
385 public void addListener(DialerCallListener listener) {
386 Assert.isMainThread();
387 mListeners.add(listener);
388 }
389
390 public void removeListener(DialerCallListener listener) {
391 Assert.isMainThread();
392 mListeners.remove(listener);
393 }
394
395 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
396 Assert.isMainThread();
397 mCannedTextResponsesLoadedListeners.add(listener);
398 }
399
400 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
401 Assert.isMainThread();
402 mCannedTextResponsesLoadedListeners.remove(listener);
403 }
404
405 public void notifyWiFiToLteHandover() {
406 LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
407 for (DialerCallListener listener : mListeners) {
408 listener.onWiFiToLteHandover();
409 }
410 }
411
412 public void notifyHandoverToWifiFailed() {
413 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
414 for (DialerCallListener listener : mListeners) {
415 listener.onHandoverToWifiFailure();
416 }
417 }
418
Eric Erfanianc857f902017-05-15 14:05:33 -0700419 public void notifyInternationalCallOnWifi() {
420 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
421 for (DialerCallListener dialerCallListener : mListeners) {
422 dialerCallListener.onInternationalCallOnWifi();
423 }
424 }
425
Eric Erfanianccca3152017-02-22 16:32:36 -0800426 /* package-private */ Call getTelecomCall() {
427 return mTelecomCall;
428 }
429
430 public StatusHints getStatusHints() {
431 return mTelecomCall.getDetails().getStatusHints();
432 }
433
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700434 public int getCameraDir() {
435 return mCameraDirection;
436 }
437
438 public void setCameraDir(int cameraDir) {
439 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING
440 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) {
441 mCameraDirection = cameraDir;
442 } else {
443 mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN;
444 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800445 }
446
Eric Erfanian2ca43182017-08-31 06:57:16 -0700447 public boolean wasParentCall() {
448 return mLogState.conferencedCalls != 0;
449 }
450
Eric Erfanianccca3152017-02-22 16:32:36 -0800451 private void update() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700452 Trace.beginSection("DialerCall.update");
Eric Erfanianccca3152017-02-22 16:32:36 -0800453 int oldState = getState();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700454 // Clear any cache here that could potentially change on update.
455 videoTech = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800456 // We want to potentially register a video call callback here.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700457 updateFromTelecomCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800458 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
459 for (DialerCallListener listener : mListeners) {
460 listener.onDialerCallDisconnect();
461 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700462 EnrichedCallComponent.get(mContext)
463 .getEnrichedCallManager()
464 .unregisterCapabilitiesListener(this);
465 EnrichedCallComponent.get(mContext)
466 .getEnrichedCallManager()
467 .unregisterStateChangedListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800468 } else {
469 for (DialerCallListener listener : mListeners) {
470 listener.onDialerCallUpdate();
471 }
472 }
473 Trace.endSection();
474 }
475
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700476 private void updateFromTelecomCall() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700477 Trace.beginSection("DialerCall.updateFromTelecomCall");
Eric Erfanianccca3152017-02-22 16:32:36 -0800478 LogUtil.v("DialerCall.updateFromTelecomCall", mTelecomCall.toString());
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700479
480 mVideoTechManager.dispatchCallStateChanged(mTelecomCall.getState());
481
Eric Erfanianccca3152017-02-22 16:32:36 -0800482 final int translatedState = translateState(mTelecomCall.getState());
483 if (mState != State.BLOCKED) {
484 setState(translatedState);
485 setDisconnectCause(mTelecomCall.getDetails().getDisconnectCause());
Eric Erfanianccca3152017-02-22 16:32:36 -0800486 }
487
488 mChildCallIds.clear();
489 final int numChildCalls = mTelecomCall.getChildren().size();
490 for (int i = 0; i < numChildCalls; i++) {
491 mChildCallIds.add(
492 mDialerCallDelegate
493 .getDialerCallFromTelecomCall(mTelecomCall.getChildren().get(i))
494 .getId());
495 }
496
497 // The number of conferenced calls can change over the course of the call, so use the
498 // maximum number of conferenced child calls as the metric for conference call usage.
499 mLogState.conferencedCalls = Math.max(numChildCalls, mLogState.conferencedCalls);
500
501 updateFromCallExtras(mTelecomCall.getDetails().getExtras());
502
503 // If the handle of the call has changed, update state for the call determining if it is an
504 // emergency call.
505 Uri newHandle = mTelecomCall.getDetails().getHandle();
506 if (!Objects.equals(mHandle, newHandle)) {
507 mHandle = newHandle;
508 updateEmergencyCallState();
509 }
510
511 // If the phone account handle of the call is set, cache capability bit indicating whether
512 // the phone account supports call subjects.
513 PhoneAccountHandle newPhoneAccountHandle = mTelecomCall.getDetails().getAccountHandle();
514 if (!Objects.equals(mPhoneAccountHandle, newPhoneAccountHandle)) {
515 mPhoneAccountHandle = newPhoneAccountHandle;
516
517 if (mPhoneAccountHandle != null) {
518 PhoneAccount phoneAccount =
519 mContext.getSystemService(TelecomManager.class).getPhoneAccount(mPhoneAccountHandle);
520 if (phoneAccount != null) {
521 mIsCallSubjectSupported =
522 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
523 }
524 }
525 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700526 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800527 }
528
529 /**
530 * Tests corruption of the {@code callExtras} bundle by calling {@link
531 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
532 * be thrown and caught by this function.
533 *
534 * @param callExtras the bundle to verify
535 * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
536 */
537 protected boolean areCallExtrasCorrupted(Bundle callExtras) {
538 /**
539 * There's currently a bug in Telephony service (b/25613098) that could corrupt the extras
540 * bundle, resulting in a IllegalArgumentException while validating data under {@link
541 * Bundle#containsKey(String)}.
542 */
543 try {
544 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
545 return false;
546 } catch (IllegalArgumentException e) {
547 LogUtil.e(
548 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
549 return true;
550 }
551 }
552
553 protected void updateFromCallExtras(Bundle callExtras) {
554 if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
555 /**
556 * If the bundle is corrupted, abandon information update as a work around. These are not
557 * critical for the dialer to function.
558 */
559 return;
560 }
561 // Check for a change in the child address and notify any listeners.
562 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
563 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
564 if (!Objects.equals(childNumber, mChildNumber)) {
565 mChildNumber = childNumber;
566 for (DialerCallListener listener : mListeners) {
567 listener.onDialerCallChildNumberChange();
568 }
569 }
570 }
571
572 // Last forwarded number comes in as an array of strings. We want to choose the
573 // last item in the array. The forwarding numbers arrive independently of when the
574 // call is originally set up, so we need to notify the the UI of the change.
575 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
576 ArrayList<String> lastForwardedNumbers =
577 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
578
579 if (lastForwardedNumbers != null) {
580 String lastForwardedNumber = null;
581 if (!lastForwardedNumbers.isEmpty()) {
582 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
583 }
584
585 if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) {
586 mLastForwardedNumber = lastForwardedNumber;
587 for (DialerCallListener listener : mListeners) {
588 listener.onDialerCallLastForwardedNumberChange();
589 }
590 }
591 }
592 }
593
594 // DialerCall subject is present in the extras at the start of call, so we do not need to
595 // notify any other listeners of this.
596 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
597 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
598 if (!Objects.equals(mCallSubject, callSubject)) {
599 mCallSubject = callSubject;
600 }
601 }
602 }
603
Eric Erfanianccca3152017-02-22 16:32:36 -0800604 public String getId() {
605 return mId;
606 }
607
Eric Erfanianc857f902017-05-15 14:05:33 -0700608 /**
609 * @return name appended with a number if the number is restricted/unknown and the user has
610 * received more than one restricted/unknown call.
611 */
612 @Nullable
613 public String updateNameIfRestricted(@Nullable String name) {
614 if (name != null && isHiddenNumber() && mHiddenId != 0 && sHiddenCounter > 1) {
615 return mContext.getString(R.string.unknown_counter, name, mHiddenId);
616 }
617 return name;
618 }
619
620 public static void clearRestrictedCount() {
621 sHiddenCounter = 0;
622 }
623
624 private boolean isHiddenNumber() {
625 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
626 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
627 }
628
Eric Erfanianccca3152017-02-22 16:32:36 -0800629 public boolean hasShownWiFiToLteHandoverToast() {
630 return hasShownWiFiToLteHandoverToast;
631 }
632
633 public void setHasShownWiFiToLteHandoverToast() {
634 hasShownWiFiToLteHandoverToast = true;
635 }
636
637 public boolean showWifiHandoverAlertAsToast() {
638 return doNotShowDialogForHandoffToWifiFailure;
639 }
640
641 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
642 doNotShowDialogForHandoffToWifiFailure = bool;
643 }
644
645 public long getTimeAddedMs() {
646 return mTimeAddedMs;
647 }
648
649 @Nullable
650 public String getNumber() {
651 return TelecomCallUtil.getNumber(mTelecomCall);
652 }
653
654 public void blockCall() {
655 mTelecomCall.reject(false, null);
656 setState(State.BLOCKED);
657 }
658
659 @Nullable
660 public Uri getHandle() {
661 return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandle();
662 }
663
664 public boolean isEmergencyCall() {
665 return mIsEmergencyCall;
666 }
667
668 public boolean isPotentialEmergencyCallback() {
669 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
670 // is actually in emergency callback mode (ie data is disabled).
671 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
672 return true;
673 }
674 // We want to treat any incoming call that arrives a short time after an outgoing emergency call
675 // as a potential emergency callback.
676 if (getExtras() != null
677 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
678 > 0) {
679 long lastEmergencyCallMillis =
680 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
681 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
682 return true;
683 }
684 }
685 return false;
686 }
687
688 boolean isInEmergencyCallbackWindow(long timestampMillis) {
689 long emergencyCallbackWindowMillis =
690 ConfigProviderBindings.get(mContext)
691 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
692 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
693 }
694
695 public int getState() {
696 if (mTelecomCall != null && mTelecomCall.getParent() != null) {
697 return State.CONFERENCED;
698 } else {
699 return mState;
700 }
701 }
702
703 public void setState(int state) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700704 if (state == State.INCOMING) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800705 mLogState.isIncoming = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700706 } else if (state == State.DISCONNECTED) {
707 long newDuration =
Eric Erfanianccca3152017-02-22 16:32:36 -0800708 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700709 if (mState != state) {
710 mLogState.duration = newDuration;
711 } else {
712 LogUtil.i(
713 "DialerCall.setState",
714 "ignoring state transition from DISCONNECTED to DISCONNECTED."
715 + " Duration would have changed from %s to %s",
716 mLogState.duration,
717 newDuration);
718 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800719 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700720 mState = state;
Eric Erfanianccca3152017-02-22 16:32:36 -0800721 }
722
723 public int getNumberPresentation() {
724 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getHandlePresentation();
725 }
726
727 public int getCnapNamePresentation() {
728 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getCallerDisplayNamePresentation();
729 }
730
731 @Nullable
732 public String getCnapName() {
733 return mTelecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
734 }
735
736 public Bundle getIntentExtras() {
737 return mTelecomCall.getDetails().getIntentExtras();
738 }
739
740 @Nullable
741 public Bundle getExtras() {
742 return mTelecomCall == null ? null : mTelecomCall.getDetails().getExtras();
743 }
744
745 /** @return The child number for the call, or {@code null} if none specified. */
746 public String getChildNumber() {
747 return mChildNumber;
748 }
749
750 /** @return The last forwarded number for the call, or {@code null} if none specified. */
751 public String getLastForwardedNumber() {
752 return mLastForwardedNumber;
753 }
754
755 /** @return The call subject, or {@code null} if none specified. */
756 public String getCallSubject() {
757 return mCallSubject;
758 }
759
760 /**
761 * @return {@code true} if the call's phone account supports call subjects, {@code false}
762 * otherwise.
763 */
764 public boolean isCallSubjectSupported() {
765 return mIsCallSubjectSupported;
766 }
767
768 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
769 public DisconnectCause getDisconnectCause() {
770 if (mState == State.DISCONNECTED || mState == State.IDLE) {
771 return mDisconnectCause;
772 }
773
774 return new DisconnectCause(DisconnectCause.UNKNOWN);
775 }
776
777 public void setDisconnectCause(DisconnectCause disconnectCause) {
778 mDisconnectCause = disconnectCause;
779 mLogState.disconnectCause = mDisconnectCause;
780 }
781
782 /** Returns the possible text message responses. */
783 public List<String> getCannedSmsResponses() {
784 return mTelecomCall.getCannedTextResponses();
785 }
786
787 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
788 public boolean can(int capabilities) {
789 int supportedCapabilities = mTelecomCall.getDetails().getCallCapabilities();
790
791 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
792 // We allow you to merge if the capabilities allow it or if it is a call with
793 // conferenceable calls.
794 if (mTelecomCall.getConferenceableCalls().isEmpty()
795 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
796 // Cannot merge calls if there are no calls to merge with.
797 return false;
798 }
799 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
800 }
801 return (capabilities == (capabilities & supportedCapabilities));
802 }
803
804 public boolean hasProperty(int property) {
805 return mTelecomCall.getDetails().hasProperty(property);
806 }
807
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700808 @NonNull
Eric Erfanianccca3152017-02-22 16:32:36 -0800809 public String getUniqueCallId() {
810 return uniqueCallId;
811 }
812
813 /** Gets the time when the call first became active. */
814 public long getConnectTimeMillis() {
815 return mTelecomCall.getDetails().getConnectTimeMillis();
816 }
817
818 public boolean isConferenceCall() {
819 return hasProperty(Call.Details.PROPERTY_CONFERENCE);
820 }
821
822 @Nullable
823 public GatewayInfo getGatewayInfo() {
824 return mTelecomCall == null ? null : mTelecomCall.getDetails().getGatewayInfo();
825 }
826
827 @Nullable
828 public PhoneAccountHandle getAccountHandle() {
829 return mTelecomCall == null ? null : mTelecomCall.getDetails().getAccountHandle();
830 }
831
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700832 /** @return The {@link VideoCall} instance associated with the {@link Call}. */
Eric Erfanianccca3152017-02-22 16:32:36 -0800833 public VideoCall getVideoCall() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700834 return mTelecomCall == null ? null : mTelecomCall.getVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800835 }
836
837 public List<String> getChildCallIds() {
838 return mChildCallIds;
839 }
840
841 public String getParentId() {
842 Call parentCall = mTelecomCall.getParent();
843 if (parentCall != null) {
844 return mDialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
845 }
846 return null;
847 }
848
849 public int getVideoState() {
850 return mTelecomCall.getDetails().getVideoState();
851 }
852
853 public boolean isVideoCall() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700854 return getVideoTech().isTransmittingOrReceiving();
855 }
856
857 public boolean hasReceivedVideoUpgradeRequest() {
858 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
859 }
860
861 public boolean hasSentVideoUpgradeRequest() {
862 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState());
Eric Erfanianccca3152017-02-22 16:32:36 -0800863 }
864
865 /**
866 * Determines if the call handle is an emergency number or not and caches the result to avoid
867 * repeated calls to isEmergencyNumber.
868 */
869 private void updateEmergencyCallState() {
870 mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
871 }
872
Eric Erfanianccca3152017-02-22 16:32:36 -0800873 public LogState getLogState() {
874 return mLogState;
875 }
876
877 /**
878 * Determines if the call is an external call.
879 *
880 * <p>An external call is one which does not exist locally for the {@link
881 * android.telecom.ConnectionService} it is associated with.
882 *
883 * <p>External calls are only supported in N and higher.
884 *
885 * @return {@code true} if the call is an external call, {@code false} otherwise.
886 */
887 public boolean isExternalCall() {
888 return VERSION.SDK_INT >= VERSION_CODES.N
889 && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
890 }
891
892 /**
Eric Erfanianccca3152017-02-22 16:32:36 -0800893 * Determines if answering this call will cause an ongoing video call to be dropped.
894 *
895 * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
896 * otherwise.
897 */
898 public boolean answeringDisconnectsForegroundVideoCall() {
899 Bundle extras = getExtras();
900 if (extras == null
901 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
902 return false;
903 }
904 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
905 }
906
907 private void parseCallSpecificAppData() {
908 if (isExternalCall()) {
909 return;
910 }
911
912 mLogState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
913 if (mLogState.callSpecificAppData == null) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700914
915 mLogState.callSpecificAppData =
916 CallSpecificAppData.newBuilder()
917 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION)
918 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800919 }
920 if (getState() == State.INCOMING) {
Eric Erfanian8369df02017-05-03 10:27:13 -0700921 mLogState.callSpecificAppData =
922 mLogState
923 .callSpecificAppData
924 .toBuilder()
925 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION)
926 .build();
Eric Erfanianccca3152017-02-22 16:32:36 -0800927 }
928 }
929
930 @Override
931 public String toString() {
932 if (mTelecomCall == null) {
933 // This should happen only in testing since otherwise we would never have a null
934 // Telecom call.
935 return String.valueOf(mId);
936 }
937
938 return String.format(
939 Locale.US,
940 "[%s, %s, %s, %s, children:%s, parent:%s, "
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700941 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]",
Eric Erfanianccca3152017-02-22 16:32:36 -0800942 mId,
943 State.toString(getState()),
944 Details.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
945 Details.propertiesToString(mTelecomCall.getDetails().getCallProperties()),
946 mChildCallIds,
947 getParentId(),
948 this.mTelecomCall.getConferenceableCalls(),
949 VideoProfile.videoStateToString(mTelecomCall.getDetails().getVideoState()),
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700950 getVideoTech().getSessionModificationState(),
951 getCameraDir());
Eric Erfanianccca3152017-02-22 16:32:36 -0800952 }
953
954 public String toSimpleString() {
955 return super.toString();
956 }
957
958 @CallHistoryStatus
959 public int getCallHistoryStatus() {
960 return mCallHistoryStatus;
961 }
962
963 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
964 mCallHistoryStatus = callHistoryStatus;
965 }
966
967 public boolean didShowCameraPermission() {
968 return didShowCameraPermission;
969 }
970
971 public void setDidShowCameraPermission(boolean didShow) {
972 didShowCameraPermission = didShow;
973 }
974
975 public boolean isInGlobalSpamList() {
976 return isInGlobalSpamList;
977 }
978
979 public void setIsInGlobalSpamList(boolean inSpamList) {
980 isInGlobalSpamList = inSpamList;
981 }
982
983 public boolean isInUserSpamList() {
984 return isInUserSpamList;
985 }
986
987 public void setIsInUserSpamList(boolean inSpamList) {
988 isInUserSpamList = inSpamList;
989 }
990
991 public boolean isInUserWhiteList() {
992 return isInUserWhiteList;
993 }
994
995 public void setIsInUserWhiteList(boolean inWhiteList) {
996 isInUserWhiteList = inWhiteList;
997 }
998
999 public boolean isSpam() {
1000 return mIsSpam;
1001 }
1002
1003 public void setSpam(boolean isSpam) {
1004 mIsSpam = isSpam;
1005 }
1006
1007 public boolean isBlocked() {
1008 return mIsBlocked;
1009 }
1010
1011 public void setBlockedStatus(boolean isBlocked) {
1012 mIsBlocked = isBlocked;
1013 }
1014
1015 public boolean isRemotelyHeld() {
1016 return isRemotelyHeld;
1017 }
1018
Eric Erfanian2ca43182017-08-31 06:57:16 -07001019 public boolean isMergeInProcess() {
1020 return isMergeInProcess;
1021 }
1022
Eric Erfanianccca3152017-02-22 16:32:36 -08001023 public boolean isIncoming() {
1024 return mLogState.isIncoming;
1025 }
1026
Eric Erfanian2ca43182017-08-31 06:57:16 -07001027 public boolean isAssistedDialed() {
1028 if (getIntentExtras() != null) {
1029 return getIntentExtras().getBoolean(TelephonyManagerCompat.IS_ASSISTED_DIALED, false);
1030 }
1031 return false;
1032 }
1033
Eric Erfanianccca3152017-02-22 16:32:36 -08001034 public LatencyReport getLatencyReport() {
1035 return mLatencyReport;
1036 }
1037
Eric Erfanian2ca43182017-08-31 06:57:16 -07001038 public int getAnswerAndReleaseButtonDisplayedTimes() {
1039 return answerAndReleaseButtonDisplayedTimes;
1040 }
1041
1042 public void increaseAnswerAndReleaseButtonDisplayedTimes() {
1043 answerAndReleaseButtonDisplayedTimes++;
1044 }
1045
1046 public boolean getReleasedByAnsweringSecondCall() {
1047 return releasedByAnsweringSecondCall;
1048 }
1049
1050 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
1051 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
1052 }
1053
1054 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
1055 return secondCallWithoutAnswerAndReleasedButtonTimes;
1056 }
1057
1058 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
1059 secondCallWithoutAnswerAndReleasedButtonTimes++;
1060 }
1061
Eric Erfanian8369df02017-05-03 10:27:13 -07001062 @Nullable
1063 public EnrichedCallCapabilities getEnrichedCallCapabilities() {
1064 return mEnrichedCallCapabilities;
1065 }
1066
1067 public void setEnrichedCallCapabilities(
1068 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
1069 this.mEnrichedCallCapabilities = mEnrichedCallCapabilities;
1070 }
1071
1072 @Nullable
1073 public Session getEnrichedCallSession() {
1074 return mEnrichedCallSession;
1075 }
1076
1077 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
1078 this.mEnrichedCallSession = mEnrichedCallSession;
1079 }
1080
Eric Erfanianccca3152017-02-22 16:32:36 -08001081 public void unregisterCallback() {
1082 mTelecomCall.unregisterCallback(mTelecomCallCallback);
1083 }
1084
Eric Erfanianccca3152017-02-22 16:32:36 -08001085 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
1086 LogUtil.i(
1087 "DialerCall.phoneAccountSelected",
1088 "accountHandle: %s, setDefault: %b",
1089 accountHandle,
1090 setDefault);
1091 mTelecomCall.phoneAccountSelected(accountHandle, setDefault);
1092 }
1093
1094 public void disconnect() {
1095 LogUtil.i("DialerCall.disconnect", "");
1096 setState(DialerCall.State.DISCONNECTING);
1097 for (DialerCallListener listener : mListeners) {
1098 listener.onDialerCallUpdate();
1099 }
1100 mTelecomCall.disconnect();
1101 }
1102
1103 public void hold() {
1104 LogUtil.i("DialerCall.hold", "");
1105 mTelecomCall.hold();
1106 }
1107
1108 public void unhold() {
1109 LogUtil.i("DialerCall.unhold", "");
1110 mTelecomCall.unhold();
1111 }
1112
1113 public void splitFromConference() {
1114 LogUtil.i("DialerCall.splitFromConference", "");
1115 mTelecomCall.splitFromConference();
1116 }
1117
1118 public void answer(int videoState) {
1119 LogUtil.i("DialerCall.answer", "videoState: " + videoState);
1120 mTelecomCall.answer(videoState);
1121 }
1122
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001123 public void answer() {
1124 answer(mTelecomCall.getDetails().getVideoState());
1125 }
1126
Eric Erfanianccca3152017-02-22 16:32:36 -08001127 public void reject(boolean rejectWithMessage, String message) {
1128 LogUtil.i("DialerCall.reject", "");
1129 mTelecomCall.reject(rejectWithMessage, message);
1130 }
1131
1132 /** Return the string label to represent the call provider */
1133 public String getCallProviderLabel() {
1134 if (callProviderLabel == null) {
1135 PhoneAccount account = getPhoneAccount();
1136 if (account != null && !TextUtils.isEmpty(account.getLabel())) {
1137 List<PhoneAccountHandle> accounts =
1138 mContext.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts();
1139 if (accounts != null && accounts.size() > 1) {
1140 callProviderLabel = account.getLabel().toString();
1141 }
1142 }
1143 if (callProviderLabel == null) {
1144 callProviderLabel = "";
1145 }
1146 }
1147 return callProviderLabel;
1148 }
1149
1150 private PhoneAccount getPhoneAccount() {
1151 PhoneAccountHandle accountHandle = getAccountHandle();
1152 if (accountHandle == null) {
1153 return null;
1154 }
1155 return mContext.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
1156 }
1157
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001158 public VideoTech getVideoTech() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001159 if (videoTech == null) {
1160 videoTech = mVideoTechManager.getVideoTech();
1161 }
1162 return videoTech;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001163 }
1164
Eric Erfanianccca3152017-02-22 16:32:36 -08001165 public String getCallbackNumber() {
1166 if (callbackNumber == null) {
1167 // Show the emergency callback number if either:
1168 // 1. This is an emergency call.
1169 // 2. The phone is in Emergency Callback Mode, which means we should show the callback
1170 // number.
1171 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
1172
1173 if (isEmergencyCall() || showCallbackNumber) {
wangqi339b4f32017-09-18 14:32:09 -07001174 callbackNumber =
1175 mContext.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
Eric Erfanianccca3152017-02-22 16:32:36 -08001176 }
1177
Eric Erfanianccca3152017-02-22 16:32:36 -08001178 if (callbackNumber == null) {
1179 callbackNumber = "";
1180 }
1181 }
1182 return callbackNumber;
1183 }
1184
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001185 @Override
1186 public void onVideoTechStateChanged() {
1187 update();
1188 }
1189
1190 @Override
1191 public void onSessionModificationStateChanged() {
1192 for (DialerCallListener listener : mListeners) {
1193 listener.onDialerCallSessionModificationStateChange();
1194 }
1195 }
1196
1197 @Override
1198 public void onCameraDimensionsChanged(int width, int height) {
1199 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
1200 }
1201
1202 @Override
1203 public void onPeerDimensionsChanged(int width, int height) {
1204 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
1205 }
1206
1207 @Override
1208 public void onVideoUpgradeRequestReceived() {
1209 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived");
1210
1211 for (DialerCallListener listener : mListeners) {
1212 listener.onDialerCallUpgradeToVideo();
1213 }
1214
1215 update();
Eric Erfanian8369df02017-05-03 10:27:13 -07001216
1217 Logger.get(mContext)
1218 .logCallImpression(
1219 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs());
1220 }
1221
1222 @Override
1223 public void onUpgradedToVideo(boolean switchToSpeaker) {
1224 LogUtil.enterBlock("DialerCall.onUpgradedToVideo");
1225
1226 if (!switchToSpeaker) {
1227 return;
1228 }
1229
1230 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState();
1231
1232 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
1233 LogUtil.e(
1234 "DialerCall.onUpgradedToVideo",
1235 "toggling speakerphone not allowed when bluetooth supported.");
1236 return;
1237 }
1238
1239 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
1240 return;
1241 }
1242
1243 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001244 }
1245
Eric Erfanian2ca43182017-08-31 06:57:16 -07001246 @Override
1247 public void onCapabilitiesUpdated() {
1248 if (getNumber() == null) {
1249 return;
1250 }
1251 EnrichedCallCapabilities capabilities =
1252 EnrichedCallComponent.get(mContext).getEnrichedCallManager().getCapabilities(getNumber());
1253 if (capabilities != null) {
1254 setEnrichedCallCapabilities(capabilities);
1255 update();
1256 }
1257 }
1258
1259 @Override
1260 public void onEnrichedCallStateChanged() {
1261 updateEnrichedCallSession();
1262 }
1263
1264 @Override
1265 public void onImpressionLoggingNeeded(DialerImpression.Type impressionType) {
1266 Logger.get(mContext).logCallImpression(impressionType, getUniqueCallId(), getTimeAddedMs());
twyendde01c52017-09-22 10:07:31 -07001267 if (impressionType == DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED) {
1268 if (getLogState().contactLookupResult == Type.NOT_FOUND) {
1269 Logger.get(mContext)
1270 .logCallImpression(
1271 DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_UPGRADE_REQUESTED,
1272 getUniqueCallId(),
1273 getTimeAddedMs());
1274 }
1275 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001276 }
1277
1278 private void updateEnrichedCallSession() {
1279 if (getNumber() == null) {
1280 return;
1281 }
1282 if (getEnrichedCallSession() != null) {
1283 // State changes to existing sessions are currently handled by the UI components (which have
1284 // their own listeners). Someday instead we could remove those and just call update() here and
1285 // have the usual onDialerCallUpdate update the UI.
1286 dispatchOnEnrichedCallSessionUpdate();
1287 return;
1288 }
1289
1290 EnrichedCallManager manager = EnrichedCallComponent.get(mContext).getEnrichedCallManager();
1291
1292 Filter filter =
1293 isIncoming()
1294 ? manager.createIncomingCallComposerFilter()
1295 : manager.createOutgoingCallComposerFilter();
1296
1297 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter);
1298 if (session == null) {
1299 return;
1300 }
1301
1302 session.setUniqueDialerCallId(getUniqueCallId());
1303 setEnrichedCallSession(session);
1304
1305 LogUtil.i(
1306 "DialerCall.updateEnrichedCallSession",
1307 "setting session %d's dialer id to %s",
1308 session.getSessionId(),
1309 getUniqueCallId());
1310
1311 dispatchOnEnrichedCallSessionUpdate();
1312 }
1313
1314 private void dispatchOnEnrichedCallSessionUpdate() {
1315 for (DialerCallListener listener : mListeners) {
1316 listener.onEnrichedCallSessionUpdate();
1317 }
1318 }
1319
1320 void onRemovedFromCallList() {
1321 // Ensure we clean up when this call is removed.
1322 mVideoTechManager.dispatchRemovedFromCallList();
1323 }
1324
yueg457b3972017-09-18 15:11:47 -07001325 public boolean isImsReachable() {
1326 return isImsReachable;
1327 }
1328
1329 private void setImsReachable(boolean imsReachable) {
1330 isImsReachable = imsReachable;
1331 }
1332
Eric Erfanianccca3152017-02-22 16:32:36 -08001333 /**
1334 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
1335 * means there is no result.
1336 */
1337 @IntDef({
1338 CALL_HISTORY_STATUS_UNKNOWN,
1339 CALL_HISTORY_STATUS_PRESENT,
1340 CALL_HISTORY_STATUS_NOT_PRESENT
1341 })
1342 @Retention(RetentionPolicy.SOURCE)
1343 public @interface CallHistoryStatus {}
1344
1345 /* Defines different states of this call */
1346 public static class State {
1347
1348 public static final int INVALID = 0;
1349 public static final int NEW = 1; /* The call is new. */
1350 public static final int IDLE = 2; /* The call is idle. Nothing active */
1351 public static final int ACTIVE = 3; /* There is an active call */
1352 public static final int INCOMING = 4; /* A normal incoming phone call */
1353 public static final int CALL_WAITING = 5; /* Incoming call while another is active */
1354 public static final int DIALING = 6; /* An outgoing call during dial phase */
1355 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
1356 public static final int ONHOLD = 8; /* An active phone call placed on hold */
1357 public static final int DISCONNECTING = 9; /* A call is being ended. */
1358 public static final int DISCONNECTED = 10; /* State after a call disconnects */
1359 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
1360 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
1361 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
1362 public static final int BLOCKED = 14; /* The number was found on the block list */
1363 public static final int PULLING = 15; /* An external call being pulled to the device */
Eric Erfanian2ca43182017-08-31 06:57:16 -07001364 public static final int CALL_PENDING = 16; /* A call is pending on a long process to finish */
Eric Erfanianccca3152017-02-22 16:32:36 -08001365
1366 public static boolean isConnectingOrConnected(int state) {
1367 switch (state) {
1368 case ACTIVE:
1369 case INCOMING:
1370 case CALL_WAITING:
1371 case CONNECTING:
1372 case DIALING:
1373 case PULLING:
1374 case REDIALING:
1375 case ONHOLD:
1376 case CONFERENCED:
1377 return true;
1378 default:
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001379 return false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001380 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001381 }
1382
1383 public static boolean isDialing(int state) {
1384 return state == DIALING || state == PULLING || state == REDIALING;
1385 }
1386
1387 public static String toString(int state) {
1388 switch (state) {
1389 case INVALID:
1390 return "INVALID";
1391 case NEW:
1392 return "NEW";
1393 case IDLE:
1394 return "IDLE";
1395 case ACTIVE:
1396 return "ACTIVE";
1397 case INCOMING:
1398 return "INCOMING";
1399 case CALL_WAITING:
1400 return "CALL_WAITING";
1401 case DIALING:
1402 return "DIALING";
1403 case PULLING:
1404 return "PULLING";
1405 case REDIALING:
1406 return "REDIALING";
1407 case ONHOLD:
1408 return "ONHOLD";
1409 case DISCONNECTING:
1410 return "DISCONNECTING";
1411 case DISCONNECTED:
1412 return "DISCONNECTED";
1413 case CONFERENCED:
1414 return "CONFERENCED";
1415 case SELECT_PHONE_ACCOUNT:
1416 return "SELECT_PHONE_ACCOUNT";
1417 case CONNECTING:
1418 return "CONNECTING";
1419 case BLOCKED:
1420 return "BLOCKED";
1421 default:
1422 return "UNKNOWN";
1423 }
1424 }
1425 }
1426
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001427 /** Camera direction constants */
1428 public static class CameraDirection {
Eric Erfanianccca3152017-02-22 16:32:36 -08001429 public static final int CAMERA_DIRECTION_UNKNOWN = -1;
1430 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
1431 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
Eric Erfanianccca3152017-02-22 16:32:36 -08001432 }
1433
1434 /**
1435 * Tracks any state variables that is useful for logging. There is some amount of overlap with
1436 * existing call member variables, but this duplication helps to ensure that none of these logging
1437 * variables will interface with/and affect call logic.
1438 */
1439 public static class LogState {
1440
1441 public DisconnectCause disconnectCause;
1442 public boolean isIncoming = false;
Eric Erfanian8369df02017-05-03 10:27:13 -07001443 public ContactLookupResult.Type contactLookupResult =
1444 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
Eric Erfanianccca3152017-02-22 16:32:36 -08001445 public CallSpecificAppData callSpecificAppData;
1446 // If this was a conference call, the total number of calls involved in the conference.
1447 public int conferencedCalls = 0;
1448 public long duration = 0;
1449 public boolean isLogged = false;
1450
Eric Erfanian8369df02017-05-03 10:27:13 -07001451 private static String lookupToString(ContactLookupResult.Type lookupType) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001452 switch (lookupType) {
Eric Erfanian8369df02017-05-03 10:27:13 -07001453 case LOCAL_CONTACT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001454 return "Local";
Eric Erfanian8369df02017-05-03 10:27:13 -07001455 case LOCAL_CACHE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001456 return "Cache";
Eric Erfanian8369df02017-05-03 10:27:13 -07001457 case REMOTE:
Eric Erfanianccca3152017-02-22 16:32:36 -08001458 return "Remote";
Eric Erfanian8369df02017-05-03 10:27:13 -07001459 case EMERGENCY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001460 return "Emergency";
Eric Erfanian8369df02017-05-03 10:27:13 -07001461 case VOICEMAIL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001462 return "Voicemail";
1463 default:
1464 return "Not found";
1465 }
1466 }
1467
1468 private static String initiationToString(CallSpecificAppData callSpecificAppData) {
1469 if (callSpecificAppData == null) {
1470 return "null";
1471 }
Eric Erfanian8369df02017-05-03 10:27:13 -07001472 switch (callSpecificAppData.getCallInitiationType()) {
1473 case INCOMING_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001474 return "Incoming";
Eric Erfanian8369df02017-05-03 10:27:13 -07001475 case DIALPAD:
Eric Erfanianccca3152017-02-22 16:32:36 -08001476 return "Dialpad";
Eric Erfanian8369df02017-05-03 10:27:13 -07001477 case SPEED_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001478 return "Speed Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001479 case REMOTE_DIRECTORY:
Eric Erfanianccca3152017-02-22 16:32:36 -08001480 return "Remote Directory";
Eric Erfanian8369df02017-05-03 10:27:13 -07001481 case SMART_DIAL:
Eric Erfanianccca3152017-02-22 16:32:36 -08001482 return "Smart Dial";
Eric Erfanian8369df02017-05-03 10:27:13 -07001483 case REGULAR_SEARCH:
Eric Erfanianccca3152017-02-22 16:32:36 -08001484 return "Regular Search";
Eric Erfanian8369df02017-05-03 10:27:13 -07001485 case CALL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001486 return "DialerCall Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001487 case CALL_LOG_FILTER:
Eric Erfanianccca3152017-02-22 16:32:36 -08001488 return "DialerCall Log Filter";
Eric Erfanian8369df02017-05-03 10:27:13 -07001489 case VOICEMAIL_LOG:
Eric Erfanianccca3152017-02-22 16:32:36 -08001490 return "Voicemail Log";
Eric Erfanian8369df02017-05-03 10:27:13 -07001491 case CALL_DETAILS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001492 return "DialerCall Details";
Eric Erfanian8369df02017-05-03 10:27:13 -07001493 case QUICK_CONTACTS:
Eric Erfanianccca3152017-02-22 16:32:36 -08001494 return "Quick Contacts";
Eric Erfanian8369df02017-05-03 10:27:13 -07001495 case EXTERNAL_INITIATION:
Eric Erfanianccca3152017-02-22 16:32:36 -08001496 return "External";
Eric Erfanian8369df02017-05-03 10:27:13 -07001497 case LAUNCHER_SHORTCUT:
Eric Erfanianccca3152017-02-22 16:32:36 -08001498 return "Launcher Shortcut";
1499 default:
Eric Erfanian8369df02017-05-03 10:27:13 -07001500 return "Unknown: " + callSpecificAppData.getCallInitiationType();
Eric Erfanianccca3152017-02-22 16:32:36 -08001501 }
1502 }
1503
1504 @Override
1505 public String toString() {
1506 return String.format(
1507 Locale.US,
1508 "["
1509 + "%s, " // DisconnectCause toString already describes the object type
1510 + "isIncoming: %s, "
1511 + "contactLookup: %s, "
1512 + "callInitiation: %s, "
1513 + "duration: %s"
1514 + "]",
1515 disconnectCause,
1516 isIncoming,
1517 lookupToString(contactLookupResult),
1518 initiationToString(callSpecificAppData),
1519 duration);
1520 }
1521 }
1522
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001523 private static class VideoTechManager {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001524 private final Context context;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001525 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech();
Eric Erfanian90508232017-03-24 09:31:16 -07001526 private final List<VideoTech> videoTechs;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001527 private VideoTech savedTech;
1528
1529 VideoTechManager(DialerCall call) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001530 this.context = call.mContext;
1531
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001532 String phoneNumber = call.getNumber();
Eric Erfaniand8046e52017-04-06 09:41:50 -07001533 phoneNumber = phoneNumber != null ? phoneNumber : "";
Eric Erfanian2ca43182017-08-31 06:57:16 -07001534 phoneNumber = phoneNumber.replaceAll("[^+0-9]", "");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001535
1536 // Insert order here determines the priority of that video tech option
Eric Erfanian8369df02017-05-03 10:27:13 -07001537 videoTechs = new ArrayList<>();
yueg457b3972017-09-18 15:11:47 -07001538
1539 ImsVideoTech imsVideoTech =
1540 new ImsVideoTech(Logger.get(call.mContext), call, call.mTelecomCall);
1541 call.setImsReachable(imsVideoTech.isAvailable(context));
1542 videoTechs.add(imsVideoTech);
Eric Erfanian90508232017-03-24 09:31:16 -07001543
1544 VideoTech rcsVideoTech =
1545 EnrichedCallComponent.get(call.mContext)
1546 .getRcsVideoShareFactory()
1547 .newRcsVideoShare(
1548 EnrichedCallComponent.get(call.mContext).getEnrichedCallManager(),
1549 call,
Eric Erfaniand8046e52017-04-06 09:41:50 -07001550 phoneNumber);
Eric Erfanian90508232017-03-24 09:31:16 -07001551 if (rcsVideoTech != null) {
1552 videoTechs.add(rcsVideoTech);
1553 }
Eric Erfaniand8046e52017-04-06 09:41:50 -07001554
1555 videoTechs.add(
1556 new LightbringerTech(
Eric Erfanian2ca43182017-08-31 06:57:16 -07001557 LightbringerComponent.get(call.mContext).getLightbringer(),
1558 call,
1559 call.mTelecomCall,
1560 phoneNumber));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001561 }
1562
1563 VideoTech getVideoTech() {
1564 if (savedTech != null) {
1565 return savedTech;
1566 }
1567
1568 for (VideoTech tech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001569 if (tech.isAvailable(context)) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001570 // Remember the first VideoTech that becomes available and always use it
1571 savedTech = tech;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001572 savedTech.becomePrimary();
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001573 return savedTech;
1574 }
1575 }
1576
1577 return emptyVideoTech;
1578 }
1579
1580 void dispatchCallStateChanged(int newState) {
1581 for (VideoTech videoTech : videoTechs) {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001582 videoTech.onCallStateChanged(context, newState);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001583 }
1584 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001585
1586 void dispatchRemovedFromCallList() {
1587 for (VideoTech videoTech : videoTechs) {
1588 videoTech.onRemovedFromCallList();
1589 }
1590 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001591 }
1592
Eric Erfanianccca3152017-02-22 16:32:36 -08001593 /** Called when canned text responses have been loaded. */
1594 public interface CannedTextResponsesLoadedListener {
1595 void onCannedTextResponsesLoaded(DialerCall call);
1596 }
1597}