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