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