• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 
17 package com.android.internal.telephony.imsphone;
18 
19 import static com.android.internal.telephony.Call.State.DISCONNECTED;
20 import static com.android.internal.telephony.Call.State.IDLE;
21 
22 import android.annotation.NonNull;
23 
24 import com.android.internal.annotations.VisibleForTesting;
25 import com.android.internal.telephony.Call;
26 import com.android.internal.telephony.Connection;
27 import com.android.internal.telephony.Phone;
28 import com.android.telephony.Rlog;
29 
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Comparator;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 
39 /**
40  * Contains the state of all IMS calls.
41  */
42 public class ImsCallInfoTracker {
43     private static final String LOG_TAG = "ImsCallInfoTracker";
44     private static final boolean DBG = false;
45 
46     private final Phone mPhone;
47     private final List<ImsCallInfo> mQueue = new ArrayList<>();
48     private int mNextIndex = 1;
49 
50     private final Map<Connection, ImsCallInfo> mImsCallInfo = new HashMap<>();
51 
ImsCallInfoTracker(Phone phone)52     public ImsCallInfoTracker(Phone phone) {
53         mPhone = phone;
54     }
55 
56     /**
57      * Adds a new instance of the IMS call.
58      *
59      * @param c The instance of {@link ImsPhoneConnection}.
60      */
addImsCallStatus(@onNull ImsPhoneConnection c)61     public void addImsCallStatus(@NonNull ImsPhoneConnection c) {
62         if (DBG) Rlog.d(LOG_TAG, "addImsCallStatus");
63 
64         synchronized (mImsCallInfo) {
65             if (mQueue.isEmpty()) {
66                 mQueue.add(new ImsCallInfo(mNextIndex++));
67             }
68 
69             Iterator<ImsCallInfo> it = mQueue.iterator();
70             ImsCallInfo imsCallInfo = it.next();
71             mQueue.remove(imsCallInfo);
72 
73             imsCallInfo.init(c);
74             mImsCallInfo.put(c, imsCallInfo);
75 
76             if (!imsCallInfo.shouldIgnoreUpdate()) {
77                 notifyImsCallStatus();
78             }
79 
80             if (DBG) dump();
81         }
82     }
83 
84     /**
85      * Updates the list of IMS calls.
86      *
87      * @param c The instance of {@link ImsPhoneConnection}.
88      */
updateImsCallStatus(@onNull ImsPhoneConnection c)89     public void updateImsCallStatus(@NonNull ImsPhoneConnection c) {
90         updateImsCallStatus(c, false, false);
91     }
92 
93     /**
94      * Updates the list of IMS calls.
95      *
96      * @param c The instance of {@link ImsPhoneConnection}.
97      * @param holdReceived {@code true} if the remote party held the call.
98      * @param resumeReceived {@code true} if the remote party resumed the call.
99      */
updateImsCallStatus(@onNull ImsPhoneConnection c, boolean holdReceived, boolean resumeReceived)100     public void updateImsCallStatus(@NonNull ImsPhoneConnection c,
101             boolean holdReceived, boolean resumeReceived) {
102         if (DBG) {
103             Rlog.d(LOG_TAG, "updateImsCallStatus holdReceived=" + holdReceived
104                     + ", resumeReceived=" + resumeReceived);
105         }
106 
107         synchronized (mImsCallInfo) {
108             ImsCallInfo info = mImsCallInfo.get(c);
109 
110             if (info == null) {
111                 // This happens when the user tries to hangup the call after handover has completed.
112                 return;
113             }
114 
115             boolean changed = info.update(c, holdReceived, resumeReceived);
116 
117             if (changed) notifyImsCallStatus();
118 
119             Call.State state = c.getState();
120 
121             if (DBG) Rlog.d(LOG_TAG, "updateImsCallStatus state=" + state);
122             // Call is disconnected. There are 2 cases in disconnected state:
123             // if silent redial, state == IDLE, otherwise, state == DISCONNECTED.
124             if (state == DISCONNECTED || state == IDLE) {
125                 // clear the disconnected call
126                 mImsCallInfo.remove(c);
127                 info.reset();
128                 if (info.getIndex() < (mNextIndex - 1)) {
129                     mQueue.add(info);
130                     sort(mQueue);
131                 } else {
132                     mNextIndex--;
133                 }
134             }
135 
136             if (DBG) dump();
137         }
138     }
139 
140     /** Clears all orphaned IMS call information. */
clearAllOrphanedConnections()141     public void clearAllOrphanedConnections() {
142         if (DBG) Rlog.d(LOG_TAG, "clearAllOrphanedConnections");
143 
144         Collection<ImsCallInfo> infos = mImsCallInfo.values();
145         infos.stream().forEach(info -> { info.onDisconnect(); });
146         notifyImsCallStatus();
147         clearAllCallInfo();
148 
149         if (DBG) dump();
150     }
151 
152     /** Notifies that SRVCC has completed. */
notifySrvccCompleted()153     public void notifySrvccCompleted() {
154         if (DBG) Rlog.d(LOG_TAG, "notifySrvccCompleted");
155 
156         clearAllCallInfo();
157         notifyImsCallStatus();
158 
159         if (DBG) dump();
160     }
161 
clearAllCallInfo()162     private void clearAllCallInfo() {
163         try {
164             Collection<ImsCallInfo> infos = mImsCallInfo.values();
165             infos.stream().forEach(info -> { info.reset(); });
166             mImsCallInfo.clear();
167             mQueue.clear();
168             mNextIndex = 1;
169         } catch (UnsupportedOperationException e) {
170             Rlog.e(LOG_TAG, "e=" + e);
171         }
172     }
173 
notifyImsCallStatus()174     private void notifyImsCallStatus() {
175         Collection<ImsCallInfo> infos = mImsCallInfo.values()
176                 .stream().filter(info -> !info.shouldIgnoreUpdate()).toList();
177         ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos);
178         sort(imsCallInfo);
179         mPhone.updateImsCallStatus(imsCallInfo, null);
180     }
181 
182     /**
183      * Sorts the list of IMS calls by the call index.
184      *
185      * @param infos The list of IMS calls.
186      */
187     @VisibleForTesting
sort(List<ImsCallInfo> infos)188     public static void sort(List<ImsCallInfo> infos) {
189         Collections.sort(infos, new Comparator<ImsCallInfo>() {
190             @Override
191             public int compare(ImsCallInfo l, ImsCallInfo r) {
192                 if (l.getIndex() > r.getIndex()) {
193                     return 1;
194                 } else if (l.getIndex() < r.getIndex()) {
195                     return -1;
196                 }
197                 return 0;
198             }
199         });
200     }
201 
dump()202     private void dump() {
203         Collection<ImsCallInfo> infos = mImsCallInfo.values();
204         ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos);
205         sort(imsCallInfo);
206         Rlog.d(LOG_TAG, "imsCallInfos=" + imsCallInfo);
207     }
208 }
209