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.phone; 18 19 import android.net.Uri; 20 import android.os.Binder; 21 import android.os.RemoteException; 22 import android.os.ServiceSpecificException; 23 import android.telephony.SubscriptionManager; 24 import android.telephony.TelephonyFrameworkInitializer; 25 import android.telephony.ims.ImsException; 26 import android.telephony.ims.RegistrationManager; 27 import android.telephony.ims.aidl.IImsCapabilityCallback; 28 import android.telephony.ims.aidl.IImsRcsController; 29 import android.telephony.ims.aidl.IImsRegistrationCallback; 30 import android.telephony.ims.aidl.IRcsUceControllerCallback; 31 import android.telephony.ims.feature.RcsFeature; 32 import android.telephony.ims.stub.ImsRegistrationImplBase; 33 import android.util.Log; 34 35 import com.android.ims.ImsManager; 36 import com.android.internal.telephony.IIntegerConsumer; 37 import com.android.internal.telephony.Phone; 38 import com.android.internal.telephony.TelephonyPermissions; 39 import com.android.internal.telephony.imsphone.ImsPhone; 40 import com.android.services.telephony.rcs.RcsFeatureController; 41 import com.android.services.telephony.rcs.TelephonyRcsService; 42 import com.android.services.telephony.rcs.UserCapabilityExchangeImpl; 43 44 import java.util.List; 45 46 /** 47 * Implementation of the IImsRcsController interface. 48 */ 49 public class ImsRcsController extends IImsRcsController.Stub { 50 private static final String TAG = "ImsRcsController"; 51 52 /** The singleton instance. */ 53 private static ImsRcsController sInstance; 54 55 private PhoneGlobals mApp; 56 private TelephonyRcsService mRcsService; 57 58 /** 59 * Initialize the singleton ImsRcsController instance. 60 * This is only done once, at startup, from PhoneApp.onCreate(). 61 */ init(PhoneGlobals app)62 static ImsRcsController init(PhoneGlobals app) { 63 synchronized (ImsRcsController.class) { 64 if (sInstance == null) { 65 sInstance = new ImsRcsController(app); 66 } else { 67 Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance); 68 } 69 return sInstance; 70 } 71 } 72 73 /** Private constructor; @see init() */ ImsRcsController(PhoneGlobals app)74 private ImsRcsController(PhoneGlobals app) { 75 Log.i(TAG, "ImsRcsController"); 76 mApp = app; 77 TelephonyFrameworkInitializer 78 .getTelephonyServiceManager().getTelephonyImsServiceRegisterer().register(this); 79 } 80 81 /** 82 * Register a {@link RegistrationManager.RegistrationCallback} to receive IMS network 83 * registration state. 84 */ 85 @Override registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback)86 public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) { 87 enforceReadPrivilegedPermission("registerImsRegistrationCallback"); 88 final long token = Binder.clearCallingIdentity(); 89 try { 90 getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback); 91 } catch (ImsException e) { 92 Log.e(TAG, "registerImsRegistrationCallback: sudId=" + subId + ", " + e.getMessage()); 93 throw new ServiceSpecificException(e.getCode()); 94 } finally { 95 Binder.restoreCallingIdentity(token); 96 } 97 } 98 99 /** 100 * Removes an existing {@link RegistrationManager.RegistrationCallback}. 101 */ 102 @Override unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback)103 public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) { 104 enforceReadPrivilegedPermission("unregisterImsRegistrationCallback"); 105 final long token = Binder.clearCallingIdentity(); 106 try { 107 getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback); 108 } catch (ServiceSpecificException e) { 109 Log.e(TAG, "unregisterImsRegistrationCallback: error=" + e.errorCode); 110 } finally { 111 Binder.restoreCallingIdentity(token); 112 } 113 } 114 115 /** 116 * Get the IMS service registration state for the RcsFeature associated with this sub id. 117 */ 118 @Override getImsRcsRegistrationState(int subId, IIntegerConsumer consumer)119 public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) { 120 enforceReadPrivilegedPermission("getImsRcsRegistrationState"); 121 final long token = Binder.clearCallingIdentity(); 122 try { 123 getRcsFeatureController(subId).getRegistrationState(regState -> { 124 try { 125 consumer.accept((regState == null) 126 ? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState); 127 } catch (RemoteException e) { 128 Log.w(TAG, "getImsRcsRegistrationState: callback is not available."); 129 } 130 }); 131 } finally { 132 Binder.restoreCallingIdentity(token); 133 } 134 } 135 136 /** 137 * Gets the Transport Type associated with the current IMS RCS registration. 138 */ 139 @Override getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer)140 public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) { 141 enforceReadPrivilegedPermission("getImsRcsRegistrationTransportType"); 142 final long token = Binder.clearCallingIdentity(); 143 try { 144 getRcsFeatureController(subId).getRegistrationTech(regTech -> { 145 // Convert registration tech from ImsRegistrationImplBase -> RegistrationManager 146 int regTechConverted = (regTech == null) 147 ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech; 148 regTechConverted = RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get( 149 regTechConverted); 150 try { 151 consumer.accept(regTechConverted); 152 } catch (RemoteException e) { 153 Log.w(TAG, "getImsRcsRegistrationTransportType: callback is not available."); 154 } 155 }); 156 } finally { 157 Binder.restoreCallingIdentity(token); 158 } 159 } 160 161 /** 162 * Register a capability callback which will provide RCS availability updates for the 163 * subscription specified. 164 * 165 * @param subId the subscription ID 166 * @param callback The ImsCapabilityCallback to be registered. 167 */ 168 @Override registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)169 public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) { 170 enforceReadPrivilegedPermission("registerRcsAvailabilityCallback"); 171 final long token = Binder.clearCallingIdentity(); 172 try { 173 getRcsFeatureController(subId).registerRcsAvailabilityCallback(subId, callback); 174 } catch (ImsException e) { 175 Log.e(TAG, "registerRcsAvailabilityCallback: sudId=" + subId + ", " + e.getMessage()); 176 throw new ServiceSpecificException(e.getCode()); 177 } finally { 178 Binder.restoreCallingIdentity(token); 179 } 180 } 181 182 /** 183 * Remove the registered capability callback. 184 * 185 * @param subId the subscription ID 186 * @param callback The ImsCapabilityCallback to be removed. 187 */ 188 @Override unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)189 public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) { 190 enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback"); 191 final long token = Binder.clearCallingIdentity(); 192 try { 193 getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback); 194 } finally { 195 Binder.restoreCallingIdentity(token); 196 } 197 } 198 199 /** 200 * Query for the capability of an IMS RCS service 201 * 202 * @param subId the subscription ID 203 * @param capability the RCS capability to query. 204 * @param radioTech the radio tech that this capability failed for 205 * @return true if the RCS capability is capable for this subscription, false otherwise. 206 */ 207 @Override isCapable(int subId, @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)208 public boolean isCapable(int subId, 209 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, 210 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { 211 enforceReadPrivilegedPermission("isCapable"); 212 final long token = Binder.clearCallingIdentity(); 213 try { 214 return getRcsFeatureController(subId).isCapable(capability, radioTech); 215 } catch (ImsException e) { 216 Log.e(TAG, "isCapable: sudId=" + subId 217 + ", capability=" + capability + ", " + e.getMessage()); 218 return false; 219 } finally { 220 Binder.restoreCallingIdentity(token); 221 } 222 } 223 224 /** 225 * Query the availability of an IMS RCS capability. 226 * 227 * @param subId the subscription ID 228 * @param capability the RCS capability to query. 229 * @return true if the RCS capability is currently available for the associated subscription, 230 * false otherwise. 231 */ 232 @Override isAvailable(int subId, @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability)233 public boolean isAvailable(int subId, 234 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) { 235 enforceReadPrivilegedPermission("isAvailable"); 236 final long token = Binder.clearCallingIdentity(); 237 try { 238 return getRcsFeatureController(subId).isAvailable(capability); 239 } catch (ImsException e) { 240 Log.e(TAG, "isAvailable: sudId=" + subId 241 + ", capability=" + capability + ", " + e.getMessage()); 242 return false; 243 } finally { 244 Binder.restoreCallingIdentity(token); 245 } 246 } 247 248 @Override requestCapabilities(int subId, String callingPackage, String callingFeatureId, List<Uri> contactNumbers, IRcsUceControllerCallback c)249 public void requestCapabilities(int subId, String callingPackage, String callingFeatureId, 250 List<Uri> contactNumbers, IRcsUceControllerCallback c) { 251 enforceReadPrivilegedPermission("requestCapabilities"); 252 if (!isUceSettingEnabled(subId, callingPackage, callingFeatureId)) { 253 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, 254 "The user has not enabled UCE for this subscription."); 255 } 256 final long token = Binder.clearCallingIdentity(); 257 try { 258 UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature( 259 UserCapabilityExchangeImpl.class); 260 if (uce == null) { 261 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, 262 "This subscription does not support UCE."); 263 } 264 uce.requestCapabilities(contactNumbers, c); 265 } finally { 266 Binder.restoreCallingIdentity(token); 267 } 268 } 269 270 @Override getUcePublishState(int subId)271 public int getUcePublishState(int subId) { 272 enforceReadPrivilegedPermission("getUcePublishState"); 273 final long token = Binder.clearCallingIdentity(); 274 try { 275 UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature( 276 UserCapabilityExchangeImpl.class); 277 if (uce == null) { 278 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, 279 "This subscription does not support UCE."); 280 } 281 return uce.getUcePublishState(); 282 } finally { 283 Binder.restoreCallingIdentity(token); 284 } 285 } 286 287 @Override isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId)288 public boolean isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId) { 289 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( 290 mApp, subId, callingPackage, callingFeatureId, "isUceSettingEnabled")) { 291 Log.w(TAG, "isUceSettingEnabled: READ_PHONE_STATE app op disabled when accessing " 292 + "isUceSettingEnabled"); 293 return false; 294 } 295 final long token = Binder.clearCallingIdentity(); 296 try { 297 return SubscriptionManager.getBooleanSubscriptionProperty(subId, 298 SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp); 299 } finally { 300 Binder.restoreCallingIdentity(token); 301 } 302 } 303 304 @Override setUceSettingEnabled(int subId, boolean isEnabled)305 public void setUceSettingEnabled(int subId, boolean isEnabled) { 306 enforceModifyPermission(); 307 final long token = Binder.clearCallingIdentity(); 308 try { 309 SubscriptionManager.setSubscriptionProperty(subId, 310 SubscriptionManager.IMS_RCS_UCE_ENABLED, (isEnabled ? "1" : "0")); 311 } finally { 312 Binder.restoreCallingIdentity(token); 313 } 314 } 315 316 /** 317 * Make sure either called from same process as self (phone) or IPC caller has read privilege. 318 * 319 * @throws SecurityException if the caller does not have the required permission 320 */ enforceReadPrivilegedPermission(String message)321 private void enforceReadPrivilegedPermission(String message) { 322 mApp.enforceCallingOrSelfPermission( 323 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message); 324 } 325 326 /** 327 * Make sure the caller has the MODIFY_PHONE_STATE permission. 328 * 329 * @throws SecurityException if the caller does not have the required permission 330 */ enforceModifyPermission()331 private void enforceModifyPermission() { 332 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null); 333 } 334 335 /** 336 * Retrieve ImsPhone instance. 337 * 338 * @param subId the subscription ID 339 * @return The ImsPhone instance 340 * @throws ServiceSpecificException if getting ImsPhone instance failed. 341 */ getImsPhone(int subId)342 private ImsPhone getImsPhone(int subId) { 343 if (!ImsManager.isImsSupportedOnDevice(mApp)) { 344 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, 345 "IMS is not available on device."); 346 } 347 Phone phone = PhoneGlobals.getPhone(subId); 348 if (phone == null) { 349 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION, 350 "Invalid subscription Id: " + subId); 351 } 352 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone(); 353 if (imsPhone == null) { 354 throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, 355 "Cannot find ImsPhone instance: " + subId); 356 } 357 return imsPhone; 358 } 359 360 /** 361 * Retrieve RcsFeatureManager instance. 362 * 363 * @param subId the subscription ID 364 * @return The RcsFeatureManager instance 365 * @throws ServiceSpecificException if getting RcsFeatureManager instance failed. 366 */ getRcsFeatureController(int subId)367 private RcsFeatureController getRcsFeatureController(int subId) { 368 if (!ImsManager.isImsSupportedOnDevice(mApp)) { 369 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, 370 "IMS is not available on device."); 371 } 372 if (mRcsService == null) { 373 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, 374 "IMS is not available on device."); 375 } 376 Phone phone = PhoneGlobals.getPhone(subId); 377 if (phone == null) { 378 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION, 379 "Invalid subscription Id: " + subId); 380 } 381 int slotId = phone.getPhoneId(); 382 RcsFeatureController c = mRcsService.getFeatureController(slotId); 383 if (c == null) { 384 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, 385 "The requested operation is not supported for subId " + subId); 386 } 387 return c; 388 } 389 setRcsService(TelephonyRcsService rcsService)390 void setRcsService(TelephonyRcsService rcsService) { 391 mRcsService = rcsService; 392 } 393 } 394