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.update(c); 74 mImsCallInfo.put(c, imsCallInfo); 75 76 notifyImsCallStatus(); 77 78 if (DBG) dump(); 79 } 80 } 81 82 /** 83 * Updates the list of IMS calls. 84 * 85 * @param c The instance of {@link ImsPhoneConnection}. 86 */ updateImsCallStatus(@onNull ImsPhoneConnection c)87 public void updateImsCallStatus(@NonNull ImsPhoneConnection c) { 88 updateImsCallStatus(c, false, false); 89 } 90 91 /** 92 * Updates the list of IMS calls. 93 * 94 * @param c The instance of {@link ImsPhoneConnection}. 95 * @param holdReceived {@code true} if the remote party held the call. 96 * @param resumeReceived {@code true} if the remote party resumed the call. 97 */ updateImsCallStatus(@onNull ImsPhoneConnection c, boolean holdReceived, boolean resumeReceived)98 public void updateImsCallStatus(@NonNull ImsPhoneConnection c, 99 boolean holdReceived, boolean resumeReceived) { 100 if (DBG) { 101 Rlog.d(LOG_TAG, "updateImsCallStatus holdReceived=" + holdReceived 102 + ", resumeReceived=" + resumeReceived); 103 } 104 105 synchronized (mImsCallInfo) { 106 ImsCallInfo info = mImsCallInfo.get(c); 107 108 if (info == null) { 109 // This happens when the user tries to hangup the call after handover has completed. 110 return; 111 } 112 113 boolean changed = info.update(c, holdReceived, resumeReceived); 114 115 if (changed) notifyImsCallStatus(); 116 117 Call.State state = c.getState(); 118 119 if (DBG) Rlog.d(LOG_TAG, "updateImsCallStatus state=" + state); 120 // Call is disconnected. There are 2 cases in disconnected state: 121 // if silent redial, state == IDLE, otherwise, state == DISCONNECTED. 122 if (state == DISCONNECTED || state == IDLE) { 123 // clear the disconnected call 124 mImsCallInfo.remove(c); 125 info.reset(); 126 if (info.getIndex() < (mNextIndex - 1)) { 127 mQueue.add(info); 128 sort(mQueue); 129 } else { 130 mNextIndex--; 131 } 132 } 133 134 if (DBG) dump(); 135 } 136 } 137 138 /** Clears all orphaned IMS call information. */ clearAllOrphanedConnections()139 public void clearAllOrphanedConnections() { 140 if (DBG) Rlog.d(LOG_TAG, "clearAllOrphanedConnections"); 141 142 Collection<ImsCallInfo> infos = mImsCallInfo.values(); 143 infos.stream().forEach(info -> { info.onDisconnect(); }); 144 notifyImsCallStatus(); 145 clearAllCallInfo(); 146 147 if (DBG) dump(); 148 } 149 150 /** Notifies that SRVCC has completed. */ notifySrvccCompleted()151 public void notifySrvccCompleted() { 152 if (DBG) Rlog.d(LOG_TAG, "notifySrvccCompleted"); 153 154 clearAllCallInfo(); 155 notifyImsCallStatus(); 156 157 if (DBG) dump(); 158 } 159 clearAllCallInfo()160 private void clearAllCallInfo() { 161 try { 162 Collection<ImsCallInfo> infos = mImsCallInfo.values(); 163 infos.stream().forEach(info -> { info.reset(); }); 164 mImsCallInfo.clear(); 165 mQueue.clear(); 166 mNextIndex = 1; 167 } catch (UnsupportedOperationException e) { 168 Rlog.e(LOG_TAG, "e=" + e); 169 } 170 } 171 notifyImsCallStatus()172 private void notifyImsCallStatus() { 173 Collection<ImsCallInfo> infos = mImsCallInfo.values(); 174 ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos); 175 sort(imsCallInfo); 176 mPhone.updateImsCallStatus(imsCallInfo, null); 177 } 178 179 /** 180 * Sorts the list of IMS calls by the call index. 181 * 182 * @param infos The list of IMS calls. 183 */ 184 @VisibleForTesting sort(List<ImsCallInfo> infos)185 public static void sort(List<ImsCallInfo> infos) { 186 Collections.sort(infos, new Comparator<ImsCallInfo>() { 187 @Override 188 public int compare(ImsCallInfo l, ImsCallInfo r) { 189 if (l.getIndex() > r.getIndex()) { 190 return 1; 191 } else if (l.getIndex() < r.getIndex()) { 192 return -1; 193 } 194 return 0; 195 } 196 }); 197 } 198 dump()199 private void dump() { 200 Collection<ImsCallInfo> infos = mImsCallInfo.values(); 201 ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos); 202 sort(imsCallInfo); 203 Rlog.d(LOG_TAG, "imsCallInfos=" + imsCallInfo); 204 } 205 } 206