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.annotation.TestApi;
23 import android.content.Context;
24 import android.os.PersistableBundle;
25 import android.os.RemoteException;
26 import android.telephony.ims.ProvisioningManager;
27 import android.telephony.ims.aidl.IImsConfig;
28 import android.telephony.ims.aidl.IImsConfigCallback;
29 import android.util.Log;
30 
31 import com.android.ims.ImsConfig;
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.internal.telephony.util.RemoteCallbackListExt;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import java.lang.ref.WeakReference;
38 import java.util.HashMap;
39 
40 /**
41  * Controls the modification of IMS specific configurations. For more information on the supported
42  * IMS configuration constants, see {@link ImsConfig}.
43  *
44  * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
45  * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
46  * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
47  * during initialization, or times when a lot of configuration parameters are being set/get
48  * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
49  * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
50  * performed every time.
51  * @hide
52  */
53 @SystemApi
54 @TestApi
55 public class ImsConfigImplBase {
56 
57     private static final String TAG = "ImsConfigImplBase";
58 
59     /**
60      * Implements the IImsConfig AIDL interface, which is called by potentially many processes
61      * in order to get/set configuration parameters.
62      *
63      * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
64      * with actual implementations from vendors. This class caches provisioned values from
65      * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
66      * it first checks cache layer. If missed, it will call the vendor implementation of
67      * ImsConfigImplBase API.
68      * and cache the return value if the set succeeds.
69      *
70      * Provides APIs to get/set the IMS service feature/capability/parameters.
71      * The config items include:
72      * 1) Items provisioned by the operator.
73      * 2) Items configured by user. Mainly service feature class.
74      *
75      * @hide
76      */
77     @VisibleForTesting
78     static public class ImsConfigStub extends IImsConfig.Stub {
79         WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
80         private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
81         private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
82 
83         @VisibleForTesting
ImsConfigStub(ImsConfigImplBase imsConfigImplBase)84         public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
85             mImsConfigImplBaseWeakReference =
86                     new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
87         }
88 
89         @Override
addImsConfigCallback(IImsConfigCallback c)90         public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
91             getImsConfigImpl().addImsConfigCallback(c);
92         }
93 
94         @Override
removeImsConfigCallback(IImsConfigCallback c)95         public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
96             getImsConfigImpl().removeImsConfigCallback(c);
97         }
98 
99         /**
100          * Gets the value for ims service/capabilities parameters. It first checks its local cache,
101          * if missed, it will call ImsConfigImplBase.getConfigInt.
102          * Synchronous blocking call.
103          *
104          * @param item integer key
105          * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if
106          * unavailable.
107          */
108         @Override
getConfigInt(int item)109         public synchronized int getConfigInt(int item) throws RemoteException {
110             if (mProvisionedIntValue.containsKey(item)) {
111                 return mProvisionedIntValue.get(item);
112             } else {
113                 int retVal = getImsConfigImpl().getConfigInt(item);
114                 if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
115                     updateCachedValue(item, retVal, false);
116                 }
117                 return retVal;
118             }
119         }
120 
121         /**
122          * Gets the value for ims service/capabilities parameters. It first checks its local cache,
123          * if missed, it will call #ImsConfigImplBase.getConfigString.
124          * Synchronous blocking call.
125          *
126          * @param item integer key
127          * @return value in String format.
128          */
129         @Override
getConfigString(int item)130         public synchronized String getConfigString(int item) throws RemoteException {
131             if (mProvisionedIntValue.containsKey(item)) {
132                 return mProvisionedStringValue.get(item);
133             } else {
134                 String retVal = getImsConfigImpl().getConfigString(item);
135                 if (retVal != null) {
136                     updateCachedValue(item, retVal, false);
137                 }
138                 return retVal;
139             }
140         }
141 
142         /**
143          * Sets the value for IMS service/capabilities parameters by the operator device
144          * management entity. It sets the config item value in the provisioned storage
145          * from which the main value is derived, and write it into local cache.
146          * Synchronous blocking call.
147          *
148          * @param item integer key
149          * @param value in Integer format.
150          * @return the result of setting the configuration value, defined as either
151          * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
152          */
153         @Override
setConfigInt(int item, int value)154         public synchronized int setConfigInt(int item, int value) throws RemoteException {
155             mProvisionedIntValue.remove(item);
156             int retVal = getImsConfigImpl().setConfig(item, value);
157             if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
158                 updateCachedValue(item, value, true);
159             } else {
160                 Log.d(TAG, "Set provision value of " + item +
161                         " to " + value + " failed with error code " + retVal);
162             }
163 
164             return retVal;
165         }
166 
167         /**
168          * Sets the value for IMS service/capabilities parameters by the operator device
169          * management entity. It sets the config item value in the provisioned storage
170          * from which the main value is derived, and write it into local cache.
171          * Synchronous blocking call.
172          *
173          * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
174          * @param value in String format.
175          * @return the result of setting the configuration value, defined as either
176          * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
177          */
178         @Override
setConfigString(int item, String value)179         public synchronized int setConfigString(int item, String value)
180                 throws RemoteException {
181             mProvisionedStringValue.remove(item);
182             int retVal = getImsConfigImpl().setConfig(item, value);
183             if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
184                 updateCachedValue(item, value, true);
185             }
186 
187             return retVal;
188         }
189 
190         @Override
updateImsCarrierConfigs(PersistableBundle bundle)191         public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
192             getImsConfigImpl().updateImsCarrierConfigs(bundle);
193         }
194 
getImsConfigImpl()195         private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
196             ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
197             if (ref == null) {
198                 throw new RemoteException("Fail to get ImsConfigImpl");
199             } else {
200                 return ref;
201             }
202         }
203 
204         @Override
notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)205         public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
206                 throws RemoteException {
207             getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed);
208         }
209 
notifyImsConfigChanged(int item, int value)210         private void notifyImsConfigChanged(int item, int value) throws RemoteException {
211             getImsConfigImpl().notifyConfigChanged(item, value);
212         }
213 
notifyImsConfigChanged(int item, String value)214         private void notifyImsConfigChanged(int item, String value) throws RemoteException {
215             getImsConfigImpl().notifyConfigChanged(item, value);
216         }
217 
updateCachedValue(int item, int value, boolean notifyChange)218         protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
219         throws RemoteException {
220             mProvisionedIntValue.put(item, value);
221             if (notifyChange) {
222                 notifyImsConfigChanged(item, value);
223             }
224         }
225 
updateCachedValue(int item, String value, boolean notifyChange)226         protected synchronized void updateCachedValue(int item, String value,
227                 boolean notifyChange) throws RemoteException {
228             mProvisionedStringValue.put(item, value);
229             if (notifyChange) {
230                 notifyImsConfigChanged(item, value);
231             }
232         }
233     }
234 
235     /**
236      * The configuration requested resulted in an unknown result. This may happen if the
237      * IMS configurations are unavailable.
238      */
239     public static final int CONFIG_RESULT_UNKNOWN = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN;
240 
241     /**
242      * Setting the configuration value completed.
243      */
244     public static final int CONFIG_RESULT_SUCCESS = 0;
245     /**
246      * Setting the configuration value failed.
247      */
248     public static final int CONFIG_RESULT_FAILED =  1;
249 
250     /**
251      * @hide
252      */
253     @Retention(RetentionPolicy.SOURCE)
254     @IntDef(prefix = "CONFIG_RESULT_", value = {
255             CONFIG_RESULT_SUCCESS,
256             CONFIG_RESULT_FAILED
257     })
258     public @interface SetConfigResult {}
259 
260     private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks =
261             new RemoteCallbackListExt<>();
262     ImsConfigStub mImsConfigStub;
263 
264     /**
265      * Used for compatibility between older versions of the ImsService.
266      * @hide
267      */
ImsConfigImplBase(Context context)268     public ImsConfigImplBase(Context context) {
269         mImsConfigStub = new ImsConfigStub(this);
270     }
271 
ImsConfigImplBase()272     public ImsConfigImplBase() {
273         mImsConfigStub = new ImsConfigStub(this);
274     }
275 
276     /**
277      * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
278      * notified when a value in the configuration changes.
279      * @param c callback to add.
280      */
addImsConfigCallback(IImsConfigCallback c)281     private void addImsConfigCallback(IImsConfigCallback c) {
282         mCallbacks.register(c);
283     }
284     /**
285      * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
286      * notified when a value in the configuration changes.
287      * @param c callback to remove.
288      */
removeImsConfigCallback(IImsConfigCallback c)289     private void removeImsConfigCallback(IImsConfigCallback c) {
290         mCallbacks.unregister(c);
291     }
292 
293     /**
294      * @param item
295      * @param value
296      */
notifyConfigChanged(int item, int value)297     private final void notifyConfigChanged(int item, int value) {
298         // can be null in testing
299         if (mCallbacks == null) {
300             return;
301         }
302         mCallbacks.broadcastAction(c -> {
303             try {
304                 c.onIntConfigChanged(item, value);
305             } catch (RemoteException e) {
306                 Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping.");
307             }
308         });
309     }
310 
notifyConfigChanged(int item, String value)311     private void notifyConfigChanged(int item, String value) {
312         // can be null in testing
313         if (mCallbacks == null) {
314             return;
315         }
316         mCallbacks.broadcastAction(c -> {
317             try {
318                 c.onStringConfigChanged(item, value);
319             } catch (RemoteException e) {
320                 Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
321             }
322         });
323     }
324 
325     /**
326      * @hide
327      */
getIImsConfig()328     public IImsConfig getIImsConfig() { return mImsConfigStub; }
329 
330     /**
331      * Updates provisioning value and notifies the framework of the change.
332      * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded.
333      * This should only be used when the IMS implementer implicitly changed provisioned values.
334      *
335      * @param item an integer key.
336      * @param value in Integer format.
337      */
notifyProvisionedValueChanged(int item, int value)338     public final void notifyProvisionedValueChanged(int item, int value) {
339         try {
340             mImsConfigStub.updateCachedValue(item, value, true);
341         } catch (RemoteException e) {
342             Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
343         }
344     }
345 
346     /**
347      * Updates provisioning value and notifies the framework of the change.
348      * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded.
349      * This should only be used when the IMS implementer implicitly changed provisioned values.
350      *
351      * @param item an integer key.
352      * @param value in String format.
353      */
notifyProvisionedValueChanged(int item, String value)354     public final void notifyProvisionedValueChanged(int item, String value) {
355         try {
356         mImsConfigStub.updateCachedValue(item, value, true);
357         } catch (RemoteException e) {
358             Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
359         }
360     }
361 
362     /**
363      * The framework has received an RCS autoconfiguration XML file for provisioning.
364      *
365      * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format.
366      * @param isCompressed The XML file is compressed in gzip format and must be decompressed
367      *         before being read.
368      *
369      */
notifyRcsAutoConfigurationReceived(@onNull byte[] config, boolean isCompressed)370     public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
371     }
372 
373     /**
374      * Sets the configuration value for this ImsService.
375      *
376      * @param item an integer key.
377      * @param value an integer containing the configuration value.
378      * @return the result of setting the configuration value.
379      */
setConfig(int item, int value)380     public @SetConfigResult int setConfig(int item, int value) {
381         // Base Implementation - To be overridden.
382         return CONFIG_RESULT_FAILED;
383     }
384 
385     /**
386      * Sets the configuration value for this ImsService.
387      *
388      * @param item an integer key.
389      * @param value a String containing the new configuration value.
390      * @return Result of setting the configuration value.
391      */
setConfig(int item, String value)392     public @SetConfigResult int setConfig(int item, String value) {
393         // Base Implementation - To be overridden.
394         return CONFIG_RESULT_FAILED;
395     }
396 
397     /**
398      * Gets the currently stored value configuration value from the ImsService for {@code item}.
399      *
400      * @param item an integer key.
401      * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if
402      * unavailable.
403      */
getConfigInt(int item)404     public int getConfigInt(int item) {
405         // Base Implementation - To be overridden.
406         return CONFIG_RESULT_UNKNOWN;
407     }
408 
409     /**
410      * Gets the currently stored value configuration value from the ImsService for {@code item}.
411      *
412      * @param item an integer key.
413      * @return configuration value, stored in String format or {@code null} if unavailable.
414      */
getConfigString(int item)415     public String getConfigString(int item) {
416         // Base Implementation - To be overridden.
417         return null;
418     }
419 
420     /**
421      * @hide
422      */
updateImsCarrierConfigs(PersistableBundle bundle)423     public void updateImsCarrierConfigs(PersistableBundle bundle) {
424         // Base Implementation - Should be overridden
425     }
426 }
427