ImsExternalCallTracker.java revision fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2
1/* 2 * Copyright (C) 2016 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.internal.telephony.imsphone; 18 19import com.android.ims.ImsExternalCallState; 20import com.android.internal.telephony.Call; 21import com.android.internal.telephony.Connection; 22import com.android.internal.telephony.Phone; 23 24import android.os.Bundle; 25import android.telecom.PhoneAccountHandle; 26import android.util.ArrayMap; 27import android.util.Log; 28 29import java.util.ArrayList; 30import java.util.Iterator; 31import java.util.List; 32import java.util.Map; 33 34/** 35 * Responsible for tracking external calls known to the system. 36 */ 37public class ImsExternalCallTracker { 38 39 public final static String TAG = "ImsExternalCallTracker"; 40 41 /** 42 * Extra key used when informing telecom of a new external call using the 43 * {@link android.telecom.TelecomManager#addNewUnknownCall(PhoneAccountHandle, Bundle)} API. 44 * Used to ensure that when Telecom requests the {@link android.telecom.ConnectionService} to 45 * create the connection for the unknown call that we can determine which 46 * {@link ImsExternalConnection} in {@link #mExternalConnections} is the one being requested. 47 */ 48 public final static String EXTRA_IMS_EXTERNAL_CALL_ID = 49 "android.telephony.ImsExternalCallTracker.extra.EXTERNAL_CALL_ID"; 50 51 /** 52 * Contains a list of the external connections known by the ImsPhoneCallTracker. These are 53 * connections which originated from a dialog event package and reside on another device. 54 * Used in multi-endpoint (VoLTE for internet connected endpoints) scenarios. 55 */ 56 private Map<Integer, ImsExternalConnection> mExternalConnections = 57 new ArrayMap<Integer, ImsExternalConnection>(); 58 59 private ImsPhone mPhone; 60 61 public ImsExternalCallTracker(ImsPhone phone) { 62 mPhone = phone; 63 } 64 65 /** 66 * Called when the IMS stack receives a new dialog event package. Triggers the creation and 67 * update of {@link ImsExternalConnection}s to represent the dialogs in the dialog event 68 * package data. 69 * 70 * @param externalCallStates the {@link ImsExternalCallState} information for the dialog event 71 * package. 72 */ 73 public void refreshExternalCallState(List<ImsExternalCallState> externalCallStates) { 74 Log.d(TAG, "refreshExternalCallState: depSize = " + externalCallStates.size()); 75 76 // Check to see if any call Ids are no longer present in the external call state. If they 77 // are, the calls are terminated and should be removed. 78 Iterator<Map.Entry<Integer, ImsExternalConnection>> connectionIterator = 79 mExternalConnections.entrySet().iterator(); 80 boolean wasCallRemoved = false; 81 while (connectionIterator.hasNext()) { 82 Map.Entry<Integer, ImsExternalConnection> entry = connectionIterator.next(); 83 int callId = entry.getKey().intValue(); 84 85 if (!containsCallId(externalCallStates, callId)) { 86 ImsExternalConnection externalConnection = entry.getValue(); 87 externalConnection.setTerminated(); 88 connectionIterator.remove(); 89 wasCallRemoved = true; 90 } 91 } 92 // If one or more calls were removed, trigger a notification that will cause the 93 // TelephonyConnection instancse to refresh their state with Telecom. 94 if (wasCallRemoved) { 95 mPhone.notifyPreciseCallStateChanged(); 96 } 97 98 // Check for new calls, and updates to existing ones. 99 for (ImsExternalCallState callState : externalCallStates) { 100 if (!mExternalConnections.containsKey(callState.getCallId())) { 101 Log.d(TAG, "refreshExternalCallState: got = " + callState); 102 // If there is a new entry and it is already terminated, don't bother adding it to 103 // telecom. 104 if (callState.getCallState() != ImsExternalCallState.CALL_STATE_CONFIRMED) { 105 continue; 106 } 107 createExternalConnection(callState); 108 } else{ 109 updateExistingConnection(mExternalConnections.get(callState.getCallId()), 110 callState); 111 } 112 } 113 } 114 115 /** 116 * Finds an external connection given a call Id. 117 * 118 * @param callId The call Id. 119 * @return The {@link Connection}, or {@code null} if no match found. 120 */ 121 public Connection getConnectionById(int callId) { 122 return mExternalConnections.get(callId); 123 } 124 125 /** 126 * Given an {@link ImsExternalCallState} instance obtained from a dialog event package, 127 * creates a new instance of {@link ImsExternalConnection} to represent the connection, and 128 * initiates the addition of the new call to Telecom as an unknown call. 129 * 130 * @param state External call state from a dialog event package. 131 */ 132 private void createExternalConnection(ImsExternalCallState state) { 133 Log.i(TAG, "createExternalConnection"); 134 135 ImsExternalConnection connection = new ImsExternalConnection(mPhone, 136 state.getCallId(), /* Dialog event package call id */ 137 state.getAddress().getSchemeSpecificPart() /* phone number */, 138 state.isCallPullable()); 139 140 // Add to list of tracked connections. 141 mExternalConnections.put(connection.getCallId(), connection); 142 143 // Note: The notification of unknown connection is ultimately handled by 144 // PstnIncomingCallNotifier#addNewUnknownCall. That method will ensure that an extra is set 145 // containing the ImsExternalConnection#mCallId so that we have a means of reconciling which 146 // unknown call was added. 147 mPhone.notifyUnknownConnection(connection); 148 } 149 150 /** 151 * Given an existing {@link ImsExternalConnection}, applies any changes found found in a 152 * {@link ImsExternalCallState} instance received from a dialog event package to the connection. 153 * 154 * @param connection The connection to apply changes to. 155 * @param state The new dialog state for the connection. 156 */ 157 private void updateExistingConnection(ImsExternalConnection connection, 158 ImsExternalCallState state) { 159 Call.State existingState = connection.getState(); 160 Call.State newState = state.getCallState() == ImsExternalCallState.CALL_STATE_CONFIRMED ? 161 Call.State.ACTIVE : Call.State.DISCONNECTED; 162 163 if (existingState != newState) { 164 if (newState == Call.State.ACTIVE) { 165 connection.setActive(); 166 } else { 167 connection.setTerminated(); 168 mExternalConnections.remove(connection); 169 mPhone.notifyPreciseCallStateChanged(); 170 } 171 } 172 173 connection.setIsPullable(state.isCallPullable()); 174 } 175 176 /** 177 * Determines if a list of call states obtained from a dialog event package contacts an existing 178 * call Id. 179 * 180 * @param externalCallStates The dialog event package state information. 181 * @param callId The call Id. 182 * @return {@code true} if the state information contains the call Id, {@code false} otherwise. 183 */ 184 private boolean containsCallId(List<ImsExternalCallState> externalCallStates, int callId) { 185 for (ImsExternalCallState state : externalCallStates) { 186 if (state.getCallId() == callId) { 187 return true; 188 } 189 } 190 191 return false; 192 } 193} 194