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.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.net.Uri; 25 import android.os.RemoteException; 26 import android.telephony.ims.ImsReasonInfo; 27 import android.telephony.ims.ImsRegistrationAttributes; 28 import android.telephony.ims.RegistrationManager; 29 import android.telephony.ims.aidl.IImsRegistration; 30 import android.telephony.ims.aidl.IImsRegistrationCallback; 31 import android.util.Log; 32 33 import com.android.internal.telephony.util.RemoteCallbackListExt; 34 import com.android.internal.util.ArrayUtils; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 39 /** 40 * Controls IMS registration for this ImsService and notifies the framework when the IMS 41 * registration for this ImsService has changed status. 42 * <p> 43 * Note: There is no guarantee on the thread that the calls from the framework will be called on. It 44 * is the implementors responsibility to handle moving the calls to a working thread if required. 45 * @hide 46 */ 47 @SystemApi 48 public class ImsRegistrationImplBase { 49 50 private static final String LOG_TAG = "ImsRegistrationImplBase"; 51 52 /** 53 * @hide 54 */ 55 // Defines the underlying radio technology type that we have registered for IMS over. 56 @IntDef(value = { 57 REGISTRATION_TECH_NONE, 58 REGISTRATION_TECH_LTE, 59 REGISTRATION_TECH_IWLAN, 60 REGISTRATION_TECH_CROSS_SIM, 61 REGISTRATION_TECH_NR 62 }) 63 @Retention(RetentionPolicy.SOURCE) 64 public @interface ImsRegistrationTech {} 65 /** 66 * No registration technology specified, used when we are not registered. 67 */ 68 public static final int REGISTRATION_TECH_NONE = -1; 69 /** 70 * This ImsService is registered to IMS via LTE. 71 */ 72 public static final int REGISTRATION_TECH_LTE = 0; 73 /** 74 * This ImsService is registered to IMS via IWLAN. 75 */ 76 public static final int REGISTRATION_TECH_IWLAN = 1; 77 78 /** 79 * This ImsService is registered to IMS via internet over second subscription. 80 */ 81 public static final int REGISTRATION_TECH_CROSS_SIM = 2; 82 83 /** 84 * This ImsService is registered to IMS via NR. 85 */ 86 public static final int REGISTRATION_TECH_NR = 3; 87 88 // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current 89 // state. 90 // The unknown state is set as the initialization state. This is so that we do not call back 91 // with NOT_REGISTERED in the case where the ImsService has not updated the registration state 92 // yet. 93 private static final int REGISTRATION_STATE_UNKNOWN = -1; 94 95 private final IImsRegistration mBinder = new IImsRegistration.Stub() { 96 97 @Override 98 public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { 99 synchronized (mLock) { 100 return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE 101 : mRegistrationAttributes.getRegistrationTechnology(); 102 } 103 } 104 105 @Override 106 public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 107 ImsRegistrationImplBase.this.addRegistrationCallback(c); 108 } 109 110 @Override 111 public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 112 ImsRegistrationImplBase.this.removeRegistrationCallback(c); 113 } 114 115 @Override 116 public void triggerFullNetworkRegistration(int sipCode, String sipReason) { 117 ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason); 118 } 119 120 @Override 121 public void triggerUpdateSipDelegateRegistration() { 122 ImsRegistrationImplBase.this.updateSipDelegateRegistration(); 123 } 124 125 @Override 126 public void triggerSipDelegateDeregistration() { 127 ImsRegistrationImplBase.this.triggerSipDelegateDeregistration(); 128 } 129 }; 130 131 private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks = 132 new RemoteCallbackListExt<>(); 133 private final Object mLock = new Object(); 134 // Locked on mLock 135 private ImsRegistrationAttributes mRegistrationAttributes; 136 // Locked on mLock 137 private int mRegistrationState = REGISTRATION_STATE_UNKNOWN; 138 // Locked on mLock, create unspecified disconnect cause. 139 private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo(); 140 141 // We hold onto the uris each time they change so that we can send it to a callback when its 142 // first added. 143 private Uri[] mUris = new Uri[0]; 144 private boolean mUrisSet = false; 145 146 /** 147 * @hide 148 */ getBinder()149 public final IImsRegistration getBinder() { 150 return mBinder; 151 } 152 addRegistrationCallback(IImsRegistrationCallback c)153 private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 154 mCallbacks.register(c); 155 updateNewCallbackWithState(c); 156 } 157 removeRegistrationCallback(IImsRegistrationCallback c)158 private void removeRegistrationCallback(IImsRegistrationCallback c) { 159 mCallbacks.unregister(c); 160 } 161 162 /** 163 * Called by the framework to request that the ImsService perform the network registration 164 * of all SIP delegates associated with this ImsService. 165 * <p> 166 * If the SIP delegate feature tag configuration has changed, then this method will be 167 * called in order to let the ImsService know that it can pick up these changes in the IMS 168 * registration. 169 */ updateSipDelegateRegistration()170 public void updateSipDelegateRegistration() { 171 // Stub implementation, ImsService should implement this 172 } 173 174 175 /** 176 * Called by the framework to request that the ImsService perform the network deregistration of 177 * all SIP delegates associated with this ImsService. 178 * <p> 179 * This is typically called in situations where the user has changed the configuration of the 180 * device (for example, the default messaging application) and the framework is reconfiguring 181 * the tags associated with each IMS application. 182 * <p> 183 * This should not affect the registration of features managed by the ImsService itself, such as 184 * feature tags related to MMTEL registration. 185 */ triggerSipDelegateDeregistration()186 public void triggerSipDelegateDeregistration() { 187 // Stub implementation, ImsService should implement this 188 } 189 190 /** 191 * Called by the framework to notify the ImsService that a SIP delegate connection has received 192 * a SIP message containing a permanent failure response (such as a 403) or an indication that a 193 * SIP response timer has timed out in response to an outgoing SIP message. This method will be 194 * called when this condition occurs to trigger the ImsService to tear down the full IMS 195 * registration and re-register again. 196 * 197 * @param sipCode The SIP error code that represents a permanent failure that was received in 198 * response to a request generated by the IMS application. See RFC3261 7.2 for the general 199 * classes of responses available here, however the codes that generate this condition may 200 * be carrier specific. 201 * @param sipReason The reason associated with the SIP error code. {@code null} if there was no 202 * reason associated with the error. 203 */ triggerFullNetworkRegistration(@ntRangefrom = 100, to = 699) int sipCode, @Nullable String sipReason)204 public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode, 205 @Nullable String sipReason) { 206 // Stub implementation, ImsService should implement this 207 } 208 209 210 /** 211 * Notify the framework that the device is connected to the IMS network. 212 * 213 * @param imsRadioTech the radio access technology. 214 */ onRegistered(@msRegistrationTech int imsRadioTech)215 public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { 216 onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); 217 } 218 219 /** 220 * Notify the framework that the device is connected to the IMS network. 221 * 222 * @param attributes The attributes associated with the IMS registration. 223 */ onRegistered(@onNull ImsRegistrationAttributes attributes)224 public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) { 225 updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); 226 mCallbacks.broadcastAction((c) -> { 227 try { 228 c.onRegistered(attributes); 229 } catch (RemoteException e) { 230 Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback."); 231 } 232 }); 233 } 234 235 /** 236 * Notify the framework that the device is trying to connect the IMS network. 237 * 238 * @param imsRadioTech the radio access technology. 239 */ onRegistering(@msRegistrationTech int imsRadioTech)240 public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { 241 onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); 242 } 243 244 /** 245 * Notify the framework that the device is trying to connect the IMS network. 246 * 247 * @param attributes The attributes associated with the IMS registration. 248 */ onRegistering(@onNull ImsRegistrationAttributes attributes)249 public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) { 250 updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); 251 mCallbacks.broadcastAction((c) -> { 252 try { 253 c.onRegistering(attributes); 254 } catch (RemoteException e) { 255 Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback."); 256 } 257 }); 258 } 259 260 /** 261 * Notify the framework that the device is disconnected from the IMS network. 262 * <p> 263 * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any 264 * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent 265 * to the framework. For example, 266 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} 267 * and 268 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} 269 * may be set to unavailable to ensure the framework knows these services are no longer 270 * available due to de-registration. If you do not report capability changes impacted by 271 * de-registration, the framework will not know which features are no longer available as a 272 * result. 273 * 274 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 275 */ onDeregistered(ImsReasonInfo info)276 public final void onDeregistered(ImsReasonInfo info) { 277 updateToDisconnectedState(info); 278 // ImsReasonInfo should never be null. 279 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 280 mCallbacks.broadcastAction((c) -> { 281 try { 282 c.onDeregistered(reasonInfo); 283 } catch (RemoteException e) { 284 Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback."); 285 } 286 }); 287 } 288 289 /** 290 * Notify the framework that the handover from the current radio technology to the technology 291 * defined in {@code imsRadioTech} has failed. 292 * @param imsRadioTech The technology that has failed to be changed. Valid values are 293 * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and 294 * {@link #REGISTRATION_TECH_CROSS_SIM}. 295 * @param info The {@link ImsReasonInfo} for the failure to change technology. 296 */ onTechnologyChangeFailed(@msRegistrationTech int imsRadioTech, ImsReasonInfo info)297 public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, 298 ImsReasonInfo info) { 299 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 300 mCallbacks.broadcastAction((c) -> { 301 try { 302 c.onTechnologyChangeFailed(imsRadioTech, reasonInfo); 303 } catch (RemoteException e) { 304 Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback."); 305 } 306 }); 307 } 308 309 /** 310 * Invoked when the {@link Uri}s associated to this device's subscriber have changed. 311 * These {@link Uri}s' are filtered out during conference calls. 312 * 313 * The {@link Uri}s are not guaranteed to be different between subsequent calls. 314 * @param uris changed uris 315 */ onSubscriberAssociatedUriChanged(Uri[] uris)316 public final void onSubscriberAssociatedUriChanged(Uri[] uris) { 317 synchronized (mLock) { 318 mUris = ArrayUtils.cloneOrNull(uris); 319 mUrisSet = true; 320 } 321 mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris)); 322 } 323 onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris)324 private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) { 325 try { 326 callback.onSubscriberAssociatedUriChanged(uris); 327 } catch (RemoteException e) { 328 Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback."); 329 } 330 } 331 updateToState(ImsRegistrationAttributes attributes, int newState)332 private void updateToState(ImsRegistrationAttributes attributes, int newState) { 333 synchronized (mLock) { 334 mRegistrationAttributes = attributes; 335 mRegistrationState = newState; 336 mLastDisconnectCause = null; 337 } 338 } 339 updateToDisconnectedState(ImsReasonInfo info)340 private void updateToDisconnectedState(ImsReasonInfo info) { 341 synchronized (mLock) { 342 //We don't want to send this info over if we are disconnected 343 mUrisSet = false; 344 mUris = null; 345 346 updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(), 347 RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); 348 if (info != null) { 349 mLastDisconnectCause = info; 350 } else { 351 Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); 352 mLastDisconnectCause = new ImsReasonInfo(); 353 } 354 } 355 } 356 357 /** 358 * @param c the newly registered callback that will be updated with the current registration 359 * state. 360 */ updateNewCallbackWithState(IImsRegistrationCallback c)361 private void updateNewCallbackWithState(IImsRegistrationCallback c) 362 throws RemoteException { 363 int state; 364 ImsRegistrationAttributes attributes; 365 ImsReasonInfo disconnectInfo; 366 boolean urisSet; 367 Uri[] uris; 368 synchronized (mLock) { 369 state = mRegistrationState; 370 attributes = mRegistrationAttributes; 371 disconnectInfo = mLastDisconnectCause; 372 urisSet = mUrisSet; 373 uris = mUris; 374 } 375 switch (state) { 376 case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: { 377 c.onDeregistered(disconnectInfo); 378 break; 379 } 380 case RegistrationManager.REGISTRATION_STATE_REGISTERING: { 381 c.onRegistering(attributes); 382 break; 383 } 384 case RegistrationManager.REGISTRATION_STATE_REGISTERED: { 385 c.onRegistered(attributes); 386 break; 387 } 388 case REGISTRATION_STATE_UNKNOWN: { 389 // Do not callback if the state has not been updated yet by the ImsService. 390 break; 391 } 392 } 393 if (urisSet) { 394 onSubscriberAssociatedUriChanged(c, uris); 395 } 396 } 397 } 398