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