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