1ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/*
2ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Copyright (C) 2017 The Android Open Source Project
3ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
4ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License");
5ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * you may not use this file except in compliance with the License.
6ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * You may obtain a copy of the License at
7ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
8ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *      http://www.apache.org/licenses/LICENSE-2.0
9ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
10ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Unless required by applicable law or agreed to in writing, software
11ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS,
12ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * See the License for the specific language governing permissions and
14ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * limitations under the License
15ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
16ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
17ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpackage com.android.incallui.call;
18ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
19ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.Context;
20ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Handler;
21ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Message;
22ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Trace;
23ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.NonNull;
24ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.Nullable;
25ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.VisibleForTesting;
26ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v4.os.BuildCompat;
27ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.telecom.Call;
28ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.telecom.DisconnectCause;
29ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.telecom.PhoneAccount;
30ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.util.ArrayMap;
31ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
32ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.blocking.FilteredNumbersUtil;
33ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.common.Assert;
34ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.common.LogUtil;
352f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanianimport com.android.dialer.enrichedcall.EnrichedCallComponent;
362f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanianimport com.android.dialer.enrichedcall.EnrichedCallManager;
3710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport com.android.dialer.location.GeoUtil;
388369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanianimport com.android.dialer.logging.DialerImpression;
39ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.logging.Logger;
40ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.shortcuts.ShortcutUsageReporter;
41ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.spam.Spam;
42ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.spam.SpamBindings;
43ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.call.DialerCall.State;
44ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.latencyreport.LatencyReport;
45ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.util.TelecomCallUtil;
469050823ccf6f512e06ad65c8a741cb17cbc4a833Eric Erfanianimport com.android.incallui.videotech.utils.SessionModificationState;
47ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Collections;
48ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Iterator;
49ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Map;
50ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Objects;
51ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Set;
52ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.concurrent.ConcurrentHashMap;
53ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
54ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/**
55ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Maintains the list of active calls and notifies interested classes of changes to the call list as
56ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * they are received from the telephony stack. Primary listener of changes to this class is
57ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * InCallPresenter.
58ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
59ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpublic class CallList implements DialerCallDelegate {
60ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
61ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int DISCONNECTED_CALL_SHORT_TIMEOUT_MS = 200;
62ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS = 2000;
63ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int DISCONNECTED_CALL_LONG_TIMEOUT_MS = 5000;
64ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
65ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int EVENT_DISCONNECTED_TIMEOUT = 1;
66ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
67ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static CallList sInstance = new CallList();
68ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
69ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Map<String, DialerCall> mCallById = new ArrayMap<>();
70ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Map<android.telecom.Call, DialerCall> mCallByTelecomCall = new ArrayMap<>();
71ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
72ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
73ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is load factor before
74ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * resizing, 1 means we only expect a single thread to access the map so make only a single shard
75ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
76ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Set<Listener> mListeners =
77ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Collections.newSetFromMap(new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
78ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
79ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Set<DialerCall> mPendingDisconnectCalls =
80ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Collections.newSetFromMap(new ConcurrentHashMap<DialerCall, Boolean>(8, 0.9f, 1));
81ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Handles the timeout for destroying disconnected calls. */
82ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Handler mHandler =
83ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      new Handler() {
84ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        @Override
85ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        public void handleMessage(Message msg) {
86ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          switch (msg.what) {
87ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            case EVENT_DISCONNECTED_TIMEOUT:
88ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              LogUtil.d("CallList.handleMessage", "EVENT_DISCONNECTED_TIMEOUT ", msg.obj);
89ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              finishDisconnectedCall((DialerCall) msg.obj);
90ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              break;
91ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            default:
92ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              LogUtil.e("CallList.handleMessage", "Message not expected: " + msg.what);
93ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              break;
94ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
95ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
96ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      };
97ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
98ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
99ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * USED ONLY FOR TESTING Testing-only constructor. Instance should only be acquired through
10010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian   * getRunningInstance().
101ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
102ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @VisibleForTesting
103ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public CallList() {}
104ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
105d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian  @VisibleForTesting
106d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian  public static void setCallListInstance(CallList callList) {
107d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian    sInstance = callList;
108d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian  }
109d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian
110ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Static singleton accessor method. */
111ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public static CallList getInstance() {
112ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return sInstance;
113ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
114ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
115ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onCallAdded(
116ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
117ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Trace.beginSection("onCallAdded");
118ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final DialerCall call =
119ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new DialerCall(context, this, telecomCall, latencyReport, true /* registerCallback */);
120d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    logSecondIncomingCall(context, call);
121d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
1222f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian    EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
1232f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian    manager.registerCapabilitiesListener(call);
1242f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian    manager.registerStateChangedListener(call);
1252f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian
126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final DialerCallListenerImpl dialerCallListener = new DialerCallListenerImpl(call);
127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    call.addListener(dialerCallListener);
128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    LogUtil.d("CallList.onCallAdded", "callState=" + call.getState());
129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (Spam.get(context).isSpamEnabled()) {
130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      String number = TelecomCallUtil.getNumber(telecomCall);
131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Spam.get(context)
132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          .checkSpamStatus(
133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              number,
134ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              null,
135ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              new SpamBindings.Listener() {
136ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                @Override
137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                public void onComplete(boolean isSpam) {
138d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                  boolean isIncomingCall =
139d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                      call.getState() == DialerCall.State.INCOMING
140d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                          || call.getState() == DialerCall.State.CALL_WAITING;
141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  if (isSpam) {
142d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                    if (!isIncomingCall) {
143ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                      LogUtil.i(
144ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                          "CallList.onCallAdded",
145ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                          "marking spam call as not spam because it's not an incoming call");
146ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                      isSpam = false;
147ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                    } else if (isPotentialEmergencyCallback(context, call)) {
148ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                      LogUtil.i(
149ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                          "CallList.onCallAdded",
150ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                          "marking spam call as not spam because an emergency call was made on this"
151ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                              + " device recently");
152ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                      isSpam = false;
153ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                    }
154ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  }
155ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
156d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                  if (isIncomingCall) {
157d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                    Logger.get(context)
158d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                        .logCallImpression(
159d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                            isSpam
160d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                                ? DialerImpression.Type.INCOMING_SPAM_CALL
161d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                                : DialerImpression.Type.INCOMING_NON_SPAM_CALL,
162d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                            call.getUniqueCallId(),
163d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                            call.getTimeAddedMs());
164d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian                  }
165ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  call.setSpam(isSpam);
166ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  dialerCallListener.onDialerCallUpdate();
167ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                }
168ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              });
169ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
170ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      updateUserMarkedSpamStatus(call, context, number, dialerCallListener);
171ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
172ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
173ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler =
174ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new FilteredNumberAsyncQueryHandler(context);
175ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
176ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    filteredNumberAsyncQueryHandler.isBlockedNumber(
177ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
178ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          @Override
179ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          public void onCheckComplete(Integer id) {
180ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            if (id != null && id != FilteredNumberAsyncQueryHandler.INVALID_ID) {
181ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              call.setBlockedStatus(true);
182ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              dialerCallListener.onDialerCallUpdate();
183ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
184ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
185ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        },
186ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        call.getNumber(),
187ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        GeoUtil.getCurrentCountryIso(context));
188ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
189ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.getState() == DialerCall.State.INCOMING
190ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        || call.getState() == DialerCall.State.CALL_WAITING) {
191ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      onIncoming(call);
192ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
193ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      dialerCallListener.onDialerCallUpdate();
194ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
195ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
196ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.getState() != State.INCOMING) {
197ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Only report outgoing calls
198ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      ShortcutUsageReporter.onOutgoingCallAdded(context, call.getNumber());
199ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
200ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
201ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Trace.endSection();
202ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
203ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
204d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private void logSecondIncomingCall(@NonNull Context context, @NonNull DialerCall incomingCall) {
205d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    DialerCall firstCall = getFirstCall();
206d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    if (firstCall != null) {
2078369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian      DialerImpression.Type impression;
208d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      if (firstCall.isVideoCall()) {
209d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        if (incomingCall.isVideoCall()) {
210d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian          impression = DialerImpression.Type.VIDEO_CALL_WITH_INCOMING_VIDEO_CALL;
211d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        } else {
212d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian          impression = DialerImpression.Type.VIDEO_CALL_WITH_INCOMING_VOICE_CALL;
213d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        }
214d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      } else {
215d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        if (incomingCall.isVideoCall()) {
216d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian          impression = DialerImpression.Type.VOICE_CALL_WITH_INCOMING_VIDEO_CALL;
217d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        } else {
218d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian          impression = DialerImpression.Type.VOICE_CALL_WITH_INCOMING_VOICE_CALL;
219d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        }
220d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      }
2218369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian      Assert.checkArgument(impression != null);
222d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      Logger.get(context)
223d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian          .logCallImpression(
224d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian              impression, incomingCall.getUniqueCallId(), incomingCall.getTimeAddedMs());
225d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
226d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
227d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
228ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static boolean isPotentialEmergencyCallback(Context context, DialerCall call) {
229ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (BuildCompat.isAtLeastO()) {
230ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return call.isPotentialEmergencyCallback();
231ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
232ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      long timestampMillis = FilteredNumbersUtil.getLastEmergencyCallTimeMillis(context);
233ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return call.isInEmergencyCallbackWindow(timestampMillis);
234ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
235ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
236ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
237ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
238ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getDialerCallFromTelecomCall(Call telecomCall) {
239ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return mCallByTelecomCall.get(telecomCall);
240ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
241ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
242ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void updateUserMarkedSpamStatus(
243ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      final DialerCall call,
244ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      final Context context,
245ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      String number,
246ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      final DialerCallListenerImpl dialerCallListener) {
247ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
248ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Spam.get(context)
249ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        .checkUserMarkedNonSpamStatus(
250ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            number,
251ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            null,
252ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            new SpamBindings.Listener() {
253ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              @Override
254ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              public void onComplete(boolean isInUserWhiteList) {
255ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                call.setIsInUserWhiteList(isInUserWhiteList);
256ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              }
257ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            });
258ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
259ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Spam.get(context)
260ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        .checkGlobalSpamListStatus(
261ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            number,
262ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            null,
263ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            new SpamBindings.Listener() {
264ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              @Override
265ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              public void onComplete(boolean isInGlobalSpamList) {
266ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                call.setIsInGlobalSpamList(isInGlobalSpamList);
267ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              }
268ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            });
269ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
270ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Spam.get(context)
271ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        .checkUserMarkedSpamStatus(
272ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            number,
273ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            null,
274ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            new SpamBindings.Listener() {
275ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              @Override
276ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              public void onComplete(boolean isInUserSpamList) {
277ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                call.setIsInUserSpamList(isInUserSpamList);
278ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              }
279ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            });
280ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
281ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
282ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onCallRemoved(Context context, android.telecom.Call telecomCall) {
283ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mCallByTelecomCall.containsKey(telecomCall)) {
284ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      DialerCall call = mCallByTelecomCall.get(telecomCall);
285ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Assert.checkArgument(!call.isExternalCall());
286ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
2872f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian      EnrichedCallManager manager = EnrichedCallComponent.get(context).getEnrichedCallManager();
2882f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian      manager.unregisterCapabilitiesListener(call);
2892f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian      manager.unregisterStateChangedListener(call);
2902f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian
291ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Don't log an already logged call. logCall() might be called multiple times
292ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // for the same call due to b/24109437.
293ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (call.getLogState() != null && !call.getLogState().isLogged) {
294ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getLegacyBindings(context).logCall(call);
295ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        call.getLogState().isLogged = true;
296ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
297ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
298ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (updateCallInMap(call)) {
299ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        LogUtil.w(
300ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            "CallList.onCallRemoved", "Removing call not previously disconnected " + call.getId());
301ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
3022f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian
3032f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian      call.onRemovedFromCallList();
304ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
305c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian
306c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    if (!hasLiveCall()) {
307c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      DialerCall.clearRestrictedCount();
308c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    }
309ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
310ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
311ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  InCallUiLegacyBindings getLegacyBindings(Context context) {
312ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Objects.requireNonNull(context);
313ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
314ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Context application = context.getApplicationContext();
315ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    InCallUiLegacyBindings legacyInstance = null;
316ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (application instanceof InCallUiLegacyBindingsFactory) {
317ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      legacyInstance = ((InCallUiLegacyBindingsFactory) application).newInCallUiLegacyBindings();
318ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
319ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
320ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (legacyInstance == null) {
321ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      legacyInstance = new InCallUiLegacyBindingsStub();
322ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
323ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return legacyInstance;
324ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
325ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
326ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
327ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Handles the case where an internal call has become an exteral call. We need to
328ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
329ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param context
330ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param telecomCall
331ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
332ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onInternalCallMadeExternal(Context context, android.telecom.Call telecomCall) {
333ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
334ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mCallByTelecomCall.containsKey(telecomCall)) {
335ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      DialerCall call = mCallByTelecomCall.get(telecomCall);
336ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
337ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Don't log an already logged call. logCall() might be called multiple times
338ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // for the same call due to b/24109437.
339ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (call.getLogState() != null && !call.getLogState().isLogged) {
340ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getLegacyBindings(context).logCall(call);
341ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        call.getLogState().isLogged = true;
342ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
343ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
344ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // When removing a call from the call list because it became an external call, we need to
345ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // ensure the callback is unregistered -- this is normally only done when calls disconnect.
346ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // However, the call won't be disconnected in this case.  Also, logic in updateCallInMap
347ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // would just re-add the call anyways.
348ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call.unregisterCallback();
349ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallById.remove(call.getId());
350ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallByTelecomCall.remove(telecomCall);
351ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
352ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
353ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
354ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Called when a single call has changed. */
355ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void onIncoming(DialerCall call) {
356ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (updateCallInMap(call)) {
357ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      LogUtil.i("CallList.onIncoming", String.valueOf(call));
358ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
359ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
360ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (Listener listener : mListeners) {
361ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      listener.onIncomingCall(call);
362ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
363ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
364ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
365ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void addListener(@NonNull Listener listener) {
366ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Objects.requireNonNull(listener);
367ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
368ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mListeners.add(listener);
369ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
370ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Let the listener know about the active calls immediately.
371ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    listener.onCallListChange(this);
372ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
373ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
374ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void removeListener(@Nullable Listener listener) {
375ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (listener != null) {
376ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mListeners.remove(listener);
377ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
378ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
379ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
380ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
381ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * TODO: Change so that this function is not needed. Instead of assuming there is an active call,
382ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * the code should rely on the status of a specific DialerCall and allow the presenters to update
383ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * the DialerCall object when the active call changes.
384ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
385ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getIncomingOrActive() {
386ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall retval = getIncomingCall();
387ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (retval == null) {
388ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      retval = getActiveCall();
389ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
390ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return retval;
391ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
392ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
393ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getOutgoingOrActive() {
394ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall retval = getOutgoingCall();
395ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (retval == null) {
396ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      retval = getActiveCall();
397ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
398ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return retval;
399ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
400ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
401ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** A call that is waiting for {@link PhoneAccount} selection */
402ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getWaitingForAccountCall() {
403ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getFirstCallWithState(DialerCall.State.SELECT_PHONE_ACCOUNT);
404ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
405ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
406ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getPendingOutgoingCall() {
407ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getFirstCallWithState(DialerCall.State.CONNECTING);
408ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
409ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
410ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getOutgoingCall() {
411ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall call = getFirstCallWithState(DialerCall.State.DIALING);
412ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call == null) {
413ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call = getFirstCallWithState(DialerCall.State.REDIALING);
414ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
415ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call == null) {
416ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call = getFirstCallWithState(DialerCall.State.PULLING);
417ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
418ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return call;
419ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
420ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
421ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getActiveCall() {
422ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getFirstCallWithState(DialerCall.State.ACTIVE);
423ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
424ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
425ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getSecondActiveCall() {
426ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getCallWithState(DialerCall.State.ACTIVE, 1);
427ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
428ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
429ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getBackgroundCall() {
430ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getFirstCallWithState(DialerCall.State.ONHOLD);
431ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
432ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
433ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getDisconnectedCall() {
434ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getFirstCallWithState(DialerCall.State.DISCONNECTED);
435ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
436ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
437ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getDisconnectingCall() {
438ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getFirstCallWithState(DialerCall.State.DISCONNECTING);
439ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
440ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
441ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getSecondBackgroundCall() {
442ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getCallWithState(DialerCall.State.ONHOLD, 1);
443ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
444ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
445ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getActiveOrBackgroundCall() {
446ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall call = getActiveCall();
447ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call == null) {
448ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call = getBackgroundCall();
449ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
450ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return call;
451ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
452ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
453ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getIncomingCall() {
454ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall call = getFirstCallWithState(DialerCall.State.INCOMING);
455ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call == null) {
456ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call = getFirstCallWithState(DialerCall.State.CALL_WAITING);
457ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
458ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
459ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return call;
460ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
461ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
462ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getFirstCall() {
463ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall result = getIncomingCall();
464ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (result == null) {
465ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      result = getPendingOutgoingCall();
466ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
467ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (result == null) {
468ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      result = getOutgoingCall();
469ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
470ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (result == null) {
471ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      result = getFirstCallWithState(DialerCall.State.ACTIVE);
472ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
473ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (result == null) {
474ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      result = getDisconnectingCall();
475ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
476ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (result == null) {
477ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      result = getDisconnectedCall();
478ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
479ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return result;
480ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
481ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
482ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public boolean hasLiveCall() {
483ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall call = getFirstCall();
484ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return call != null && call != getDisconnectingCall() && call != getDisconnectedCall();
485ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
486ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
487ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
488ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns the first call found in the call map with the upgrade to video modification state.
489ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
490ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @return The first call with the upgrade to video state.
491ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
492ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getVideoUpgradeRequestCall() {
493ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (DialerCall call : mCallById.values()) {
494d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      if (call.getVideoTech().getSessionModificationState()
4959050823ccf6f512e06ad65c8a741cb17cbc4a833Eric Erfanian          == SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
496ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        return call;
497ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
498ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
499ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return null;
500ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
501ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
502ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getCallById(String callId) {
503ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return mCallById.get(callId);
504ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
505ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
506ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Returns first call found in the call map with the specified state. */
507ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getFirstCallWithState(int state) {
508ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getCallWithState(state, 0);
509ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
510ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
511ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
512ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns the [position]th call found in the call map with the specified state. TODO: Improve
513ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * this logic to sort by call time.
514ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
515ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DialerCall getCallWithState(int state, int positionToFind) {
516ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall retval = null;
517ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    int position = 0;
518ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (DialerCall call : mCallById.values()) {
519ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (call.getState() == state) {
520ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        if (position >= positionToFind) {
521ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          retval = call;
522ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          break;
523ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        } else {
524ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          position++;
525ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
526ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
527ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
528ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
529ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return retval;
530ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
531ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
532ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
533ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * This is called when the service disconnects, either expectedly or unexpectedly. For the
534ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * expected case, it's because we have no calls left. For the unexpected case, it is likely a
535ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * crash of phone and we need to clean up our calls manually. Without phone, there can be no
536ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * active calls, so this is relatively safe thing to do.
537ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
538ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void clearOnDisconnect() {
539ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (DialerCall call : mCallById.values()) {
540ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      final int state = call.getState();
541ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (state != DialerCall.State.IDLE
542ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          && state != DialerCall.State.INVALID
543ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          && state != DialerCall.State.DISCONNECTED) {
544ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
545ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        call.setState(DialerCall.State.DISCONNECTED);
546ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        call.setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN));
547ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        updateCallInMap(call);
548ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
549ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
550ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    notifyGenericListeners();
551ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
552ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
553ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
554ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Called when the user has dismissed an error dialog. This indicates acknowledgement of the
555ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * disconnect cause, and that any pending disconnects should immediately occur.
556ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
557ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onErrorDialogDismissed() {
558ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Iterator<DialerCall> iterator = mPendingDisconnectCalls.iterator();
559ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    while (iterator.hasNext()) {
560ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      DialerCall call = iterator.next();
561ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      iterator.remove();
562ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      finishDisconnectedCall(call);
563ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
564ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
565ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
566ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
567ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Processes an update for a single call.
568ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
569ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param call The call to update.
570ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
5712f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian  @VisibleForTesting
5722f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian  void onUpdateCall(DialerCall call) {
573ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    LogUtil.d("CallList.onUpdateCall", String.valueOf(call));
574ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (!mCallById.containsKey(call.getId()) && call.isExternalCall()) {
575ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // When a regular call becomes external, it is removed from the call list, and there may be
576ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // pending updates to Telecom which are queued up on the Telecom call's handler which we no
577ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // longer wish to cause updates to the call in the CallList.  Bail here if the list of tracked
578ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // calls doesn't contain the call which received the update.
579ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return;
580ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
581ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
582ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (updateCallInMap(call)) {
583ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      LogUtil.i("CallList.onUpdateCall", String.valueOf(call));
584ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
585ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
586ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
587ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
588ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Sends a generic notification to all listeners that something has changed. It is up to the
589ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * listeners to call back to determine what changed.
590ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
591ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void notifyGenericListeners() {
592ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (Listener listener : mListeners) {
593ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      listener.onCallListChange(this);
594ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
595ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
596ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
597ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void notifyListenersOfDisconnect(DialerCall call) {
598ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (Listener listener : mListeners) {
599ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      listener.onDisconnect(call);
600ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
601ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
602ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
603ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
604ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Updates the call entry in the local map.
605ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
606ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @return false if no call previously existed and no call was added, otherwise true.
607ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
608ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private boolean updateCallInMap(DialerCall call) {
609ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Objects.requireNonNull(call);
610ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
611ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    boolean updated = false;
612ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
613ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.getState() == DialerCall.State.DISCONNECTED) {
614ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // update existing (but do not add!!) disconnected calls
615ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (mCallById.containsKey(call.getId())) {
616ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // For disconnected calls, we want to keep them alive for a few seconds so that the
617ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // UI has a chance to display anything it needs when a call is disconnected.
618ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
619ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // Set up a timer to destroy the call after X seconds.
620ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        final Message msg = mHandler.obtainMessage(EVENT_DISCONNECTED_TIMEOUT, call);
621ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mHandler.sendMessageDelayed(msg, getDelayForDisconnect(call));
622ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mPendingDisconnectCalls.add(call);
623ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
624ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mCallById.put(call.getId(), call);
625ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mCallByTelecomCall.put(call.getTelecomCall(), call);
626ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        updated = true;
627ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
628ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (!isCallDead(call)) {
629ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallById.put(call.getId(), call);
630ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallByTelecomCall.put(call.getTelecomCall(), call);
631ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      updated = true;
632ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (mCallById.containsKey(call.getId())) {
633ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallById.remove(call.getId());
634ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallByTelecomCall.remove(call.getTelecomCall());
635ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      updated = true;
636ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
637ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
638ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return updated;
639ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
640ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
641ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private int getDelayForDisconnect(DialerCall call) {
642ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.getState() != DialerCall.State.DISCONNECTED) {
643ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      throw new IllegalStateException();
644ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
645ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
646ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int cause = call.getDisconnectCause().getCode();
647ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int delay;
648ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    switch (cause) {
649ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case DisconnectCause.LOCAL:
650ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        delay = DISCONNECTED_CALL_SHORT_TIMEOUT_MS;
651ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        break;
652ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case DisconnectCause.REMOTE:
653ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case DisconnectCause.ERROR:
654ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        delay = DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS;
655ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        break;
656ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case DisconnectCause.REJECTED:
657ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case DisconnectCause.MISSED:
658ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case DisconnectCause.CANCELED:
659ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // no delay for missed/rejected incoming calls and canceled outgoing calls.
660ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        delay = 0;
661ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        break;
662ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      default:
663ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        delay = DISCONNECTED_CALL_LONG_TIMEOUT_MS;
664ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        break;
665ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
666ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
667ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return delay;
668ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
669ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
670ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private boolean isCallDead(DialerCall call) {
671ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int state = call.getState();
672ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return DialerCall.State.IDLE == state || DialerCall.State.INVALID == state;
673ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
674ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
675ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Sets up a call for deletion and notifies listeners of change. */
676ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void finishDisconnectedCall(DialerCall call) {
677ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mPendingDisconnectCalls.contains(call)) {
678ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mPendingDisconnectCalls.remove(call);
679ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
680ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    call.setState(DialerCall.State.IDLE);
681ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    updateCallInMap(call);
682ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    notifyGenericListeners();
683ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
684ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
685ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
686ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Notifies all video calls of a change in device orientation.
687ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
688ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param rotation The new rotation angle (in degrees).
689ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
690ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void notifyCallsOfDeviceRotation(int rotation) {
691ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (DialerCall call : mCallById.values()) {
692d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      call.getVideoTech().setDeviceOrientation(rotation);
693ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
694ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
695ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
696ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onInCallUiShown(boolean forFullScreenIntent) {
697ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (DialerCall call : mCallById.values()) {
698ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call.getLatencyReport().onInCallUiShown(forFullScreenIntent);
699ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
700ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
701ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
702ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Listener interface for any class that wants to be notified of changes to the call list. */
703ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public interface Listener {
704ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
705ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
706ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Called when a new incoming call comes in. This is the only method that gets called for
707ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * incoming calls. Listeners that want to perform an action on incoming call should respond in
708ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * this method because {@link #onCallListChange} does not automatically get called for incoming
709ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * calls.
710ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
711ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void onIncomingCall(DialerCall call);
712ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
713ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
714ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Called when a new modify call request comes in This is the only method that gets called for
715ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * modify requests.
716ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
717ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void onUpgradeToVideo(DialerCall call);
718ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
719ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /** Called when the session modification state of a call changes. */
720d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    void onSessionModificationStateChange(DialerCall call);
721ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
722ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
723ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Called anytime there are changes to the call list. The change can be switching call states,
724ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * updating information, etc. This method will NOT be called for new incoming calls and for
725ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * calls that switch to disconnected state. Listeners must add actions to those method
726ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * implementations if they want to deal with those actions.
727ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
728ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void onCallListChange(CallList callList);
729ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
730ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
731ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Called when a call switches to the disconnected state. This is the only method that will get
732ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * called upon disconnection.
733ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
734ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void onDisconnect(DialerCall call);
735ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
736ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void onWiFiToLteHandover(DialerCall call);
737ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
738ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
739ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Called when a user is in a video call and the call is unable to be handed off successfully to
740ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * WiFi
741ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
742ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void onHandoverToWifiFailed(DialerCall call);
743c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian
744c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    /** Called when the user initiates a call to an international number while on WiFi. */
745c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    void onInternationalCallOnWifi(@NonNull DialerCall call);
746ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
747ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
748ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private class DialerCallListenerImpl implements DialerCallListener {
749ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
750c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    @NonNull private final DialerCall mCall;
751ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
752c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    DialerCallListenerImpl(@NonNull DialerCall call) {
753c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      mCall = Assert.isNotNull(call);
754ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
755ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
756ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
757ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallDisconnect() {
758ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (updateCallInMap(mCall)) {
759ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        LogUtil.i("DialerCallListenerImpl.onDialerCallDisconnect", String.valueOf(mCall));
760ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // notify those listening for all disconnects
761ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        notifyListenersOfDisconnect(mCall);
762ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
763ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
764ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
765ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
766ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallUpdate() {
767ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Trace.beginSection("onUpdate");
768ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      onUpdateCall(mCall);
769ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      notifyGenericListeners();
770ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Trace.endSection();
771ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
772ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
773ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
774ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallChildNumberChange() {}
775ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
776ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
777ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallLastForwardedNumberChange() {}
778ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
779ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
780ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallUpgradeToVideo() {
781ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      for (Listener listener : mListeners) {
782ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        listener.onUpgradeToVideo(mCall);
783ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
784ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
785ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
786ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
787ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onWiFiToLteHandover() {
788ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      for (Listener listener : mListeners) {
789ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        listener.onWiFiToLteHandover(mCall);
790ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
791ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
792ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
793ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
794ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onHandoverToWifiFailure() {
795ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      for (Listener listener : mListeners) {
796ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        listener.onHandoverToWifiFailed(mCall);
797ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
798ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
799ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
800ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
801c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    public void onInternationalCallOnWifi() {
802c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      LogUtil.enterBlock("DialerCallListenerImpl.onInternationalCallOnWifi");
803c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      for (Listener listener : mListeners) {
804c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian        listener.onInternationalCallOnWifi(mCall);
805c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      }
806c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    }
807c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian
808c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    @Override
8092f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian    public void onEnrichedCallSessionUpdate() {}
8102f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian
8112f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian    @Override
812d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    public void onDialerCallSessionModificationStateChange() {
813ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      for (Listener listener : mListeners) {
814d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        listener.onSessionModificationStateChange(mCall);
815ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
816ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
817ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
818ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian}
819