1 /* 2 * Copyright (C) 2017 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 android.telephony.ims.stub; 18 19 import android.annotation.IntDef; 20 import android.annotation.SystemApi; 21 import android.annotation.TestApi; 22 import android.net.Uri; 23 import android.os.RemoteException; 24 import android.telephony.ims.ImsReasonInfo; 25 import android.telephony.ims.RegistrationManager; 26 import android.telephony.ims.aidl.IImsRegistration; 27 import android.telephony.ims.aidl.IImsRegistrationCallback; 28 import android.util.Log; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.telephony.util.RemoteCallbackListExt; 32 import com.android.internal.util.ArrayUtils; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 37 /** 38 * Controls IMS registration for this ImsService and notifies the framework when the IMS 39 * registration for this ImsService has changed status. 40 * @hide 41 */ 42 @SystemApi 43 @TestApi 44 public class ImsRegistrationImplBase { 45 46 private static final String LOG_TAG = "ImsRegistrationImplBase"; 47 48 /** 49 * @hide 50 */ 51 // Defines the underlying radio technology type that we have registered for IMS over. 52 @IntDef(flag = true, 53 value = { 54 REGISTRATION_TECH_NONE, 55 REGISTRATION_TECH_LTE, 56 REGISTRATION_TECH_IWLAN 57 }) 58 @Retention(RetentionPolicy.SOURCE) 59 public @interface ImsRegistrationTech {} 60 /** 61 * No registration technology specified, used when we are not registered. 62 */ 63 public static final int REGISTRATION_TECH_NONE = -1; 64 /** 65 * IMS is registered to IMS via LTE. 66 */ 67 public static final int REGISTRATION_TECH_LTE = 0; 68 /** 69 * IMS is registered to IMS via IWLAN. 70 */ 71 public static final int REGISTRATION_TECH_IWLAN = 1; 72 73 // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current 74 // state. 75 // The unknown state is set as the initialization state. This is so that we do not call back 76 // with NOT_REGISTERED in the case where the ImsService has not updated the registration state 77 // yet. 78 private static final int REGISTRATION_STATE_UNKNOWN = -1; 79 80 private final IImsRegistration mBinder = new IImsRegistration.Stub() { 81 82 @Override 83 public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { 84 return getConnectionType(); 85 } 86 87 @Override 88 public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 89 ImsRegistrationImplBase.this.addRegistrationCallback(c); 90 } 91 92 @Override 93 public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 94 ImsRegistrationImplBase.this.removeRegistrationCallback(c); 95 } 96 }; 97 98 private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks = 99 new RemoteCallbackListExt<>(); 100 private final Object mLock = new Object(); 101 // Locked on mLock 102 private @ImsRegistrationTech 103 int mConnectionType = REGISTRATION_TECH_NONE; 104 // Locked on mLock 105 private int mRegistrationState = REGISTRATION_STATE_UNKNOWN; 106 // Locked on mLock, create unspecified disconnect cause. 107 private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo(); 108 109 // We hold onto the uris each time they change so that we can send it to a callback when its 110 // first added. 111 private Uri[] mUris = new Uri[0]; 112 private boolean mUrisSet = false; 113 114 /** 115 * @hide 116 */ getBinder()117 public final IImsRegistration getBinder() { 118 return mBinder; 119 } 120 addRegistrationCallback(IImsRegistrationCallback c)121 private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 122 mCallbacks.register(c); 123 updateNewCallbackWithState(c); 124 } 125 removeRegistrationCallback(IImsRegistrationCallback c)126 private void removeRegistrationCallback(IImsRegistrationCallback c) { 127 mCallbacks.unregister(c); 128 } 129 130 /** 131 * Notify the framework that the device is connected to the IMS network. 132 * 133 * @param imsRadioTech the radio access technology. Valid values are defined as 134 * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. 135 */ onRegistered(@msRegistrationTech int imsRadioTech)136 public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { 137 updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED); 138 mCallbacks.broadcastAction((c) -> { 139 try { 140 c.onRegistered(imsRadioTech); 141 } catch (RemoteException e) { 142 Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " + 143 "callback."); 144 } 145 }); 146 } 147 148 /** 149 * Notify the framework that the device is trying to connect the IMS network. 150 * 151 * @param imsRadioTech the radio access technology. Valid values are defined as 152 * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. 153 */ onRegistering(@msRegistrationTech int imsRadioTech)154 public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { 155 updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING); 156 mCallbacks.broadcastAction((c) -> { 157 try { 158 c.onRegistering(imsRadioTech); 159 } catch (RemoteException e) { 160 Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " + 161 "callback."); 162 } 163 }); 164 } 165 166 /** 167 * Notify the framework that the device is disconnected from the IMS network. 168 * <p> 169 * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any 170 * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent 171 * to the framework. For example, 172 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} 173 * and 174 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} 175 * may be set to unavailable to ensure the framework knows these services are no longer 176 * available due to de-registration. If you do not report capability changes impacted by 177 * de-registration, the framework will not know which features are no longer available as a 178 * result. 179 * 180 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 181 */ onDeregistered(ImsReasonInfo info)182 public final void onDeregistered(ImsReasonInfo info) { 183 updateToDisconnectedState(info); 184 // ImsReasonInfo should never be null. 185 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 186 mCallbacks.broadcastAction((c) -> { 187 try { 188 c.onDeregistered(reasonInfo); 189 } catch (RemoteException e) { 190 Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " + 191 "callback."); 192 } 193 }); 194 } 195 196 /** 197 * Notify the framework that the handover from the current radio technology to the technology 198 * defined in {@code imsRadioTech} has failed. 199 * @param imsRadioTech The technology that has failed to be changed. Valid values are 200 * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. 201 * @param info The {@link ImsReasonInfo} for the failure to change technology. 202 */ onTechnologyChangeFailed(@msRegistrationTech int imsRadioTech, ImsReasonInfo info)203 public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, 204 ImsReasonInfo info) { 205 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 206 mCallbacks.broadcastAction((c) -> { 207 try { 208 c.onTechnologyChangeFailed(imsRadioTech, reasonInfo); 209 } catch (RemoteException e) { 210 Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " + 211 "callback."); 212 } 213 }); 214 } 215 216 /** 217 * Invoked when the {@link Uri}s associated to this device's subscriber have changed. 218 * These {@link Uri}s' are filtered out during conference calls. 219 * 220 * The {@link Uri}s are not guaranteed to be different between subsequent calls. 221 * @param uris changed uris 222 */ onSubscriberAssociatedUriChanged(Uri[] uris)223 public final void onSubscriberAssociatedUriChanged(Uri[] uris) { 224 synchronized (mLock) { 225 mUris = ArrayUtils.cloneOrNull(uris); 226 mUrisSet = true; 227 } 228 mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris)); 229 } 230 onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris)231 private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) { 232 try { 233 callback.onSubscriberAssociatedUriChanged(uris); 234 } catch (RemoteException e) { 235 Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " 236 + "callback."); 237 } 238 } 239 updateToState(@msRegistrationTech int connType, int newState)240 private void updateToState(@ImsRegistrationTech int connType, int newState) { 241 synchronized (mLock) { 242 mConnectionType = connType; 243 mRegistrationState = newState; 244 mLastDisconnectCause = null; 245 } 246 } 247 updateToDisconnectedState(ImsReasonInfo info)248 private void updateToDisconnectedState(ImsReasonInfo info) { 249 synchronized (mLock) { 250 //We don't want to send this info over if we are disconnected 251 mUrisSet = false; 252 mUris = null; 253 254 updateToState(REGISTRATION_TECH_NONE, 255 RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); 256 if (info != null) { 257 mLastDisconnectCause = info; 258 } else { 259 Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); 260 mLastDisconnectCause = new ImsReasonInfo(); 261 } 262 } 263 } 264 265 /** 266 * @return the current registration connection type. Valid values are 267 * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN} 268 * @hide 269 */ 270 @VisibleForTesting getConnectionType()271 public final @ImsRegistrationTech int getConnectionType() { 272 synchronized (mLock) { 273 return mConnectionType; 274 } 275 } 276 277 /** 278 * @param c the newly registered callback that will be updated with the current registration 279 * state. 280 */ updateNewCallbackWithState(IImsRegistrationCallback c)281 private void updateNewCallbackWithState(IImsRegistrationCallback c) 282 throws RemoteException { 283 int state; 284 ImsReasonInfo disconnectInfo; 285 boolean urisSet; 286 Uri[] uris; 287 synchronized (mLock) { 288 state = mRegistrationState; 289 disconnectInfo = mLastDisconnectCause; 290 urisSet = mUrisSet; 291 uris = mUris; 292 } 293 switch (state) { 294 case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: { 295 c.onDeregistered(disconnectInfo); 296 break; 297 } 298 case RegistrationManager.REGISTRATION_STATE_REGISTERING: { 299 c.onRegistering(getConnectionType()); 300 break; 301 } 302 case RegistrationManager.REGISTRATION_STATE_REGISTERED: { 303 c.onRegistered(getConnectionType()); 304 break; 305 } 306 case REGISTRATION_STATE_UNKNOWN: { 307 // Do not callback if the state has not been updated yet by the ImsService. 308 break; 309 } 310 } 311 if (urisSet) { 312 onSubscriberAssociatedUriChanged(c, uris); 313 } 314 } 315 } 316