1 /* 2 * Copyright (C) 2018 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.Manifest; 20 import android.annotation.CallbackExecutor; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.StringDef; 25 import android.annotation.SystemApi; 26 import android.annotation.WorkerThread; 27 import android.content.Context; 28 import android.content.pm.IPackageManager; 29 import android.content.pm.PackageManager; 30 import android.os.Binder; 31 import android.os.RemoteException; 32 import android.os.ServiceManager; 33 import android.telephony.CarrierConfigManager; 34 import android.telephony.SubscriptionManager; 35 import android.telephony.ims.aidl.IImsConfigCallback; 36 import android.telephony.ims.feature.MmTelFeature; 37 import android.telephony.ims.stub.ImsConfigImplBase; 38 import android.telephony.ims.stub.ImsRegistrationImplBase; 39 40 import com.android.internal.telephony.ITelephony; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.util.concurrent.Executor; 45 46 /** 47 * Manages IMS provisioning and configuration parameters, as well as callbacks for apps to listen 48 * to changes in these configurations. 49 * 50 * IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning 51 * applications and may vary. It is up to the carrier and OEM applications to ensure that the 52 * correct provisioning keys are being used when integrating with a vendor's ImsService. 53 * 54 * Note: For compatibility purposes, the integer values [0 - 99] used in 55 * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys 56 * previously defined in the Android framework. Please do not redefine new provisioning keys in this 57 * range or it may generate collisions with existing keys. Some common constants have also been 58 * defined in this class to make integrating with other system apps easier. 59 * @hide 60 */ 61 @SystemApi 62 public class ProvisioningManager { 63 64 /**@hide*/ 65 @StringDef(prefix = "STRING_QUERY_RESULT_ERROR_", value = { 66 STRING_QUERY_RESULT_ERROR_GENERIC, 67 STRING_QUERY_RESULT_ERROR_NOT_READY 68 }) 69 @Retention(RetentionPolicy.SOURCE) 70 public @interface StringResultError {} 71 72 /** 73 * The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error. 74 */ 75 public static final String STRING_QUERY_RESULT_ERROR_GENERIC = 76 "STRING_QUERY_RESULT_ERROR_GENERIC"; 77 78 /** 79 * The query from {@link #getProvisioningStringValue(int)} has resulted in an error because the 80 * ImsService implementation was not ready for provisioning queries. 81 */ 82 public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = 83 "STRING_QUERY_RESULT_ERROR_NOT_READY"; 84 85 /** 86 * The integer result of provisioning for the queried key is disabled. 87 */ 88 public static final int PROVISIONING_VALUE_DISABLED = 0; 89 90 /** 91 * The integer result of provisioning for the queried key is enabled. 92 */ 93 public static final int PROVISIONING_VALUE_ENABLED = 1; 94 95 96 /** 97 * Override the user-defined WiFi Roaming enabled setting for this subscription, defined in 98 * {@link SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI}, for the purposes of provisioning 99 * the subscription for WiFi Calling. 100 * 101 * @see #getProvisioningIntValue(int) 102 * @see #setProvisioningIntValue(int, int) 103 */ 104 public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; 105 106 /** 107 * Override the user-defined WiFi mode for this subscription, defined in 108 * {@link SubscriptionManager#WFC_MODE_CONTENT_URI}, for the purposes of provisioning 109 * this subscription for WiFi Calling. 110 * 111 * Valid values for this key are: 112 * {@link ImsMmTelManager#WIFI_MODE_WIFI_ONLY}, 113 * {@link ImsMmTelManager#WIFI_MODE_CELLULAR_PREFERRED}, or 114 * {@link ImsMmTelManager#WIFI_MODE_WIFI_PREFERRED}. 115 * 116 * @see #getProvisioningIntValue(int) 117 * @see #setProvisioningIntValue(int, int) 118 */ 119 public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; 120 121 /** 122 * Callback for IMS provisioning changes. 123 */ 124 public static class Callback { 125 126 private static class CallbackBinder extends IImsConfigCallback.Stub { 127 128 private final Callback mLocalConfigurationCallback; 129 private Executor mExecutor; 130 CallbackBinder(Callback localConfigurationCallback)131 private CallbackBinder(Callback localConfigurationCallback) { 132 mLocalConfigurationCallback = localConfigurationCallback; 133 } 134 135 @Override onIntConfigChanged(int item, int value)136 public final void onIntConfigChanged(int item, int value) { 137 Binder.withCleanCallingIdentity(() -> 138 mExecutor.execute(() -> 139 mLocalConfigurationCallback.onProvisioningIntChanged(item, value))); 140 } 141 142 @Override onStringConfigChanged(int item, String value)143 public final void onStringConfigChanged(int item, String value) { 144 Binder.withCleanCallingIdentity(() -> 145 mExecutor.execute(() -> 146 mLocalConfigurationCallback.onProvisioningStringChanged(item, 147 value))); 148 } 149 setExecutor(Executor executor)150 private void setExecutor(Executor executor) { 151 mExecutor = executor; 152 } 153 } 154 155 private final CallbackBinder mBinder = new CallbackBinder(this); 156 157 /** 158 * Called when a provisioning item has changed. 159 * @param item the IMS provisioning key constant, as defined by the OEM. 160 * @param value the new integer value of the IMS provisioning key. 161 */ onProvisioningIntChanged(int item, int value)162 public void onProvisioningIntChanged(int item, int value) { 163 // Base Implementation 164 } 165 166 /** 167 * Called when a provisioning item has changed. 168 * @param item the IMS provisioning key constant, as defined by the OEM. 169 * @param value the new String value of the IMS configuration constant. 170 */ onProvisioningStringChanged(int item, @NonNull String value)171 public void onProvisioningStringChanged(int item, @NonNull String value) { 172 // Base Implementation 173 } 174 175 /**@hide*/ getBinder()176 public final IImsConfigCallback getBinder() { 177 return mBinder; 178 } 179 180 /**@hide*/ setExecutor(Executor executor)181 public void setExecutor(Executor executor) { 182 mBinder.setExecutor(executor); 183 } 184 } 185 186 private int mSubId; 187 188 /** 189 * Create a new {@link ProvisioningManager} for the subscription specified. 190 * 191 * @param subId The ID of the subscription that this ProvisioningManager will use. 192 * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList() 193 * @throws IllegalArgumentException if the subscription is invalid. 194 */ createForSubscriptionId(int subId)195 public static @NonNull ProvisioningManager createForSubscriptionId(int subId) { 196 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 197 throw new IllegalArgumentException("Invalid subscription ID"); 198 } 199 200 return new ProvisioningManager(subId); 201 } 202 ProvisioningManager(int subId)203 private ProvisioningManager(int subId) { 204 mSubId = subId; 205 } 206 207 /** 208 * Register a new {@link Callback} to listen to changes to changes in IMS provisioning. 209 * 210 * When the subscription associated with this callback is removed (SIM removed, ESIM swap, 211 * etc...), this callback will automatically be removed. 212 * @param executor The {@link Executor} to call the callback methods on 213 * @param callback The provisioning callbackto be registered. 214 * @see #unregisterProvisioningChangedCallback(Callback) 215 * @see SubscriptionManager.OnSubscriptionsChangedListener 216 * @throws IllegalArgumentException if the subscription associated with this callback is not 217 * active (SIM is not inserted, ESIM inactive) or the subscription is invalid. 218 * @throws ImsException if the subscription associated with this callback is valid, but 219 * the {@link ImsService} associated with the subscription is not available. This can happen if 220 * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed 221 * reason. 222 */ 223 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) registerProvisioningChangedCallback(@onNull @allbackExecutor Executor executor, @NonNull Callback callback)224 public void registerProvisioningChangedCallback(@NonNull @CallbackExecutor Executor executor, 225 @NonNull Callback callback) throws ImsException { 226 if (!isImsAvailableOnDevice()) { 227 throw new ImsException("IMS not available on device.", 228 ImsException.CODE_ERROR_UNSUPPORTED_OPERATION); 229 } 230 callback.setExecutor(executor); 231 try { 232 getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder()); 233 } catch (RemoteException | IllegalStateException e) { 234 throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 235 } 236 } 237 238 /** 239 * Unregister an existing {@link Callback}. When the subscription associated with this 240 * callback is removed (SIM removed, ESIM swap, etc...), this callback will automatically be 241 * removed. If this method is called for an inactive subscription, it will result in a no-op. 242 * @param callback The existing {@link Callback} to be removed. 243 * @see #registerProvisioningChangedCallback(Executor, Callback) 244 * 245 * @throws IllegalArgumentException if the subscription associated with this callback is 246 * invalid. 247 */ 248 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) unregisterProvisioningChangedCallback(@onNull Callback callback)249 public void unregisterProvisioningChangedCallback(@NonNull Callback callback) { 250 try { 251 getITelephony().unregisterImsProvisioningChangedCallback(mSubId, callback.getBinder()); 252 } catch (RemoteException e) { 253 throw e.rethrowAsRuntimeException(); 254 } 255 } 256 257 /** 258 * Query for the integer value associated with the provided key. 259 * 260 * This operation is blocking and should not be performed on the UI thread. 261 * 262 * @param key An integer that represents the provisioning key, which is defined by the OEM. 263 * @return an integer value for the provided key, or 264 * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist. 265 * @throws IllegalArgumentException if the key provided was invalid. 266 */ 267 @WorkerThread 268 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) getProvisioningIntValue(int key)269 public int getProvisioningIntValue(int key) { 270 try { 271 return getITelephony().getImsProvisioningInt(mSubId, key); 272 } catch (RemoteException e) { 273 throw e.rethrowAsRuntimeException(); 274 } 275 } 276 277 /** 278 * Query for the String value associated with the provided key. 279 * 280 * This operation is blocking and should not be performed on the UI thread. 281 * 282 * @param key A String that represents the provisioning key, which is defined by the OEM. 283 * @return a String value for the provided key, {@code null} if the key doesn't exist, or 284 * {@link StringResultError} if there was an error getting the value for the provided key. 285 * @throws IllegalArgumentException if the key provided was invalid. 286 */ 287 @WorkerThread 288 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) getProvisioningStringValue(int key)289 public @Nullable @StringResultError String getProvisioningStringValue(int key) { 290 try { 291 return getITelephony().getImsProvisioningString(mSubId, key); 292 } catch (RemoteException e) { 293 throw e.rethrowAsRuntimeException(); 294 } 295 } 296 297 /** 298 * Set the integer value associated with the provided key. 299 * 300 * This operation is blocking and should not be performed on the UI thread. 301 * 302 * Use {@link #setProvisioningStringValue(int, String)} with proper namespacing (to be defined 303 * per OEM or carrier) when possible instead to avoid key collision if needed. 304 * @param key An integer that represents the provisioning key, which is defined by the OEM. 305 * @param value a integer value for the provided key. 306 * @return the result of setting the configuration value. 307 */ 308 @WorkerThread 309 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setProvisioningIntValue(int key, int value)310 public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) { 311 try { 312 return getITelephony().setImsProvisioningInt(mSubId, key, value); 313 } catch (RemoteException e) { 314 throw e.rethrowAsRuntimeException(); 315 } 316 } 317 318 /** 319 * Set the String value associated with the provided key. 320 * 321 * This operation is blocking and should not be performed on the UI thread. 322 * 323 * @param key A String that represents the provisioning key, which is defined by the OEM and 324 * should be appropriately namespaced to avoid collision. 325 * @param value a String value for the provided key. 326 * @return the result of setting the configuration value. 327 */ 328 @WorkerThread 329 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setProvisioningStringValue(int key, @NonNull String value)330 public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key, 331 @NonNull String value) { 332 try { 333 return getITelephony().setImsProvisioningString(mSubId, key, value); 334 } catch (RemoteException e) { 335 throw e.rethrowAsRuntimeException(); 336 } 337 } 338 339 /** 340 * Set the provisioning status for the IMS MmTel capability using the specified subscription. 341 * 342 * Provisioning may or may not be required, depending on the carrier configuration. If 343 * provisioning is not required for the carrier associated with this subscription or the device 344 * does not support the capability/technology combination specified, this operation will be a 345 * no-op. 346 * 347 * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL 348 * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL 349 * @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise. 350 */ 351 @WorkerThread 352 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setProvisioningStatusForCapability( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned)353 public void setProvisioningStatusForCapability( 354 @MmTelFeature.MmTelCapabilities.MmTelCapability int capability, 355 @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) { 356 try { 357 getITelephony().setImsProvisioningStatusForCapability(mSubId, capability, tech, 358 isProvisioned); 359 } catch (RemoteException e) { 360 throw e.rethrowAsRuntimeException(); 361 } 362 } 363 364 /** 365 * Get the provisioning status for the IMS MmTel capability specified. 366 * 367 * If provisioning is not required for the queried 368 * {@link MmTelFeature.MmTelCapabilities.MmTelCapability} and 369 * {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will 370 * always return {@code true}. 371 * 372 * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL 373 * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL 374 * @return true if the device is provisioned for the capability or does not require 375 * provisioning, false if the capability does require provisioning and has not been 376 * provisioned yet. 377 */ 378 @WorkerThread 379 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) getProvisioningStatusForCapability( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech)380 public boolean getProvisioningStatusForCapability( 381 @MmTelFeature.MmTelCapabilities.MmTelCapability int capability, 382 @ImsRegistrationImplBase.ImsRegistrationTech int tech) { 383 try { 384 return getITelephony().getImsProvisioningStatusForCapability(mSubId, capability, tech); 385 } catch (RemoteException e) { 386 throw e.rethrowAsRuntimeException(); 387 } 388 } 389 isImsAvailableOnDevice()390 private static boolean isImsAvailableOnDevice() { 391 IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 392 if (pm == null) { 393 // For some reason package manger is not available.. This will fail internally anyways, 394 // so do not throw error and allow. 395 return true; 396 } 397 try { 398 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS, 0); 399 } catch (RemoteException e) { 400 // For some reason package manger is not available.. This will fail internally anyways, 401 // so do not throw error and allow. 402 } 403 return true; 404 } 405 getITelephony()406 private static ITelephony getITelephony() { 407 ITelephony binder = ITelephony.Stub.asInterface( 408 ServiceManager.getService(Context.TELEPHONY_SERVICE)); 409 if (binder == null) { 410 throw new RuntimeException("Could not find Telephony Service."); 411 } 412 return binder; 413 } 414 } 415