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 import com.android.internal.telephony.util.TelephonyUtils; 37 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 import java.lang.ref.WeakReference; 41 import java.util.Arrays; 42 import java.util.HashMap; 43 import java.util.concurrent.CancellationException; 44 import java.util.concurrent.CompletableFuture; 45 import java.util.concurrent.CompletionException; 46 import java.util.concurrent.ExecutionException; 47 import java.util.concurrent.Executor; 48 import java.util.concurrent.atomic.AtomicReference; 49 import java.util.function.Supplier; 50 51 52 /** 53 * Controls the modification of IMS specific configurations. For more information on the supported 54 * IMS configuration constants, see {@link ImsConfig}. 55 * 56 * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface. 57 * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes. 58 * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in 59 * during initialization, or times when a lot of configuration parameters are being set/get 60 * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed 61 * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be 62 * performed every time. 63 * @hide 64 */ 65 @SystemApi 66 public class ImsConfigImplBase { 67 68 private static final String TAG = "ImsConfigImplBase"; 69 70 /** 71 * Implements the IImsConfig AIDL interface, which is called by potentially many processes 72 * in order to get/set configuration parameters. 73 * 74 * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl 75 * with actual implementations from vendors. This class caches provisioned values from 76 * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in, 77 * it first checks cache layer. If missed, it will call the vendor implementation of 78 * ImsConfigImplBase API. 79 * and cache the return value if the set succeeds. 80 * 81 * Provides APIs to get/set the IMS service feature/capability/parameters. 82 * The config items include: 83 * 1) Items provisioned by the operator. 84 * 2) Items configured by user. Mainly service feature class. 85 * 86 * @hide 87 */ 88 @VisibleForTesting 89 static public class ImsConfigStub extends IImsConfig.Stub { 90 WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference; 91 private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>(); 92 private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>(); 93 private final Object mLock = new Object(); 94 private Executor mExecutor; 95 96 @VisibleForTesting ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor)97 public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor) { 98 mExecutor = executor; 99 mImsConfigImplBaseWeakReference = 100 new WeakReference<ImsConfigImplBase>(imsConfigImplBase); 101 } 102 103 @Override addImsConfigCallback(IImsConfigCallback c)104 public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { 105 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 106 executeMethodAsync(()-> { 107 try { 108 getImsConfigImpl().addImsConfigCallback(c); 109 } catch (RemoteException e) { 110 exceptionRef.set(e); 111 } 112 }, "addImsConfigCallback"); 113 114 if (exceptionRef.get() != null) { 115 Log.d(TAG, "ImsConfigImplBase Exception addImsConfigCallback"); 116 throw exceptionRef.get(); 117 } 118 } 119 120 @Override removeImsConfigCallback(IImsConfigCallback c)121 public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException { 122 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 123 executeMethodAsync(()-> { 124 try { 125 getImsConfigImpl().removeImsConfigCallback(c); 126 } catch (RemoteException e) { 127 exceptionRef.set(e); 128 } 129 }, "removeImsConfigCallback"); 130 131 if (exceptionRef.get() != null) { 132 Log.d(TAG, "ImsConfigImplBase Exception removeImsConfigCallback"); 133 throw exceptionRef.get(); 134 } 135 } 136 137 /** 138 * Gets the value for ims service/capabilities parameters. It first checks its local cache, 139 * if missed, it will call ImsConfigImplBase.getConfigInt. 140 * Synchronous blocking call. 141 * 142 * @param item integer key 143 * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if 144 * unavailable. 145 */ 146 @Override getConfigInt(int item)147 public int getConfigInt(int item) throws RemoteException { 148 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 149 int retVal = executeMethodAsyncForResult(()-> { 150 int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; 151 synchronized (mLock) { 152 if (mProvisionedIntValue.containsKey(item)) { 153 return mProvisionedIntValue.get(item); 154 } else { 155 try { 156 returnVal = getImsConfigImpl().getConfigInt(item); 157 if (returnVal != ImsConfig.OperationStatusConstants.UNKNOWN) { 158 mProvisionedIntValue.put(item, returnVal); 159 } 160 } catch (RemoteException e) { 161 exceptionRef.set(e); 162 return returnVal; 163 } 164 } 165 } 166 return returnVal; 167 }, "getConfigInt"); 168 169 if (exceptionRef.get() != null) { 170 Log.d(TAG, "ImsConfigImplBase Exception getConfigString"); 171 throw exceptionRef.get(); 172 } 173 174 return retVal; 175 } 176 177 /** 178 * Gets the value for ims service/capabilities parameters. It first checks its local cache, 179 * if missed, it will call #ImsConfigImplBase.getConfigString. 180 * Synchronous blocking call. 181 * 182 * @param item integer key 183 * @return value in String format. 184 */ 185 @Override getConfigString(int item)186 public String getConfigString(int item) throws RemoteException { 187 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 188 String retVal = executeMethodAsyncForResult(()-> { 189 String returnVal = null; 190 synchronized (mLock) { 191 if (mProvisionedStringValue.containsKey(item)) { 192 returnVal = mProvisionedStringValue.get(item); 193 } else { 194 try { 195 returnVal = getImsConfigImpl().getConfigString(item); 196 if (returnVal != null) { 197 mProvisionedStringValue.put(item, returnVal); 198 } 199 } catch (RemoteException e) { 200 exceptionRef.set(e); 201 return returnVal; 202 } 203 } 204 } 205 return returnVal; 206 }, "getConfigString"); 207 208 if (exceptionRef.get() != null) { 209 Log.d(TAG, "ImsConfigImplBase Exception getConfigString"); 210 throw exceptionRef.get(); 211 } 212 213 return retVal; 214 } 215 216 /** 217 * Sets the value for IMS service/capabilities parameters by the operator device 218 * management entity. It sets the config item value in the provisioned storage 219 * from which the main value is derived, and write it into local cache. 220 * Synchronous blocking call. 221 * 222 * @param item integer key 223 * @param value in Integer format. 224 * @return the result of setting the configuration value, defined as either 225 * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. 226 */ 227 @Override setConfigInt(int item, int value)228 public int setConfigInt(int item, int value) throws RemoteException { 229 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 230 int retVal = executeMethodAsyncForResult(()-> { 231 int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; 232 try { 233 synchronized (mLock) { 234 mProvisionedIntValue.remove(item); 235 returnVal = getImsConfigImpl().setConfig(item, value); 236 if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) { 237 mProvisionedIntValue.put(item, value); 238 } else { 239 Log.d(TAG, "Set provision value of " + item 240 + " to " + value + " failed with error code " + returnVal); 241 } 242 } 243 notifyImsConfigChanged(item, value); 244 return returnVal; 245 } catch (RemoteException e) { 246 exceptionRef.set(e); 247 return returnVal; 248 } 249 }, "setConfigInt"); 250 251 if (exceptionRef.get() != null) { 252 Log.d(TAG, "ImsConfigImplBase Exception setConfigInt"); 253 throw exceptionRef.get(); 254 } 255 256 return retVal; 257 } 258 259 /** 260 * Sets the value for IMS service/capabilities parameters by the operator device 261 * management entity. It sets the config item value in the provisioned storage 262 * from which the main value is derived, and write it into local cache. 263 * Synchronous blocking call. 264 * 265 * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. 266 * @param value in String format. 267 * @return the result of setting the configuration value, defined as either 268 * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. 269 */ 270 @Override setConfigString(int item, String value)271 public int setConfigString(int item, String value) 272 throws RemoteException { 273 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 274 int retVal = executeMethodAsyncForResult(()-> { 275 int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; 276 try { 277 synchronized (mLock) { 278 mProvisionedStringValue.remove(item); 279 returnVal = getImsConfigImpl().setConfig(item, value); 280 if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) { 281 mProvisionedStringValue.put(item, value); 282 } 283 } 284 notifyImsConfigChanged(item, value); 285 return returnVal; 286 } catch (RemoteException e) { 287 exceptionRef.set(e); 288 return returnVal; 289 } 290 }, "setConfigString"); 291 292 if (exceptionRef.get() != null) { 293 Log.d(TAG, "ImsConfigImplBase Exception setConfigInt"); 294 throw exceptionRef.get(); 295 } 296 297 return retVal; 298 } 299 300 @Override updateImsCarrierConfigs(PersistableBundle bundle)301 public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException { 302 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 303 executeMethodAsync(()-> { 304 try { 305 getImsConfigImpl().updateImsCarrierConfigs(bundle); 306 } catch (RemoteException e) { 307 exceptionRef.set(e); 308 } 309 }, "updateImsCarrierConfigs"); 310 311 if (exceptionRef.get() != null) { 312 Log.d(TAG, "ImsConfigImplBase Exception updateImsCarrierConfigs"); 313 throw exceptionRef.get(); 314 } 315 } 316 getImsConfigImpl()317 private ImsConfigImplBase getImsConfigImpl() throws RemoteException { 318 ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get(); 319 if (ref == null) { 320 throw new RemoteException("Fail to get ImsConfigImpl"); 321 } else { 322 return ref; 323 } 324 } 325 326 @Override notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)327 public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) 328 throws RemoteException { 329 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 330 executeMethodAsync(()-> { 331 try { 332 getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed); 333 } catch (RemoteException e) { 334 exceptionRef.set(e); 335 } 336 }, "notifyRcsAutoConfigurationReceived"); 337 338 if (exceptionRef.get() != null) { 339 Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationReceived"); 340 throw exceptionRef.get(); 341 } 342 } 343 344 @Override notifyRcsAutoConfigurationRemoved()345 public void notifyRcsAutoConfigurationRemoved() 346 throws RemoteException { 347 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 348 executeMethodAsync(()-> { 349 try { 350 getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved(); 351 } catch (RemoteException e) { 352 exceptionRef.set(e); 353 } 354 }, "notifyRcsAutoConfigurationRemoved"); 355 356 if (exceptionRef.get() != null) { 357 Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationRemoved"); 358 throw exceptionRef.get(); 359 } 360 } 361 notifyImsConfigChanged(int item, int value)362 private void notifyImsConfigChanged(int item, int value) throws RemoteException { 363 getImsConfigImpl().notifyConfigChanged(item, value); 364 } 365 notifyImsConfigChanged(int item, String value)366 private void notifyImsConfigChanged(int item, String value) throws RemoteException { 367 getImsConfigImpl().notifyConfigChanged(item, value); 368 } 369 updateCachedValue(int item, int value)370 protected void updateCachedValue(int item, int value) { 371 synchronized (mLock) { 372 mProvisionedIntValue.put(item, value); 373 } 374 } 375 updateCachedValue(int item, String value)376 protected void updateCachedValue(int item, String value) { 377 synchronized (mLock) { 378 mProvisionedStringValue.put(item, value); 379 } 380 } 381 382 @Override addRcsConfigCallback(IRcsConfigCallback c)383 public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { 384 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 385 executeMethodAsync(()-> { 386 try { 387 getImsConfigImpl().addRcsConfigCallback(c); 388 } catch (RemoteException e) { 389 exceptionRef.set(e); 390 } 391 }, "addRcsConfigCallback"); 392 393 if (exceptionRef.get() != null) { 394 Log.d(TAG, "ImsConfigImplBase Exception addRcsConfigCallback"); 395 throw exceptionRef.get(); 396 } 397 } 398 399 @Override removeRcsConfigCallback(IRcsConfigCallback c)400 public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { 401 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 402 executeMethodAsync(()-> { 403 try { 404 getImsConfigImpl().removeRcsConfigCallback(c); 405 } catch (RemoteException e) { 406 exceptionRef.set(e); 407 } 408 }, "removeRcsConfigCallback"); 409 410 if (exceptionRef.get() != null) { 411 Log.d(TAG, "ImsConfigImplBase Exception removeRcsConfigCallback"); 412 throw exceptionRef.get(); 413 } 414 } 415 416 @Override triggerRcsReconfiguration()417 public void triggerRcsReconfiguration() throws RemoteException { 418 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 419 executeMethodAsync(()-> { 420 try { 421 getImsConfigImpl().triggerAutoConfiguration(); 422 } catch (RemoteException e) { 423 exceptionRef.set(e); 424 } 425 }, "triggerRcsReconfiguration"); 426 427 if (exceptionRef.get() != null) { 428 Log.d(TAG, "ImsConfigImplBase Exception triggerRcsReconfiguration"); 429 throw exceptionRef.get(); 430 } 431 } 432 433 @Override setRcsClientConfiguration(RcsClientConfiguration rcc)434 public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException { 435 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 436 executeMethodAsync(()-> { 437 try { 438 getImsConfigImpl().setRcsClientConfiguration(rcc); 439 } catch (RemoteException e) { 440 exceptionRef.set(e); 441 } 442 }, "setRcsClientConfiguration"); 443 444 if (exceptionRef.get() != null) { 445 Log.d(TAG, "ImsConfigImplBase Exception setRcsClientConfiguration"); 446 throw exceptionRef.get(); 447 } 448 } 449 450 @Override notifyIntImsConfigChanged(int item, int value)451 public void notifyIntImsConfigChanged(int item, int value) throws RemoteException { 452 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 453 executeMethodAsync(()-> { 454 try { 455 notifyImsConfigChanged(item, value); 456 } catch (RemoteException e) { 457 exceptionRef.set(e); 458 } 459 }, "notifyIntImsConfigChanged"); 460 461 if (exceptionRef.get() != null) { 462 Log.d(TAG, "ImsConfigImplBase Exception notifyIntImsConfigChanged"); 463 throw exceptionRef.get(); 464 } 465 } 466 467 @Override notifyStringImsConfigChanged(int item, String value)468 public void notifyStringImsConfigChanged(int item, String value) throws RemoteException { 469 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 470 executeMethodAsync(()-> { 471 try { 472 notifyImsConfigChanged(item, value); 473 } catch (RemoteException e) { 474 exceptionRef.set(e); 475 } 476 }, "notifyStringImsConfigChanged"); 477 478 if (exceptionRef.get() != null) { 479 Log.d(TAG, "ImsConfigImplBase Exception notifyStringImsConfigChanged"); 480 throw exceptionRef.get(); 481 } 482 } 483 484 // Call the methods with a clean calling identity on the executor and wait indefinitely for 485 // the future to return. executeMethodAsync(Runnable r, String errorLogName)486 private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { 487 try { 488 CompletableFuture.runAsync( 489 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 490 } catch (CancellationException | CompletionException e) { 491 Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: " 492 + e.getMessage()); 493 throw new RemoteException(e.getMessage()); 494 } 495 } 496 executeMethodAsyncForResult(Supplier<T> r, String errorLogName)497 private <T> T executeMethodAsyncForResult(Supplier<T> r, 498 String errorLogName) throws RemoteException { 499 CompletableFuture<T> future = CompletableFuture.supplyAsync( 500 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); 501 try { 502 return future.get(); 503 } catch (ExecutionException | InterruptedException e) { 504 Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: " 505 + e.getMessage()); 506 throw new RemoteException(e.getMessage()); 507 } 508 } 509 } 510 511 /** 512 * The configuration requested resulted in an unknown result. This may happen if the 513 * IMS configurations are unavailable. 514 */ 515 public static final int CONFIG_RESULT_UNKNOWN = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN; 516 517 /** 518 * Setting the configuration value completed. 519 */ 520 public static final int CONFIG_RESULT_SUCCESS = 0; 521 /** 522 * Setting the configuration value failed. 523 */ 524 public static final int CONFIG_RESULT_FAILED = 1; 525 526 /** 527 * @hide 528 */ 529 @Retention(RetentionPolicy.SOURCE) 530 @IntDef(prefix = "CONFIG_RESULT_", value = { 531 CONFIG_RESULT_SUCCESS, 532 CONFIG_RESULT_FAILED 533 }) 534 public @interface SetConfigResult {} 535 536 private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks = 537 new RemoteCallbackListExt<>(); 538 private final RemoteCallbackListExt<IRcsConfigCallback> mRcsCallbacks = 539 new RemoteCallbackListExt<>(); 540 private byte[] mRcsConfigData; 541 ImsConfigStub mImsConfigStub; 542 543 /** 544 * Create a ImsConfig using the Executor specified for methods being called by the 545 * framework. 546 * @param executor The executor for the framework to use when executing the methods overridden 547 * by the implementation of ImsConfig. 548 */ ImsConfigImplBase(@onNull Executor executor)549 public ImsConfigImplBase(@NonNull Executor executor) { 550 mImsConfigStub = new ImsConfigStub(this, executor); 551 } 552 553 /** 554 * @hide 555 */ ImsConfigImplBase(@onNull Context context)556 public ImsConfigImplBase(@NonNull Context context) { 557 mImsConfigStub = new ImsConfigStub(this, null); 558 } 559 ImsConfigImplBase()560 public ImsConfigImplBase() { 561 mImsConfigStub = new ImsConfigStub(this, null); 562 } 563 564 /** 565 * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks 566 * notified when a value in the configuration changes. 567 * @param c callback to add. 568 */ addImsConfigCallback(IImsConfigCallback c)569 private void addImsConfigCallback(IImsConfigCallback c) { 570 mCallbacks.register(c); 571 } 572 /** 573 * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks 574 * notified when a value in the configuration changes. 575 * @param c callback to remove. 576 */ removeImsConfigCallback(IImsConfigCallback c)577 private void removeImsConfigCallback(IImsConfigCallback c) { 578 mCallbacks.unregister(c); 579 } 580 581 /** 582 * @param item 583 * @param value 584 */ notifyConfigChanged(int item, int value)585 private final void notifyConfigChanged(int item, int value) { 586 // can be null in testing 587 if (mCallbacks == null) { 588 return; 589 } 590 synchronized (mCallbacks) { 591 mCallbacks.broadcastAction(c -> { 592 try { 593 c.onIntConfigChanged(item, value); 594 } catch (RemoteException e) { 595 Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping."); 596 } 597 }); 598 } 599 } 600 notifyConfigChanged(int item, String value)601 private void notifyConfigChanged(int item, String value) { 602 // can be null in testing 603 if (mCallbacks == null) { 604 return; 605 } 606 synchronized (mCallbacks) { 607 mCallbacks.broadcastAction(c -> { 608 try { 609 c.onStringConfigChanged(item, value); 610 } catch (RemoteException e) { 611 Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping."); 612 } 613 }); 614 } 615 } 616 addRcsConfigCallback(IRcsConfigCallback c)617 private void addRcsConfigCallback(IRcsConfigCallback c) { 618 mRcsCallbacks.register(c); 619 if (mRcsConfigData != null) { 620 try { 621 c.onConfigurationChanged(mRcsConfigData); 622 } catch (RemoteException e) { 623 Log.w(TAG, "dead binder to call onConfigurationChanged, skipping."); 624 } 625 } 626 } 627 removeRcsConfigCallback(IRcsConfigCallback c)628 private void removeRcsConfigCallback(IRcsConfigCallback c) { 629 mRcsCallbacks.unregister(c); 630 } 631 onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)632 private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { 633 // cache uncompressed config 634 config = isCompressed ? RcsConfig.decompressGzip(config) : config; 635 if (Arrays.equals(mRcsConfigData, config)) { 636 return; 637 } 638 mRcsConfigData = config; 639 640 // can be null in testing 641 if (mRcsCallbacks != null) { 642 synchronized (mRcsCallbacks) { 643 mRcsCallbacks.broadcastAction(c -> { 644 try { 645 c.onConfigurationChanged(mRcsConfigData); 646 } catch (RemoteException e) { 647 Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping."); 648 } 649 }); 650 } 651 } 652 notifyRcsAutoConfigurationReceived(config, isCompressed); 653 } 654 onNotifyRcsAutoConfigurationRemoved()655 private void onNotifyRcsAutoConfigurationRemoved() { 656 mRcsConfigData = null; 657 if (mRcsCallbacks != null) { 658 synchronized (mRcsCallbacks) { 659 mRcsCallbacks.broadcastAction(c -> { 660 try { 661 c.onConfigurationReset(); 662 } catch (RemoteException e) { 663 Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping."); 664 } 665 }); 666 } 667 } 668 notifyRcsAutoConfigurationRemoved(); 669 } 670 671 /** 672 * @hide 673 */ getIImsConfig()674 public IImsConfig getIImsConfig() { return mImsConfigStub; } 675 676 /** 677 * Updates provisioning value and notifies the framework of the change. 678 * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded. 679 * This should only be used when the IMS implementer implicitly changed provisioned values. 680 * 681 * @param item an integer key. 682 * @param value in Integer format. 683 */ notifyProvisionedValueChanged(int item, int value)684 public final void notifyProvisionedValueChanged(int item, int value) { 685 mImsConfigStub.updateCachedValue(item, value); 686 687 try { 688 mImsConfigStub.notifyImsConfigChanged(item, value); 689 } catch (RemoteException e) { 690 Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead."); 691 } 692 } 693 694 /** 695 * Updates provisioning value and notifies the framework of the change. 696 * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded. 697 * This should only be used when the IMS implementer implicitly changed provisioned values. 698 * 699 * @param item an integer key. 700 * @param value in String format. 701 */ notifyProvisionedValueChanged(int item, String value)702 public final void notifyProvisionedValueChanged(int item, String value) { 703 mImsConfigStub.updateCachedValue(item, value); 704 705 try { 706 mImsConfigStub.notifyImsConfigChanged(item, value); 707 } catch (RemoteException e) { 708 Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead."); 709 } 710 } 711 712 /** 713 * The framework has received an RCS autoconfiguration XML file for provisioning. 714 * 715 * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format. 716 * @param isCompressed The XML file is compressed in gzip format and must be decompressed 717 * before being read. 718 * 719 */ notifyRcsAutoConfigurationReceived(@onNull byte[] config, boolean isCompressed)720 public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { 721 } 722 723 /** 724 * The RCS autoconfiguration XML file is removed or invalid. 725 */ notifyRcsAutoConfigurationRemoved()726 public void notifyRcsAutoConfigurationRemoved() { 727 } 728 729 /** 730 * Sets the configuration value for this ImsService. 731 * 732 * @param item an integer key. 733 * @param value an integer containing the configuration value. 734 * @return the result of setting the configuration value. 735 */ setConfig(int item, int value)736 public @SetConfigResult int setConfig(int item, int value) { 737 // Base Implementation - To be overridden. 738 return CONFIG_RESULT_FAILED; 739 } 740 741 /** 742 * Sets the configuration value for this ImsService. 743 * 744 * @param item an integer key. 745 * @param value a String containing the new configuration value. 746 * @return Result of setting the configuration value. 747 */ setConfig(int item, String value)748 public @SetConfigResult int setConfig(int item, String value) { 749 // Base Implementation - To be overridden. 750 return CONFIG_RESULT_FAILED; 751 } 752 753 /** 754 * Gets the currently stored value configuration value from the ImsService for {@code item}. 755 * 756 * @param item an integer key. 757 * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if 758 * unavailable. 759 */ getConfigInt(int item)760 public int getConfigInt(int item) { 761 // Base Implementation - To be overridden. 762 return CONFIG_RESULT_UNKNOWN; 763 } 764 765 /** 766 * Gets the currently stored value configuration value from the ImsService for {@code item}. 767 * 768 * @param item an integer key. 769 * @return configuration value, stored in String format or {@code null} if unavailable. 770 */ getConfigString(int item)771 public String getConfigString(int item) { 772 // Base Implementation - To be overridden. 773 return null; 774 } 775 776 /** 777 * @hide 778 */ updateImsCarrierConfigs(PersistableBundle bundle)779 public void updateImsCarrierConfigs(PersistableBundle bundle) { 780 // Base Implementation - Should be overridden 781 } 782 783 /** 784 * Default messaging application parameters are sent to the ACS client 785 * using this interface. 786 * @param rcc RCS client configuration {@link RcsClientConfiguration} 787 */ setRcsClientConfiguration(@onNull RcsClientConfiguration rcc)788 public void setRcsClientConfiguration(@NonNull RcsClientConfiguration rcc) { 789 // Base Implementation - Should be overridden 790 } 791 792 /** 793 * Reconfiguration triggered by the RCS application. Most likely cause 794 * is the 403 forbidden to a SIP/HTTP request 795 */ triggerAutoConfiguration()796 public void triggerAutoConfiguration() { 797 // Base Implementation - Should be overridden 798 } 799 800 /** 801 * Errors during autoconfiguration connection setup are notified by the 802 * ACS client using this interface. 803 * @param errorCode HTTP error received during connection setup. 804 * @param errorString reason phrase received with the error 805 */ notifyAutoConfigurationErrorReceived(int errorCode, @NonNull String errorString)806 public final void notifyAutoConfigurationErrorReceived(int errorCode, 807 @NonNull String errorString) { 808 // can be null in testing 809 if (mRcsCallbacks == null) { 810 return; 811 } 812 synchronized (mRcsCallbacks) { 813 mRcsCallbacks.broadcastAction(c -> { 814 try { 815 c.onAutoConfigurationErrorReceived(errorCode, errorString); 816 } catch (RemoteException e) { 817 Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping."); 818 } 819 }); 820 } 821 } 822 823 /** 824 * Notifies application that pre-provisioning config is received. 825 * 826 * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific 827 * pre-provisioning configuration XML if the user has not been provisioned for RCS 828 * services yet. When such provisioning XML is received, ACS client must call this 829 * method to notify the application with the XML. 830 * 831 * @param configXml the pre-provisioning config in carrier specified format. 832 */ notifyPreProvisioningReceived(@onNull byte[] configXml)833 public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) { 834 // can be null in testing 835 if (mRcsCallbacks == null) { 836 return; 837 } 838 synchronized (mRcsCallbacks) { 839 mRcsCallbacks.broadcastAction(c -> { 840 try { 841 c.onPreProvisioningReceived(configXml); 842 } catch (RemoteException e) { 843 Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping."); 844 } 845 }); 846 } 847 } 848 849 /** 850 * Set default Executor from ImsService. 851 * @param executor The default executor for the framework to use when executing the methods 852 * overridden by the implementation of ImsConfig. 853 * @hide 854 */ setDefaultExecutor(@onNull Executor executor)855 public final void setDefaultExecutor(@NonNull Executor executor) { 856 if (mImsConfigStub.mExecutor == null) { 857 mImsConfigStub.mExecutor = executor; 858 } 859 } 860 } 861