1 /* 2 * Copyright (C) 2019 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.services.telephony; 18 19 import android.annotation.NonNull; 20 import android.os.Bundle; 21 import android.telecom.Conference; 22 import android.telecom.Connection; 23 import android.telecom.PhoneAccountHandle; 24 import android.telecom.TelecomManager; 25 import android.telephony.ServiceState; 26 27 import java.util.Collections; 28 import java.util.Iterator; 29 import java.util.Set; 30 import java.util.concurrent.ConcurrentHashMap; 31 32 /** 33 * Base class for the various Telephony {@link Conference} implementations ({@link CdmaConference}, 34 * {@link TelephonyConference}, and {@link ImsConference}). Adds some common listener code which 35 * all of these conferences use. 36 */ 37 public class TelephonyConferenceBase extends Conference { 38 private static final String TAG = "TelephonyConferenceBase"; 39 40 /** 41 * Listener for conference events. 42 */ 43 public abstract static class TelephonyConferenceListener { 44 /** 45 * Listener called when a connection is added or removed from a conference. 46 * @param connection The connection. 47 */ onConferenceMembershipChanged(Connection connection)48 public void onConferenceMembershipChanged(Connection connection) {} 49 50 /** 51 * Listener called when there conference call state changes. 52 * @param conference The conference. 53 * @param oldState previous state of conference call. 54 * @param newState new state of conference call. 55 */ onStateChanged(Conference conference, int oldState, int newState)56 public void onStateChanged(Conference conference, int oldState, int newState) {} 57 58 /** 59 * Listener called when a conference is destroyed. 60 * @param conference The conference. 61 */ onDestroyed(Conference conference)62 public void onDestroyed(Conference conference) {} 63 64 /** 65 * Listener called when a conference either reaches capacity or is no longer at capacity. 66 */ onConferenceCapacityChanged()67 public void onConferenceCapacityChanged() {} 68 } 69 70 private final Set<TelephonyConferenceListener> mListeners = Collections.newSetFromMap( 71 new ConcurrentHashMap<>(8, 0.9f, 1)); 72 73 /** 74 * Adds a listener to this conference. 75 * @param listener The listener. 76 */ addTelephonyConferenceListener(@onNull TelephonyConferenceListener listener)77 public void addTelephonyConferenceListener(@NonNull TelephonyConferenceListener listener) { 78 mListeners.add(listener); 79 } 80 81 /** 82 * Removes a listener from this conference. 83 * @param listener The listener. 84 */ removeTelephonyConferenceListener(@onNull TelephonyConferenceListener listener)85 public void removeTelephonyConferenceListener(@NonNull TelephonyConferenceListener listener) { 86 mListeners.remove(listener); 87 } 88 89 /** 90 * Constructs a new Conference with a mandatory {@link PhoneAccountHandle} 91 * 92 * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference. 93 */ TelephonyConferenceBase(PhoneAccountHandle phoneAccount)94 public TelephonyConferenceBase(PhoneAccountHandle phoneAccount) { 95 super(phoneAccount); 96 } 97 98 /** 99 * Adds a connection to this {@link Conference}. 100 * <p> 101 * Should be used in place of {@link Conference#addConnection(Connection)} to ensure 102 * {@link TelephonyConferenceListener}s are informed of the change. 103 * 104 * @param connection The connection. 105 */ addTelephonyConnection(@onNull Connection connection)106 public void addTelephonyConnection(@NonNull Connection connection) { 107 addConnection(connection); 108 notifyConferenceMembershipChanged(connection); 109 } 110 111 /** 112 * Removes a {@link Connection} from this {@link Conference}. 113 * <p> 114 * Should be used instead of {@link Conference#removeConnection(Connection)} to ensure 115 * {@link TelephonyConferenceListener}s are notified of the change. 116 * 117 * @param connection The connection. 118 */ removeTelephonyConnection(@onNull Connection connection)119 public void removeTelephonyConnection(@NonNull Connection connection) { 120 removeConnection(connection); 121 notifyConferenceMembershipChanged(connection); 122 } 123 124 /** 125 * Destroys the current {@link Conference} and notifies {@link TelephonyConferenceListener}s of 126 * the change to conference membership. 127 * <p> 128 * Should be used instead of {@link Conference#destroy()} to ensure telephony listeners are 129 * notified. 130 */ destroyTelephonyConference()131 public void destroyTelephonyConference() { 132 // Conference#removeConnection modifies the list of participants, so we need to use an 133 // iterator here to ensure all participants are removed. 134 // Technically Conference#destroy does this, but we want to notify listeners of the state 135 // change so we'll do it here first. 136 Iterator<Connection> connectionIterator = getConnections().iterator(); 137 while (connectionIterator.hasNext()) { 138 removeTelephonyConnection(connectionIterator.next()); 139 } 140 destroy(); 141 notifyDestroyed(); 142 } 143 144 /** 145 * Sets state to be on hold. 146 */ setConferenceOnHold()147 public final void setConferenceOnHold() { 148 int oldState = getState(); 149 if (oldState == Connection.STATE_HOLDING) { 150 return; 151 } 152 setOnHold(); 153 notifyStateChanged(oldState, getState()); 154 } 155 156 /** 157 * Sets state to be dialing. 158 */ setConferenceOnDialing()159 public final void setConferenceOnDialing() { 160 int oldState = getState(); 161 if (oldState == Connection.STATE_DIALING) { 162 return; 163 } 164 setDialing(); 165 notifyStateChanged(oldState, getState()); 166 } 167 168 /** 169 * Sets state to be ringing. 170 */ setConferenceOnRinging()171 public final void setConferenceOnRinging() { 172 int oldState = getState(); 173 if (oldState == Connection.STATE_RINGING) { 174 return; 175 } 176 setRinging(); 177 notifyStateChanged(oldState, getState()); 178 } 179 180 /** 181 * Sets state to be active. 182 */ setConferenceOnActive()183 public final void setConferenceOnActive() { 184 int oldState = getState(); 185 if (oldState == Connection.STATE_ACTIVE) { 186 return; 187 } 188 setActive(); 189 notifyStateChanged(oldState, getState()); 190 } 191 192 /** 193 * Updates RIL voice radio technology used for current conference after its creation. 194 */ updateCallRadioTechAfterCreation()195 public void updateCallRadioTechAfterCreation() { 196 final Connection primaryConnection = getPrimaryConnection(); 197 if (primaryConnection != null && primaryConnection instanceof TelephonyConnection) { 198 TelephonyConnection telephonyConnection = (TelephonyConnection) primaryConnection; 199 Bundle newExtras = new Bundle(); 200 newExtras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE, 201 ServiceState.rilRadioTechnologyToNetworkType( 202 telephonyConnection.getCallRadioTech())); 203 putExtras(newExtras); 204 } else { 205 Log.w(TAG, "No primary connection found while updateCallRadioTechAfterCreation"); 206 } 207 } 208 209 /** 210 * Removes the specified capability from the set of capabilities of this {@code Conference}. 211 * 212 * @param capability The capability to remove from the set. 213 */ removeCapability(int capability)214 public void removeCapability(int capability) { 215 int newCapabilities = getConnectionCapabilities(); 216 newCapabilities &= ~capability; 217 218 setConnectionCapabilities(newCapabilities); 219 } 220 221 /** 222 * Adds the specified capability to the set of capabilities of this {@code Conference}. 223 * 224 * @param capability The capability to add to the set. 225 */ addCapability(int capability)226 public void addCapability(int capability) { 227 int newCapabilities = getConnectionCapabilities(); 228 newCapabilities |= capability; 229 230 setConnectionCapabilities(newCapabilities); 231 } 232 233 /** 234 * Notifies {@link TelephonyConferenceListener}s of a connection being added or removed from 235 * the conference. 236 * @param connection The conference. 237 */ notifyConferenceMembershipChanged(@onNull Connection connection)238 private void notifyConferenceMembershipChanged(@NonNull Connection connection) { 239 for (TelephonyConferenceListener listener : mListeners) { 240 listener.onConferenceMembershipChanged(connection); 241 } 242 } 243 244 /** 245 * Notifies the {@link TelephonyConferenceListener}s when the capacity of the conference has 246 * changed. 247 */ notifyConferenceCapacityChanged()248 public void notifyConferenceCapacityChanged() { 249 mListeners.forEach(l -> l.onConferenceCapacityChanged()); 250 } 251 252 /** 253 * Notifies {@link TelephonyConferenceListener}s of a conference being destroyed 254 */ notifyDestroyed()255 private void notifyDestroyed() { 256 for (TelephonyConferenceListener listener : mListeners) { 257 listener.onDestroyed(this); 258 } 259 } 260 notifyStateChanged(int oldState, int newState)261 private void notifyStateChanged(int oldState, int newState) { 262 if (oldState != newState) { 263 for (TelephonyConferenceListener listener : mListeners) { 264 listener.onStateChanged(this, oldState, newState); 265 } 266 } 267 } 268 } 269