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