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