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.Nullable; 20 import android.content.Context; 21 import android.net.Uri; 22 import android.telephony.SubscriptionManager; 23 import android.telephony.ims.ImsException; 24 import android.telephony.ims.RcsContactUceCapability; 25 import android.telephony.ims.RcsUceAdapter; 26 import android.telephony.ims.RcsUceAdapter.PublishState; 27 import android.telephony.ims.aidl.IRcsUceControllerCallback; 28 import android.telephony.ims.aidl.IRcsUcePublishStateCallback; 29 import android.util.IndentingPrintWriter; 30 import android.util.Log; 31 32 import com.android.ims.RcsFeatureManager; 33 import com.android.ims.rcs.uce.UceController; 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.telephony.flags.FeatureFlags; 36 37 import java.io.PrintWriter; 38 import java.util.List; 39 import java.util.Set; 40 import java.util.concurrent.ExecutionException; 41 import java.util.concurrent.ExecutorService; 42 import java.util.concurrent.Executors; 43 import java.util.concurrent.Future; 44 45 /** 46 * Responsible for managing the creation and destruction of UceController. It also received the 47 * requests from {@link com.android.phone.ImsRcsController} and pass these requests to 48 * {@link UceController} 49 */ 50 public class UceControllerManager implements RcsFeatureController.Feature { 51 52 private static final String LOG_TAG = "UceControllerManager"; 53 54 private final int mSlotId; 55 private final Context mContext; 56 private final ExecutorService mExecutorService; 57 private final FeatureFlags mFeatureFlags; 58 59 private volatile @Nullable UceController mUceController; 60 private volatile @Nullable RcsFeatureManager mRcsFeatureManager; 61 UceControllerManager(Context context, int slotId, int subId, FeatureFlags featureFlags)62 public UceControllerManager(Context context, int slotId, int subId, FeatureFlags featureFlags) { 63 Log.d(LOG_TAG, "create: slotId=" + slotId + ", subId=" + subId); 64 mSlotId = slotId; 65 mContext = context; 66 mExecutorService = Executors.newSingleThreadExecutor(); 67 mFeatureFlags = featureFlags; 68 initUceController(subId); 69 } 70 71 /** 72 * Constructor to inject dependencies for testing. 73 */ 74 @VisibleForTesting UceControllerManager(Context context, int slotId, ExecutorService executor, UceController uceController, FeatureFlags featureFlags)75 public UceControllerManager(Context context, int slotId, ExecutorService executor, 76 UceController uceController, FeatureFlags featureFlags) { 77 mSlotId = slotId; 78 mContext = context; 79 mExecutorService = executor; 80 mUceController = uceController; 81 mFeatureFlags = featureFlags; 82 } 83 84 @Override onRcsConnected(RcsFeatureManager manager)85 public void onRcsConnected(RcsFeatureManager manager) { 86 mExecutorService.submit(() -> { 87 mRcsFeatureManager = manager; 88 if (mUceController != null) { 89 mUceController.onRcsConnected(manager); 90 } else { 91 Log.d(LOG_TAG, "onRcsConnected: UceController is null"); 92 } 93 }); 94 } 95 96 @Override onRcsDisconnected()97 public void onRcsDisconnected() { 98 mExecutorService.submit(() -> { 99 mRcsFeatureManager = null; 100 if (mUceController != null) { 101 mUceController.onRcsDisconnected(); 102 } else { 103 Log.d(LOG_TAG, "onRcsDisconnected: UceController is null"); 104 } 105 }); 106 } 107 108 @Override onDestroy()109 public void onDestroy() { 110 mExecutorService.submit(() -> { 111 Log.d(LOG_TAG, "onDestroy"); 112 if (mUceController != null) { 113 mUceController.onDestroy(); 114 } 115 }); 116 // When the shutdown is called, it will refuse any new tasks and let existing tasks finish. 117 mExecutorService.shutdown(); 118 } 119 120 /** 121 * This method will be called when the subscription ID associated with the slot has 122 * changed. 123 */ 124 @Override onAssociatedSubscriptionUpdated(int newSubId)125 public void onAssociatedSubscriptionUpdated(int newSubId) { 126 mExecutorService.submit(() -> { 127 Log.i(LOG_TAG, "onAssociatedSubscriptionUpdated: slotId=" + mSlotId 128 + ", newSubId=" + newSubId); 129 130 // Check and create the UceController with the new updated subscription ID. 131 initUceController(newSubId); 132 133 // The RCS should be connected when the mRcsFeatureManager is not null. Set it to the 134 // new UceController instance. 135 if (mUceController != null && mRcsFeatureManager != null) { 136 mUceController.onRcsConnected(mRcsFeatureManager); 137 } 138 }); 139 } 140 141 /** 142 * This method will be called when the carrier config of the subscription associated with this 143 * manager has changed. 144 */ 145 @Override onCarrierConfigChanged()146 public void onCarrierConfigChanged() { 147 mExecutorService.submit(() -> { 148 Log.i(LOG_TAG, "onCarrierConfigChanged"); 149 if (mUceController != null) { 150 mUceController.onCarrierConfigChanged(); 151 } else { 152 Log.d(LOG_TAG, "onCarrierConfigChanged: UceController is null"); 153 } 154 }); 155 } 156 157 /** 158 * Request the capabilities for contacts. 159 * 160 * @param contactNumbers A list of numbers that the capabilities are being requested for. 161 * @param c A callback for when the request for capabilities completes. 162 * @throws ImsException if the ImsService connected to this controller is currently down. 163 */ requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c)164 public void requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c) 165 throws ImsException { 166 Future future = mExecutorService.submit(() -> { 167 checkUceControllerState(); 168 mUceController.requestCapabilities(contactNumbers, c); 169 return true; 170 }); 171 172 try { 173 future.get(); 174 } catch (ExecutionException | InterruptedException e) { 175 Log.w(LOG_TAG, "requestCapabilities: " + e); 176 Throwable cause = e.getCause(); 177 if (cause instanceof ImsException) { 178 throw (ImsException) cause; 179 } 180 } 181 } 182 183 /** 184 * Request the capabilities for the given contact. 185 * @param contactNumber The contact of the capabilities are being requested for. 186 * @param c A callback for when the request for capabilities completes. 187 * @throws ImsException if the ImsService connected to this controller is currently down. 188 */ requestNetworkAvailability(Uri contactNumber, IRcsUceControllerCallback c)189 public void requestNetworkAvailability(Uri contactNumber, IRcsUceControllerCallback c) 190 throws ImsException { 191 Future future = mExecutorService.submit(() -> { 192 checkUceControllerState(); 193 mUceController.requestAvailability(contactNumber, c); 194 return true; 195 }); 196 197 try { 198 future.get(); 199 } catch (ExecutionException | InterruptedException e) { 200 Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e); 201 Throwable cause = e.getCause(); 202 if (cause instanceof ImsException) { 203 throw (ImsException) cause; 204 } 205 } 206 } 207 208 /** 209 * Get the UCE publish state. 210 * 211 * @throws ImsException if the ImsService connected to this controller is currently down. 212 */ getUcePublishState(boolean isSupportPublishingState)213 public @PublishState int getUcePublishState(boolean isSupportPublishingState) 214 throws ImsException { 215 Future<Integer> future = mExecutorService.submit(() -> { 216 checkUceControllerState(); 217 return mUceController.getUcePublishState(isSupportPublishingState); 218 }); 219 220 try { 221 return future.get(); 222 } catch (ExecutionException | InterruptedException e) { 223 Log.w(LOG_TAG, "getUcePublishState exception: " + e); 224 Throwable cause = e.getCause(); 225 if (cause instanceof ImsException) { 226 throw (ImsException) cause; 227 } 228 return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR; 229 } 230 } 231 232 /** 233 * Add new feature tags to the Set used to calculate the capabilities in PUBLISH. 234 */ addUceRegistrationOverride( Set<String> featureTags)235 public RcsContactUceCapability addUceRegistrationOverride( 236 Set<String> featureTags) throws ImsException { 237 Future<RcsContactUceCapability> future = mExecutorService.submit(() -> { 238 checkUceControllerState(); 239 return mUceController.addRegistrationOverrideCapabilities(featureTags); 240 }); 241 242 try { 243 return future.get(); 244 } catch (ExecutionException | InterruptedException e) { 245 Log.w(LOG_TAG, "addUceRegistrationOverride exception: " + e); 246 Throwable cause = e.getCause(); 247 if (cause instanceof ImsException) { 248 throw (ImsException) cause; 249 } 250 return null; 251 } 252 } 253 254 /** 255 * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH. 256 */ removeUceRegistrationOverride( Set<String> featureTags)257 public RcsContactUceCapability removeUceRegistrationOverride( 258 Set<String> featureTags) throws ImsException { 259 Future<RcsContactUceCapability> future = mExecutorService.submit(() -> { 260 checkUceControllerState(); 261 return mUceController.removeRegistrationOverrideCapabilities(featureTags); 262 }); 263 264 try { 265 return future.get(); 266 } catch (ExecutionException | InterruptedException e) { 267 Log.w(LOG_TAG, "removeUceRegistrationOverride exception: " + e); 268 Throwable cause = e.getCause(); 269 if (cause instanceof ImsException) { 270 throw (ImsException) cause; 271 } 272 return null; 273 } 274 } 275 276 /** 277 * Clear all overrides in the Set used to calculate the capabilities in PUBLISH. 278 */ clearUceRegistrationOverride()279 public RcsContactUceCapability clearUceRegistrationOverride() throws ImsException { 280 Future<RcsContactUceCapability> future = mExecutorService.submit(() -> { 281 checkUceControllerState(); 282 return mUceController.clearRegistrationOverrideCapabilities(); 283 }); 284 285 try { 286 return future.get(); 287 } catch (ExecutionException | InterruptedException e) { 288 Log.w(LOG_TAG, "clearUceRegistrationOverride exception: " + e); 289 Throwable cause = e.getCause(); 290 if (cause instanceof ImsException) { 291 throw (ImsException) cause; 292 } 293 return null; 294 } 295 } 296 297 /** 298 * @return current RcsContactUceCapability instance that will be used for PUBLISH. 299 */ getLatestRcsContactUceCapability()300 public RcsContactUceCapability getLatestRcsContactUceCapability() throws ImsException { 301 Future<RcsContactUceCapability> future = mExecutorService.submit(() -> { 302 checkUceControllerState(); 303 return mUceController.getLatestRcsContactUceCapability(); 304 }); 305 306 try { 307 return future.get(); 308 } catch (ExecutionException | InterruptedException e) { 309 Log.w(LOG_TAG, "getLatestRcsContactUceCapability exception: " + e); 310 Throwable cause = e.getCause(); 311 if (cause instanceof ImsException) { 312 throw (ImsException) cause; 313 } 314 return null; 315 } 316 } 317 318 /** 319 * 320 * @return The last PIDF XML sent to the IMS stack to be published. 321 */ getLastPidfXml()322 public String getLastPidfXml() throws ImsException { 323 Future<String> future = mExecutorService.submit(() -> { 324 checkUceControllerState(); 325 return mUceController.getLastPidfXml(); 326 }); 327 328 try { 329 return future.get(); 330 } catch (ExecutionException | InterruptedException e) { 331 Log.w(LOG_TAG, "getLastPidfXml exception: " + e); 332 Throwable cause = e.getCause(); 333 if (cause instanceof ImsException) { 334 throw (ImsException) cause; 335 } 336 return null; 337 } 338 } 339 340 /** 341 * Remove UCE requests cannot be sent to the network status. 342 * @return true if this command is successful. 343 */ removeUceRequestDisallowedStatus()344 public boolean removeUceRequestDisallowedStatus() throws ImsException { 345 Future<Boolean> future = mExecutorService.submit(() -> { 346 if (mUceController == null) { 347 throw new ImsException("UCE controller is null", 348 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 349 } 350 mUceController.removeRequestDisallowedStatus(); 351 return true; 352 }); 353 354 try { 355 return future.get(); 356 } catch (ExecutionException | InterruptedException e) { 357 Log.w(LOG_TAG, "removeUceRequestDisallowedStatus exception: " + e); 358 Throwable cause = e.getCause(); 359 if (cause instanceof ImsException) { 360 throw (ImsException) cause; 361 } 362 return false; 363 } 364 } 365 366 /** 367 * Set the timeout for contact capabilities request. 368 * @param timeoutAfterMs How long when the capabilities request will time up. 369 * @return true if this command is successful. 370 */ setCapabilitiesRequestTimeout(long timeoutAfterMs)371 public boolean setCapabilitiesRequestTimeout(long timeoutAfterMs) throws ImsException { 372 Future<Boolean> future = mExecutorService.submit(() -> { 373 if (mUceController == null) { 374 throw new ImsException("UCE controller is null", 375 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 376 } 377 mUceController.setCapabilitiesRequestTimeout(timeoutAfterMs); 378 return true; 379 }); 380 381 try { 382 return future.get(); 383 } catch (ExecutionException | InterruptedException e) { 384 Log.w(LOG_TAG, "setCapabilitiesRequestTimeout exception: " + e); 385 Throwable cause = e.getCause(); 386 if (cause instanceof ImsException) { 387 throw (ImsException) cause; 388 } 389 return false; 390 } 391 } 392 393 /** 394 * Register the Publish state changed callback. 395 * 396 * @throws ImsException if the ImsService connected to this controller is currently down. 397 */ registerPublishStateCallback(IRcsUcePublishStateCallback c, boolean supportPublishingState)398 public void registerPublishStateCallback(IRcsUcePublishStateCallback c, 399 boolean supportPublishingState) throws ImsException { 400 Future future = mExecutorService.submit(() -> { 401 checkUceControllerState(); 402 mUceController.registerPublishStateCallback(c, supportPublishingState); 403 return true; 404 }); 405 406 try { 407 future.get(); 408 } catch (ExecutionException | InterruptedException e) { 409 Log.w(LOG_TAG, "registerPublishStateCallback exception: " + e); 410 Throwable cause = e.getCause(); 411 if (cause instanceof ImsException) { 412 throw (ImsException) cause; 413 } 414 } 415 } 416 417 /** 418 * Unregister the existing publish state changed callback. 419 */ unregisterPublishStateCallback(IRcsUcePublishStateCallback c)420 public void unregisterPublishStateCallback(IRcsUcePublishStateCallback c) { 421 Future future = mExecutorService.submit(() -> { 422 if (checkUceControllerState()) { 423 mUceController.unregisterPublishStateCallback(c); 424 } 425 return true; 426 }); 427 428 try { 429 future.get(); 430 } catch (ExecutionException | InterruptedException e) { 431 Log.w(LOG_TAG, "unregisterPublishStateCallback exception: " + e); 432 } 433 } 434 435 /** 436 * Initialize the UceController instance associated with the given subscription ID. 437 * The existing UceController will be destroyed if the original subscription ID is different 438 * from the new subscription ID. 439 * If the new subscription ID is invalid, the UceController instance will be null. 440 */ initUceController(int newSubId)441 private void initUceController(int newSubId) { 442 Log.d(LOG_TAG, "initUceController: newSubId=" + newSubId + ", current UceController subId=" 443 + ((mUceController == null) ? "null" : mUceController.getSubId())); 444 if (mUceController == null) { 445 // Create new UceController only when the subscription ID is valid. 446 if (SubscriptionManager.isValidSubscriptionId(newSubId)) { 447 mUceController = new UceController(mContext, newSubId, mFeatureFlags); 448 } 449 } else if (mUceController.getSubId() != newSubId) { 450 // The subscription ID is updated. Remove the old UceController instance. 451 mUceController.onDestroy(); 452 mUceController = null; 453 // Create new UceController only when the subscription ID is valid. 454 if (SubscriptionManager.isValidSubscriptionId(newSubId)) { 455 mUceController = new UceController(mContext, newSubId, mFeatureFlags); 456 } 457 } 458 } 459 checkUceControllerState()460 private boolean checkUceControllerState() throws ImsException { 461 if (mUceController == null || mUceController.isUnavailable()) { 462 throw new ImsException("UCE controller is unavailable", 463 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 464 } 465 return true; 466 } 467 468 /** 469 * Get the UceController instance. 470 * <p> 471 * Used for testing ONLY. 472 */ 473 @VisibleForTesting getUceController()474 public UceController getUceController() { 475 return mUceController; 476 } 477 478 @Override dump(PrintWriter printWriter)479 public void dump(PrintWriter printWriter) { 480 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 481 pw.println("UceControllerManager" + "[" + mSlotId + "]:"); 482 pw.increaseIndent(); 483 if (mUceController != null) { 484 mUceController.dump(pw); 485 } else { 486 pw.println("UceController is null."); 487 } 488 pw.decreaseIndent(); 489 } 490 } 491