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