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