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