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; 18 19 import android.app.PendingIntent; 20 import android.os.IBinder; 21 import android.os.Message; 22 import android.os.RemoteException; 23 import android.telephony.ims.feature.IRcsFeature; 24 import android.telephony.ims.feature.ImsFeature; 25 import android.util.Log; 26 27 import com.android.ims.ImsCallProfile; 28 import com.android.ims.internal.IImsCallSession; 29 import com.android.ims.internal.IImsCallSessionListener; 30 import com.android.ims.internal.IImsConfig; 31 import com.android.ims.internal.IImsEcbm; 32 import com.android.ims.internal.IImsMultiEndpoint; 33 import com.android.ims.internal.IImsRegistrationListener; 34 import com.android.ims.internal.IImsServiceController; 35 import com.android.ims.internal.IImsServiceFeatureListener; 36 import com.android.ims.internal.IImsUt; 37 38 /** 39 * A container of the IImsServiceController binder, which implements all of the ImsFeatures that 40 * the platform currently supports: MMTel and RCS. 41 * @hide 42 */ 43 44 public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature { 45 46 protected String LOG_TAG = "ImsServiceProxy"; 47 private final int mSupportedFeature; 48 49 // Start by assuming the proxy is available for usage. 50 private boolean mIsAvailable = true; 51 // ImsFeature Status from the ImsService. Cached. 52 private Integer mFeatureStatusCached = null; 53 private ImsServiceProxy.INotifyStatusChanged mStatusCallback; 54 private final Object mLock = new Object(); 55 56 public interface INotifyStatusChanged { notifyStatusChanged()57 void notifyStatusChanged(); 58 } 59 60 private final IImsServiceFeatureListener mListenerBinder = 61 new IImsServiceFeatureListener.Stub() { 62 63 @Override 64 public void imsFeatureCreated(int slotId, int feature) throws RemoteException { 65 // The feature has been re-enabled. This may happen when the service crashes. 66 synchronized (mLock) { 67 if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) { 68 Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " + 69 feature); 70 mIsAvailable = true; 71 } 72 } 73 } 74 75 @Override 76 public void imsFeatureRemoved(int slotId, int feature) throws RemoteException { 77 synchronized (mLock) { 78 if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) { 79 Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " + 80 feature); 81 mIsAvailable = false; 82 } 83 } 84 } 85 86 @Override 87 public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException { 88 synchronized (mLock) { 89 Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature + 90 " status: " + status); 91 if (mSlotId == slotId && feature == mSupportedFeature) { 92 mFeatureStatusCached = status; 93 } 94 } 95 if (mStatusCallback != null) { 96 mStatusCallback.notifyStatusChanged(); 97 } 98 } 99 }; 100 ImsServiceProxy(int slotId, IBinder binder, int featureType)101 public ImsServiceProxy(int slotId, IBinder binder, int featureType) { 102 super(slotId, binder); 103 mSupportedFeature = featureType; 104 } 105 ImsServiceProxy(int slotId, int featureType)106 public ImsServiceProxy(int slotId, int featureType) { 107 super(slotId, null /*IBinder*/); 108 mSupportedFeature = featureType; 109 } 110 getListener()111 public IImsServiceFeatureListener getListener() { 112 return mListenerBinder; 113 } 114 setBinder(IBinder binder)115 public void setBinder(IBinder binder) { 116 mBinder = binder; 117 } 118 119 @Override startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)120 public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) 121 throws RemoteException { 122 synchronized (mLock) { 123 checkServiceIsReady(); 124 return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature, 125 incomingCallIntent, listener); 126 } 127 } 128 129 @Override endSession(int sessionId)130 public void endSession(int sessionId) throws RemoteException { 131 synchronized (mLock) { 132 checkServiceIsReady(); 133 getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId); 134 } 135 } 136 137 @Override isConnected(int callServiceType, int callType)138 public boolean isConnected(int callServiceType, int callType) 139 throws RemoteException { 140 synchronized (mLock) { 141 checkServiceIsReady(); 142 return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature, 143 callServiceType, callType); 144 } 145 } 146 147 @Override isOpened()148 public boolean isOpened() throws RemoteException { 149 synchronized (mLock) { 150 checkServiceIsReady(); 151 return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature); 152 } 153 } 154 155 @Override addRegistrationListener(IImsRegistrationListener listener)156 public void addRegistrationListener(IImsRegistrationListener listener) 157 throws RemoteException { 158 synchronized (mLock) { 159 checkServiceIsReady(); 160 getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature, 161 listener); 162 } 163 } 164 165 @Override removeRegistrationListener(IImsRegistrationListener listener)166 public void removeRegistrationListener(IImsRegistrationListener listener) 167 throws RemoteException { 168 synchronized (mLock) { 169 checkServiceIsReady(); 170 getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature, 171 listener); 172 } 173 } 174 175 @Override createCallProfile(int sessionId, int callServiceType, int callType)176 public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType) 177 throws RemoteException { 178 synchronized (mLock) { 179 checkServiceIsReady(); 180 return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature, 181 sessionId, callServiceType, callType); 182 } 183 } 184 185 @Override createCallSession(int sessionId, ImsCallProfile profile, IImsCallSessionListener listener)186 public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile, 187 IImsCallSessionListener listener) throws RemoteException { 188 synchronized (mLock) { 189 checkServiceIsReady(); 190 return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature, 191 sessionId, profile, listener); 192 } 193 } 194 195 @Override getPendingCallSession(int sessionId, String callId)196 public IImsCallSession getPendingCallSession(int sessionId, String callId) 197 throws RemoteException { 198 synchronized (mLock) { 199 checkServiceIsReady(); 200 return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature, 201 sessionId, callId); 202 } 203 } 204 205 @Override getUtInterface()206 public IImsUt getUtInterface() throws RemoteException { 207 synchronized (mLock) { 208 checkServiceIsReady(); 209 return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature); 210 } 211 } 212 213 @Override getConfigInterface()214 public IImsConfig getConfigInterface() throws RemoteException { 215 synchronized (mLock) { 216 checkServiceIsReady(); 217 return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature); 218 } 219 } 220 221 @Override turnOnIms()222 public void turnOnIms() throws RemoteException { 223 synchronized (mLock) { 224 checkServiceIsReady(); 225 getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature); 226 } 227 } 228 229 @Override turnOffIms()230 public void turnOffIms() throws RemoteException { 231 synchronized (mLock) { 232 checkServiceIsReady(); 233 getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature); 234 } 235 } 236 237 @Override getEcbmInterface()238 public IImsEcbm getEcbmInterface() throws RemoteException { 239 synchronized (mLock) { 240 checkServiceIsReady(); 241 return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature); 242 } 243 } 244 245 @Override setUiTTYMode(int uiTtyMode, Message onComplete)246 public void setUiTTYMode(int uiTtyMode, Message onComplete) 247 throws RemoteException { 248 synchronized (mLock) { 249 checkServiceIsReady(); 250 getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode, 251 onComplete); 252 } 253 } 254 255 @Override getMultiEndpointInterface()256 public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { 257 synchronized (mLock) { 258 checkServiceIsReady(); 259 return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId, 260 mSupportedFeature); 261 } 262 } 263 264 @Override getFeatureStatus()265 public int getFeatureStatus() { 266 synchronized (mLock) { 267 if (isBinderAlive() && mFeatureStatusCached != null) { 268 Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached); 269 return mFeatureStatusCached; 270 } 271 } 272 // Don't synchronize on Binder call. 273 Integer status = retrieveFeatureStatus(); 274 synchronized (mLock) { 275 if (status == null) { 276 return ImsFeature.STATE_NOT_AVAILABLE; 277 } 278 // Cache only non-null value for feature status. 279 mFeatureStatusCached = status; 280 } 281 Log.i(LOG_TAG, "getFeatureStatus - returning " + status); 282 return status; 283 } 284 285 /** 286 * Internal method used to retrieve the feature status from the corresponding ImsService. 287 */ retrieveFeatureStatus()288 private Integer retrieveFeatureStatus() { 289 if (mBinder != null) { 290 try { 291 return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature); 292 } catch (RemoteException e) { 293 // Status check failed, don't update cache 294 } 295 } 296 return null; 297 } 298 299 /** 300 * @param c Callback that will fire when the feature status has changed. 301 */ setStatusCallback(INotifyStatusChanged c)302 public void setStatusCallback(INotifyStatusChanged c) { 303 mStatusCallback = c; 304 } 305 306 /** 307 * @return Returns true if the ImsService is ready to take commands, false otherwise. If this 308 * method returns false, it doesn't mean that the Binder connection is not available (use 309 * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands 310 * at this time. 311 * 312 * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take 313 * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}. 314 */ isBinderReady()315 public boolean isBinderReady() { 316 return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY; 317 } 318 319 @Override isBinderAlive()320 public boolean isBinderAlive() { 321 return mIsAvailable && mBinder != null && mBinder.isBinderAlive(); 322 } 323 checkServiceIsReady()324 protected void checkServiceIsReady() throws RemoteException { 325 if (!isBinderReady()) { 326 throw new RemoteException("ImsServiceProxy is not ready to accept commands."); 327 } 328 } 329 getServiceInterface(IBinder b)330 private IImsServiceController getServiceInterface(IBinder b) { 331 return IImsServiceController.Stub.asInterface(b); 332 } 333 } 334