1 /* 2 * Copyright (C) 2020 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 com.android.services.telephony.rcs; 18 19 import android.annotation.AnyThread; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.os.AsyncResult; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.os.PersistableBundle; 29 import android.telephony.CarrierConfigManager; 30 import android.telephony.SubscriptionManager; 31 import android.util.Log; 32 import android.util.SparseArray; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.telephony.PhoneConfigurationManager; 36 import com.android.internal.telephony.flags.FeatureFlags; 37 import com.android.internal.telephony.metrics.RcsStats; 38 import com.android.internal.util.IndentingPrintWriter; 39 import com.android.phone.ImsStateCallbackController; 40 import com.android.phone.R; 41 42 import java.io.FileDescriptor; 43 import java.io.PrintWriter; 44 45 /** 46 * Singleton service setup to manage RCS related services that the platform provides such as User 47 * Capability Exchange. 48 */ 49 @AnyThread 50 public class TelephonyRcsService { 51 52 private static final String LOG_TAG = "TelephonyRcsService"; 53 54 /** 55 * Used to inject RcsFeatureController and UceController instances for testing. 56 */ 57 @VisibleForTesting 58 public interface FeatureFactory { 59 /** 60 * @return an {@link RcsFeatureController} associated with the slot specified. 61 */ createController(Context context, int slotId, int subId)62 RcsFeatureController createController(Context context, int slotId, int subId); 63 64 /** 65 * @return an instance of {@link UceControllerManager} associated with the slot specified. 66 */ createUceControllerManager(Context context, int slotId, int subId, FeatureFlags featureFlags)67 UceControllerManager createUceControllerManager(Context context, int slotId, int subId, 68 FeatureFlags featureFlags); 69 70 /** 71 * @return an instance of {@link SipTransportController} for the slot and subscription 72 * specified. 73 */ createSipTransportController(Context context, int slotId, int subId)74 SipTransportController createSipTransportController(Context context, int slotId, int subId); 75 } 76 77 private FeatureFactory mFeatureFactory = new FeatureFactory() { 78 @Override 79 public RcsFeatureController createController(Context context, int slotId, int subId) { 80 return new RcsFeatureController(context, slotId, subId); 81 } 82 83 @Override 84 public UceControllerManager createUceControllerManager(Context context, int slotId, 85 int subId, FeatureFlags featureFlags) { 86 return new UceControllerManager(context, slotId, subId, featureFlags); 87 } 88 89 @Override 90 public SipTransportController createSipTransportController(Context context, int slotId, 91 int subId) { 92 return new SipTransportController(context, slotId, subId); 93 } 94 }; 95 96 /** 97 * Used to inject device resource for testing. 98 */ 99 @VisibleForTesting 100 public interface ResourceProxy { 101 /** 102 * @return an whether the device supports User Capability Exchange. 103 */ getDeviceUceEnabled(Context context)104 boolean getDeviceUceEnabled(Context context); 105 } 106 107 private static ResourceProxy sResourceProxy = context -> { 108 return context.getResources().getBoolean( 109 R.bool.config_rcs_user_capability_exchange_enabled); 110 }; 111 112 // Notifies this service that there has been a change in available slots. 113 private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1; 114 115 private final Context mContext; 116 private final Object mLock = new Object(); 117 private final FeatureFlags mFeatureFlags; 118 private int mNumSlots; 119 120 // Maps slot ID -> RcsFeatureController. 121 private SparseArray<RcsFeatureController> mFeatureControllers; 122 // Maps slotId -> associatedSubIds 123 private SparseArray<Integer> mSlotToAssociatedSubIds; 124 125 // Whether the device supports User Capability Exchange 126 private boolean mRcsUceEnabled; 127 128 private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() { 129 @Override 130 public void onReceive(Context context, Intent intent) { 131 if (intent == null) { 132 return; 133 } 134 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { 135 Bundle bundle = intent.getExtras(); 136 if (bundle == null) { 137 return; 138 } 139 int slotId = bundle.getInt(CarrierConfigManager.EXTRA_SLOT_INDEX, 140 SubscriptionManager.INVALID_PHONE_INDEX); 141 int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, 142 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 143 onCarrierConfigChangedForSlot(slotId, subId); 144 } 145 } 146 }; 147 148 private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> { 149 switch (msg.what) { 150 case HANDLER_MSIM_CONFIGURATION_CHANGE: { 151 AsyncResult result = (AsyncResult) msg.obj; 152 Integer numSlots = (Integer) result.result; 153 if (numSlots == null) { 154 Log.w(LOG_TAG, "msim config change with null num slots."); 155 break; 156 } 157 updateFeatureControllerSize(numSlots); 158 break; 159 } 160 default: 161 return false; 162 } 163 return true; 164 }); 165 TelephonyRcsService(Context context, int numSlots, FeatureFlags featureFlags)166 public TelephonyRcsService(Context context, int numSlots, FeatureFlags featureFlags) { 167 mContext = context; 168 mNumSlots = numSlots; 169 mFeatureControllers = new SparseArray<>(numSlots); 170 mSlotToAssociatedSubIds = new SparseArray<>(numSlots); 171 mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext); 172 mFeatureFlags = featureFlags; 173 RcsStats.getInstance().registerUceCallback(); 174 } 175 176 @VisibleForTesting TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy, FeatureFlags featureFlags)177 public TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy, 178 FeatureFlags featureFlags) { 179 mContext = context; 180 mNumSlots = numSlots; 181 mFeatureControllers = new SparseArray<>(numSlots); 182 mSlotToAssociatedSubIds = new SparseArray<>(numSlots); 183 sResourceProxy = resourceProxy; 184 mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext); 185 mFeatureFlags = featureFlags; 186 RcsStats.getInstance().registerUceCallback(); 187 } 188 189 /** 190 * @return the {@link RcsFeatureController} associated with the given slot. 191 */ getFeatureController(int slotId)192 public RcsFeatureController getFeatureController(int slotId) { 193 synchronized (mLock) { 194 return mFeatureControllers.get(slotId); 195 } 196 } 197 198 /** 199 * Called after instance creation to initialize internal structures as well as register for 200 * system callbacks. 201 */ initialize()202 public void initialize() { 203 updateFeatureControllerSize(mNumSlots); 204 205 PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler, 206 HANDLER_MSIM_CONFIGURATION_CHANGE, null); 207 mContext.registerReceiver(mCarrierConfigChangedReceiver, new IntentFilter( 208 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 209 } 210 211 @VisibleForTesting setFeatureFactory(FeatureFactory f)212 public void setFeatureFactory(FeatureFactory f) { 213 mFeatureFactory = f; 214 } 215 216 /** 217 * Update the number of {@link RcsFeatureController}s that are created based on the number of 218 * active slots on the device. 219 */ 220 @VisibleForTesting updateFeatureControllerSize(int newNumSlots)221 public void updateFeatureControllerSize(int newNumSlots) { 222 synchronized (mLock) { 223 int oldNumSlots = mFeatureControllers.size(); 224 if (oldNumSlots == newNumSlots) { 225 return; 226 } 227 Log.i(LOG_TAG, "updateFeatureControllers: oldSlots=" + oldNumSlots + ", newNumSlots=" 228 + newNumSlots); 229 mNumSlots = newNumSlots; 230 if (oldNumSlots < newNumSlots) { 231 for (int i = oldNumSlots; i < newNumSlots; i++) { 232 RcsFeatureController c = constructFeatureController(i); 233 // Do not add feature controllers for inactive subscriptions 234 if (c.hasActiveFeatures()) { 235 mFeatureControllers.put(i, c); 236 // Do not change mSlotToAssociatedSubIds, it will be updated upon carrier 237 // config change. 238 } 239 } 240 } else { 241 for (int i = (oldNumSlots - 1); i > (newNumSlots - 1); i--) { 242 RcsFeatureController c = mFeatureControllers.get(i); 243 if (c != null) { 244 mFeatureControllers.remove(i); 245 mSlotToAssociatedSubIds.remove(i); 246 c.destroy(); 247 } 248 } 249 } 250 } 251 } 252 253 /** 254 * Verifies the subId supplied is the active subId for the slotId specified. 255 * If we have not processed a CARRIER_CONFIG_CHANGED indication for this subscription yet, 256 * either the subscription is not active or we have not finished setting up the feature yet. 257 * @param slotId The slotId we are verifying 258 * @param subId The subId we are verifying 259 * @return true if the subId is the active subId we are tracking for the slotId specified. 260 */ verifyActiveSubId(int slotId, int subId)261 public boolean verifyActiveSubId(int slotId, int subId) { 262 synchronized (mLock) { 263 int currId = mSlotToAssociatedSubIds.get(slotId, 264 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 265 return subId == currId; 266 } 267 } 268 269 /** 270 * ACTION_CARRIER_CONFIG_CHANGED was received by this service for a specific slot. 271 * @param slotId The slotId associated with the event. 272 * @param subId The subId associated with the event. May cause the subId associated with the 273 * RcsFeatureController to change if the subscription itself has changed. 274 */ onCarrierConfigChangedForSlot(int slotId, int subId)275 private void onCarrierConfigChangedForSlot(int slotId, int subId) { 276 synchronized (mLock) { 277 RcsFeatureController f = mFeatureControllers.get(slotId); 278 final int oldSubId = mSlotToAssociatedSubIds.get(slotId, 279 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 280 mSlotToAssociatedSubIds.put(slotId, subId); 281 Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId 282 + ", oldSubId= " + oldSubId + ", subId=" + subId + ", existing feature=" 283 + (f != null)); 284 if (SubscriptionManager.isValidSubscriptionId(subId)) { 285 if (f == null) { 286 // A controller doesn't exist for this slot yet. 287 f = mFeatureFactory.createController(mContext, slotId, subId); 288 updateSupportedFeatures(f, slotId, subId); 289 if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f); 290 } else { 291 updateSupportedFeatures(f, slotId, subId); 292 // Do not keep an empty container around. 293 if (!f.hasActiveFeatures()) { 294 f.destroy(); 295 mFeatureControllers.remove(slotId); 296 } 297 } 298 } 299 if (f != null) { 300 if (oldSubId == subId) { 301 f.onCarrierConfigChangedForSubscription(); 302 } else { 303 f.updateAssociatedSubscription(subId); 304 } 305 } 306 } 307 } 308 constructFeatureController(int slotId)309 private RcsFeatureController constructFeatureController(int slotId) { 310 int subId = getSubscriptionFromSlot(slotId); 311 RcsFeatureController c = mFeatureFactory.createController(mContext, slotId, subId); 312 updateSupportedFeatures(c, slotId, subId); 313 return c; 314 } 315 updateSupportedFeatures(RcsFeatureController c, int slotId, int subId)316 private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) { 317 if (isDeviceUceEnabled() && doesSubscriptionSupportPresence(subId)) { 318 if (c.getFeature(UceControllerManager.class) == null) { 319 c.addFeature(mFeatureFactory.createUceControllerManager( 320 mContext, slotId, subId, mFeatureFlags), UceControllerManager.class); 321 } 322 } else { 323 if (c.getFeature(UceControllerManager.class) != null) { 324 c.removeFeature(UceControllerManager.class); 325 } 326 } 327 328 if (doesSubscriptionSupportSingleRegistration(subId)) { 329 if (c.getFeature(SipTransportController.class) == null) { 330 c.addFeature(mFeatureFactory.createSipTransportController(mContext, slotId, subId), 331 SipTransportController.class); 332 } 333 } else { 334 if (c.getFeature(SipTransportController.class) != null) { 335 c.removeFeature(SipTransportController.class); 336 } 337 } 338 // Only start the connection procedure if we have active features. 339 if (c.hasActiveFeatures()) c.connect(); 340 341 ImsStateCallbackController.getInstance() 342 .notifyExternalRcsStateChanged(slotId, false, c.hasActiveFeatures()); 343 } 344 345 /** 346 * Get whether the device supports RCS User Capability Exchange or not. 347 */ isDeviceUceEnabled()348 public boolean isDeviceUceEnabled() { 349 return mRcsUceEnabled; 350 } 351 352 /** 353 * Set the device supports RCS User Capability Exchange. 354 */ setDeviceUceEnabled(boolean isEnabled)355 public void setDeviceUceEnabled(boolean isEnabled) { 356 mRcsUceEnabled = isEnabled; 357 } 358 doesSubscriptionSupportPresence(int subId)359 private boolean doesSubscriptionSupportPresence(int subId) { 360 if (!SubscriptionManager.isValidSubscriptionId(subId)) return false; 361 boolean supportsUce = getConfig(subId, 362 CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false /*default*/); 363 supportsUce |= getConfig(subId, 364 CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, false /*default*/); 365 return supportsUce; 366 } 367 doesSubscriptionSupportSingleRegistration(int subId)368 private boolean doesSubscriptionSupportSingleRegistration(int subId) { 369 if (!SubscriptionManager.isValidSubscriptionId(subId)) return false; 370 return getConfig(subId, CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, 371 false /*defaultValue*/); 372 } 373 getSubscriptionFromSlot(int slotId)374 private int getSubscriptionFromSlot(int slotId) { 375 return SubscriptionManager.getSubscriptionId(slotId); 376 } 377 378 /** 379 * @return the boolean result corresponding to a boolean {@link CarrierConfigManager} key. 380 */ getConfig(int subId, String key, boolean defaultValue)381 private boolean getConfig(int subId, String key, boolean defaultValue) { 382 CarrierConfigManager c = mContext.getSystemService(CarrierConfigManager.class); 383 if (c == null) return defaultValue; 384 PersistableBundle b = c.getConfigForSubId(subId, key); 385 return b != null ? b.getBoolean(key, defaultValue) : defaultValue; 386 } 387 388 /** 389 * Dump this instance into a readable format for dumpsys usage. 390 */ dump(FileDescriptor fd, PrintWriter printWriter, String[] args)391 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 392 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 393 pw.println("RcsFeatureControllers:"); 394 pw.increaseIndent(); 395 synchronized (mLock) { 396 for (int i = 0; i < mNumSlots; i++) { 397 RcsFeatureController f = mFeatureControllers.get(i); 398 if (f == null) continue; 399 pw.increaseIndent(); 400 f.dump(fd, printWriter, args); 401 pw.decreaseIndent(); 402 } 403 } 404 pw.decreaseIndent(); 405 } 406 } 407