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