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.stub; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.SystemApi; 22 import android.content.Context; 23 import android.os.PersistableBundle; 24 import android.os.RemoteException; 25 import android.telephony.ims.ProvisioningManager; 26 import android.telephony.ims.RcsClientConfiguration; 27 import android.telephony.ims.RcsConfig; 28 import android.telephony.ims.aidl.IImsConfig; 29 import android.telephony.ims.aidl.IImsConfigCallback; 30 import android.telephony.ims.aidl.IRcsConfigCallback; 31 import android.util.Log; 32 33 import com.android.ims.ImsConfig; 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.telephony.util.RemoteCallbackListExt; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.lang.ref.WeakReference; 40 import java.util.Arrays; 41 import java.util.HashMap; 42 43 /** 44 * Controls the modification of IMS specific configurations. For more information on the supported 45 * IMS configuration constants, see {@link ImsConfig}. 46 * 47 * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface. 48 * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes. 49 * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in 50 * during initialization, or times when a lot of configuration parameters are being set/get 51 * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed 52 * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be 53 * performed every time. 54 * @hide 55 */ 56 @SystemApi 57 public class ImsConfigImplBase { 58 59 private static final String TAG = "ImsConfigImplBase"; 60 61 /** 62 * Implements the IImsConfig AIDL interface, which is called by potentially many processes 63 * in order to get/set configuration parameters. 64 * 65 * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl 66 * with actual implementations from vendors. This class caches provisioned values from 67 * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in, 68 * it first checks cache layer. If missed, it will call the vendor implementation of 69 * ImsConfigImplBase API. 70 * and cache the return value if the set succeeds. 71 * 72 * Provides APIs to get/set the IMS service feature/capability/parameters. 73 * The config items include: 74 * 1) Items provisioned by the operator. 75 * 2) Items configured by user. Mainly service feature class. 76 * 77 * @hide 78 */ 79 @VisibleForTesting 80 static public class ImsConfigStub extends IImsConfig.Stub { 81 WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference; 82 private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>(); 83 private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>(); 84 85 @VisibleForTesting ImsConfigStub(ImsConfigImplBase imsConfigImplBase)86 public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) { 87 mImsConfigImplBaseWeakReference = 88 new WeakReference<ImsConfigImplBase>(imsConfigImplBase); 89 } 90 91 @Override addImsConfigCallback(IImsConfigCallback c)92 public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { 93 getImsConfigImpl().addImsConfigCallback(c); 94 } 95 96 @Override removeImsConfigCallback(IImsConfigCallback c)97 public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException { 98 getImsConfigImpl().removeImsConfigCallback(c); 99 } 100 101 /** 102 * Gets the value for ims service/capabilities parameters. It first checks its local cache, 103 * if missed, it will call ImsConfigImplBase.getConfigInt. 104 * Synchronous blocking call. 105 * 106 * @param item integer key 107 * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if 108 * unavailable. 109 */ 110 @Override getConfigInt(int item)111 public synchronized int getConfigInt(int item) throws RemoteException { 112 if (mProvisionedIntValue.containsKey(item)) { 113 return mProvisionedIntValue.get(item); 114 } else { 115 int retVal = getImsConfigImpl().getConfigInt(item); 116 if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) { 117 updateCachedValue(item, retVal, false); 118 } 119 return retVal; 120 } 121 } 122 123 /** 124 * Gets the value for ims service/capabilities parameters. It first checks its local cache, 125 * if missed, it will call #ImsConfigImplBase.getConfigString. 126 * Synchronous blocking call. 127 * 128 * @param item integer key 129 * @return value in String format. 130 */ 131 @Override getConfigString(int item)132 public synchronized String getConfigString(int item) throws RemoteException { 133 if (mProvisionedStringValue.containsKey(item)) { 134 return mProvisionedStringValue.get(item); 135 } else { 136 String retVal = getImsConfigImpl().getConfigString(item); 137 if (retVal != null) { 138 updateCachedValue(item, retVal, false); 139 } 140 return retVal; 141 } 142 } 143 144 /** 145 * Sets the value for IMS service/capabilities parameters by the operator device 146 * management entity. It sets the config item value in the provisioned storage 147 * from which the main value is derived, and write it into local cache. 148 * Synchronous blocking call. 149 * 150 * @param item integer key 151 * @param value in Integer format. 152 * @return the result of setting the configuration value, defined as either 153 * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. 154 */ 155 @Override setConfigInt(int item, int value)156 public synchronized int setConfigInt(int item, int value) throws RemoteException { 157 mProvisionedIntValue.remove(item); 158 int retVal = getImsConfigImpl().setConfig(item, value); 159 if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) { 160 updateCachedValue(item, value, true); 161 } else { 162 Log.d(TAG, "Set provision value of " + item + 163 " to " + value + " failed with error code " + retVal); 164 } 165 166 return retVal; 167 } 168 169 /** 170 * Sets the value for IMS service/capabilities parameters by the operator device 171 * management entity. It sets the config item value in the provisioned storage 172 * from which the main value is derived, and write it into local cache. 173 * Synchronous blocking call. 174 * 175 * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. 176 * @param value in String format. 177 * @return the result of setting the configuration value, defined as either 178 * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. 179 */ 180 @Override setConfigString(int item, String value)181 public synchronized int setConfigString(int item, String value) 182 throws RemoteException { 183 mProvisionedStringValue.remove(item); 184 int retVal = getImsConfigImpl().setConfig(item, value); 185 if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) { 186 updateCachedValue(item, value, true); 187 } 188 189 return retVal; 190 } 191 192 @Override updateImsCarrierConfigs(PersistableBundle bundle)193 public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException { 194 getImsConfigImpl().updateImsCarrierConfigs(bundle); 195 } 196 getImsConfigImpl()197 private ImsConfigImplBase getImsConfigImpl() throws RemoteException { 198 ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get(); 199 if (ref == null) { 200 throw new RemoteException("Fail to get ImsConfigImpl"); 201 } else { 202 return ref; 203 } 204 } 205 206 @Override notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)207 public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) 208 throws RemoteException { 209 getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed); 210 } 211 212 @Override notifyRcsAutoConfigurationRemoved()213 public void notifyRcsAutoConfigurationRemoved() 214 throws RemoteException { 215 getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved(); 216 } 217 notifyImsConfigChanged(int item, int value)218 private void notifyImsConfigChanged(int item, int value) throws RemoteException { 219 getImsConfigImpl().notifyConfigChanged(item, value); 220 } 221 notifyImsConfigChanged(int item, String value)222 private void notifyImsConfigChanged(int item, String value) throws RemoteException { 223 getImsConfigImpl().notifyConfigChanged(item, value); 224 } 225 updateCachedValue(int item, int value, boolean notifyChange)226 protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) 227 throws RemoteException { 228 mProvisionedIntValue.put(item, value); 229 if (notifyChange) { 230 notifyImsConfigChanged(item, value); 231 } 232 } 233 updateCachedValue(int item, String value, boolean notifyChange)234 protected synchronized void updateCachedValue(int item, String value, 235 boolean notifyChange) throws RemoteException { 236 mProvisionedStringValue.put(item, value); 237 if (notifyChange) { 238 notifyImsConfigChanged(item, value); 239 } 240 } 241 242 @Override addRcsConfigCallback(IRcsConfigCallback c)243 public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { 244 getImsConfigImpl().addRcsConfigCallback(c); 245 } 246 247 @Override removeRcsConfigCallback(IRcsConfigCallback c)248 public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { 249 getImsConfigImpl().removeRcsConfigCallback(c); 250 } 251 252 @Override triggerRcsReconfiguration()253 public void triggerRcsReconfiguration() throws RemoteException { 254 getImsConfigImpl().triggerAutoConfiguration(); 255 } 256 257 @Override setRcsClientConfiguration(RcsClientConfiguration rcc)258 public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException { 259 getImsConfigImpl().setRcsClientConfiguration(rcc); 260 } 261 262 @Override notifyIntImsConfigChanged(int item, int value)263 public void notifyIntImsConfigChanged(int item, int value) throws RemoteException { 264 notifyImsConfigChanged(item, value); 265 } 266 267 @Override notifyStringImsConfigChanged(int item, String value)268 public void notifyStringImsConfigChanged(int item, String value) throws RemoteException { 269 notifyImsConfigChanged(item, value); 270 } 271 } 272 273 /** 274 * The configuration requested resulted in an unknown result. This may happen if the 275 * IMS configurations are unavailable. 276 */ 277 public static final int CONFIG_RESULT_UNKNOWN = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN; 278 279 /** 280 * Setting the configuration value completed. 281 */ 282 public static final int CONFIG_RESULT_SUCCESS = 0; 283 /** 284 * Setting the configuration value failed. 285 */ 286 public static final int CONFIG_RESULT_FAILED = 1; 287 288 /** 289 * @hide 290 */ 291 @Retention(RetentionPolicy.SOURCE) 292 @IntDef(prefix = "CONFIG_RESULT_", value = { 293 CONFIG_RESULT_SUCCESS, 294 CONFIG_RESULT_FAILED 295 }) 296 public @interface SetConfigResult {} 297 298 private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks = 299 new RemoteCallbackListExt<>(); 300 private final RemoteCallbackListExt<IRcsConfigCallback> mRcsCallbacks = 301 new RemoteCallbackListExt<>(); 302 private byte[] mRcsConfigData; 303 ImsConfigStub mImsConfigStub; 304 305 /** 306 * Used for compatibility between older versions of the ImsService. 307 * @hide 308 */ ImsConfigImplBase(Context context)309 public ImsConfigImplBase(Context context) { 310 mImsConfigStub = new ImsConfigStub(this); 311 } 312 ImsConfigImplBase()313 public ImsConfigImplBase() { 314 mImsConfigStub = new ImsConfigStub(this); 315 } 316 317 /** 318 * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks 319 * notified when a value in the configuration changes. 320 * @param c callback to add. 321 */ addImsConfigCallback(IImsConfigCallback c)322 private void addImsConfigCallback(IImsConfigCallback c) { 323 mCallbacks.register(c); 324 } 325 /** 326 * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks 327 * notified when a value in the configuration changes. 328 * @param c callback to remove. 329 */ removeImsConfigCallback(IImsConfigCallback c)330 private void removeImsConfigCallback(IImsConfigCallback c) { 331 mCallbacks.unregister(c); 332 } 333 334 /** 335 * @param item 336 * @param value 337 */ notifyConfigChanged(int item, int value)338 private final void notifyConfigChanged(int item, int value) { 339 // can be null in testing 340 if (mCallbacks == null) { 341 return; 342 } 343 mCallbacks.broadcastAction(c -> { 344 try { 345 c.onIntConfigChanged(item, value); 346 } catch (RemoteException e) { 347 Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping."); 348 } 349 }); 350 } 351 notifyConfigChanged(int item, String value)352 private void notifyConfigChanged(int item, String value) { 353 // can be null in testing 354 if (mCallbacks == null) { 355 return; 356 } 357 mCallbacks.broadcastAction(c -> { 358 try { 359 c.onStringConfigChanged(item, value); 360 } catch (RemoteException e) { 361 Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping."); 362 } 363 }); 364 } 365 addRcsConfigCallback(IRcsConfigCallback c)366 private void addRcsConfigCallback(IRcsConfigCallback c) { 367 mRcsCallbacks.register(c); 368 if (mRcsConfigData != null) { 369 try { 370 c.onConfigurationChanged(mRcsConfigData); 371 } catch (RemoteException e) { 372 Log.w(TAG, "dead binder to call onConfigurationChanged, skipping."); 373 } 374 } 375 } 376 removeRcsConfigCallback(IRcsConfigCallback c)377 private void removeRcsConfigCallback(IRcsConfigCallback c) { 378 mRcsCallbacks.unregister(c); 379 } 380 onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)381 private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { 382 // cache uncompressed config 383 config = isCompressed ? RcsConfig.decompressGzip(config) : config; 384 if (Arrays.equals(mRcsConfigData, config)) { 385 return; 386 } 387 mRcsConfigData = config; 388 389 // can be null in testing 390 if (mRcsCallbacks != null) { 391 mRcsCallbacks.broadcastAction(c -> { 392 try { 393 c.onConfigurationChanged(mRcsConfigData); 394 } catch (RemoteException e) { 395 Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping."); 396 } 397 }); 398 } 399 notifyRcsAutoConfigurationReceived(config, isCompressed); 400 } 401 onNotifyRcsAutoConfigurationRemoved()402 private void onNotifyRcsAutoConfigurationRemoved() { 403 mRcsConfigData = null; 404 if (mRcsCallbacks != null) { 405 mRcsCallbacks.broadcastAction(c -> { 406 try { 407 c.onConfigurationReset(); 408 } catch (RemoteException e) { 409 Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping."); 410 } 411 }); 412 } 413 notifyRcsAutoConfigurationRemoved(); 414 } 415 416 /** 417 * @hide 418 */ getIImsConfig()419 public IImsConfig getIImsConfig() { return mImsConfigStub; } 420 421 /** 422 * Updates provisioning value and notifies the framework of the change. 423 * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded. 424 * This should only be used when the IMS implementer implicitly changed provisioned values. 425 * 426 * @param item an integer key. 427 * @param value in Integer format. 428 */ notifyProvisionedValueChanged(int item, int value)429 public final void notifyProvisionedValueChanged(int item, int value) { 430 try { 431 mImsConfigStub.updateCachedValue(item, value, true); 432 } catch (RemoteException e) { 433 Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead."); 434 } 435 } 436 437 /** 438 * Updates provisioning value and notifies the framework of the change. 439 * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded. 440 * This should only be used when the IMS implementer implicitly changed provisioned values. 441 * 442 * @param item an integer key. 443 * @param value in String format. 444 */ notifyProvisionedValueChanged(int item, String value)445 public final void notifyProvisionedValueChanged(int item, String value) { 446 try { 447 mImsConfigStub.updateCachedValue(item, value, true); 448 } catch (RemoteException e) { 449 Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead."); 450 } 451 } 452 453 /** 454 * The framework has received an RCS autoconfiguration XML file for provisioning. 455 * 456 * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format. 457 * @param isCompressed The XML file is compressed in gzip format and must be decompressed 458 * before being read. 459 * 460 */ notifyRcsAutoConfigurationReceived(@onNull byte[] config, boolean isCompressed)461 public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { 462 } 463 464 /** 465 * The RCS autoconfiguration XML file is removed or invalid. 466 */ notifyRcsAutoConfigurationRemoved()467 public void notifyRcsAutoConfigurationRemoved() { 468 } 469 470 /** 471 * Sets the configuration value for this ImsService. 472 * 473 * @param item an integer key. 474 * @param value an integer containing the configuration value. 475 * @return the result of setting the configuration value. 476 */ setConfig(int item, int value)477 public @SetConfigResult int setConfig(int item, int value) { 478 // Base Implementation - To be overridden. 479 return CONFIG_RESULT_FAILED; 480 } 481 482 /** 483 * Sets the configuration value for this ImsService. 484 * 485 * @param item an integer key. 486 * @param value a String containing the new configuration value. 487 * @return Result of setting the configuration value. 488 */ setConfig(int item, String value)489 public @SetConfigResult int setConfig(int item, String value) { 490 // Base Implementation - To be overridden. 491 return CONFIG_RESULT_FAILED; 492 } 493 494 /** 495 * Gets the currently stored value configuration value from the ImsService for {@code item}. 496 * 497 * @param item an integer key. 498 * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if 499 * unavailable. 500 */ getConfigInt(int item)501 public int getConfigInt(int item) { 502 // Base Implementation - To be overridden. 503 return CONFIG_RESULT_UNKNOWN; 504 } 505 506 /** 507 * Gets the currently stored value configuration value from the ImsService for {@code item}. 508 * 509 * @param item an integer key. 510 * @return configuration value, stored in String format or {@code null} if unavailable. 511 */ getConfigString(int item)512 public String getConfigString(int item) { 513 // Base Implementation - To be overridden. 514 return null; 515 } 516 517 /** 518 * @hide 519 */ updateImsCarrierConfigs(PersistableBundle bundle)520 public void updateImsCarrierConfigs(PersistableBundle bundle) { 521 // Base Implementation - Should be overridden 522 } 523 524 /** 525 * Default messaging application parameters are sent to the ACS client 526 * using this interface. 527 * @param rcc RCS client configuration {@link RcsClientConfiguration} 528 */ setRcsClientConfiguration(@onNull RcsClientConfiguration rcc)529 public void setRcsClientConfiguration(@NonNull RcsClientConfiguration rcc) { 530 // Base Implementation - Should be overridden 531 } 532 533 /** 534 * Reconfiguration triggered by the RCS application. Most likely cause 535 * is the 403 forbidden to a SIP/HTTP request 536 */ triggerAutoConfiguration()537 public void triggerAutoConfiguration() { 538 // Base Implementation - Should be overridden 539 } 540 541 /** 542 * Errors during autoconfiguration connection setup are notified by the 543 * ACS client using this interface. 544 * @param errorCode HTTP error received during connection setup. 545 * @param errorString reason phrase received with the error 546 */ notifyAutoConfigurationErrorReceived(int errorCode, @NonNull String errorString)547 public final void notifyAutoConfigurationErrorReceived(int errorCode, 548 @NonNull String errorString) { 549 // can be null in testing 550 if (mRcsCallbacks == null) { 551 return; 552 } 553 mRcsCallbacks.broadcastAction(c -> { 554 try { 555 c.onAutoConfigurationErrorReceived(errorCode, errorString); 556 } catch (RemoteException e) { 557 Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping."); 558 } 559 }); 560 } 561 562 /** 563 * Notifies application that pre-provisioning config is received. 564 * 565 * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific 566 * pre-provisioning configuration XML if the user has not been provisioned for RCS 567 * services yet. When such provisioning XML is received, ACS client must call this 568 * method to notify the application with the XML. 569 * 570 * @param configXml the pre-provisioning config in carrier specified format. 571 */ notifyPreProvisioningReceived(@onNull byte[] configXml)572 public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) { 573 // can be null in testing 574 if (mRcsCallbacks == null) { 575 return; 576 } 577 mRcsCallbacks.broadcastAction(c -> { 578 try { 579 c.onPreProvisioningReceived(configXml); 580 } catch (RemoteException e) { 581 Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping."); 582 } 583 }); 584 } 585 } 586