• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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