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()209 public @PublishState int getUcePublishState() throws ImsException { 210 Future<Integer> future = mExecutorService.submit(() -> { 211 checkUceControllerState(); 212 return mUceController.getUcePublishState(); 213 }); 214 215 try { 216 return future.get(); 217 } catch (ExecutionException | InterruptedException e) { 218 Log.w(LOG_TAG, "getUcePublishState exception: " + e); 219 Throwable cause = e.getCause(); 220 if (cause instanceof ImsException) { 221 throw (ImsException) cause; 222 } 223 return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR; 224 } 225 } 226 227 /** 228 * Add new feature tags to the Set used to calculate the capabilities in PUBLISH. 229 */ addUceRegistrationOverride( Set<String> featureTags)230 public RcsContactUceCapability addUceRegistrationOverride( 231 Set<String> featureTags) throws ImsException { 232 Future<RcsContactUceCapability> future = mExecutorService.submit(() -> { 233 checkUceControllerState(); 234 return mUceController.addRegistrationOverrideCapabilities(featureTags); 235 }); 236 237 try { 238 return future.get(); 239 } catch (ExecutionException | InterruptedException e) { 240 Log.w(LOG_TAG, "addUceRegistrationOverride exception: " + e); 241 Throwable cause = e.getCause(); 242 if (cause instanceof ImsException) { 243 throw (ImsException) cause; 244 } 245 return null; 246 } 247 } 248 249 /** 250 * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH. 251 */ removeUceRegistrationOverride( Set<String> featureTags)252 public RcsContactUceCapability removeUceRegistrationOverride( 253 Set<String> featureTags) throws ImsException { 254 Future<RcsContactUceCapability> future = mExecutorService.submit(() -> { 255 checkUceControllerState(); 256 return mUceController.removeRegistrationOverrideCapabilities(featureTags); 257 }); 258 259 try { 260 return future.get(); 261 } catch (ExecutionException | InterruptedException e) { 262 Log.w(LOG_TAG, "removeUceRegistrationOverride exception: " + e); 263 Throwable cause = e.getCause(); 264 if (cause instanceof ImsException) { 265 throw (ImsException) cause; 266 } 267 return null; 268 } 269 } 270 271 /** 272 * Clear all overrides in the Set used to calculate the capabilities in PUBLISH. 273 */ clearUceRegistrationOverride()274 public RcsContactUceCapability clearUceRegistrationOverride() throws ImsException { 275 Future<RcsContactUceCapability> future = mExecutorService.submit(() -> { 276 checkUceControllerState(); 277 return mUceController.clearRegistrationOverrideCapabilities(); 278 }); 279 280 try { 281 return future.get(); 282 } catch (ExecutionException | InterruptedException e) { 283 Log.w(LOG_TAG, "clearUceRegistrationOverride exception: " + e); 284 Throwable cause = e.getCause(); 285 if (cause instanceof ImsException) { 286 throw (ImsException) cause; 287 } 288 return null; 289 } 290 } 291 292 /** 293 * @return current RcsContactUceCapability instance that will be used for PUBLISH. 294 */ getLatestRcsContactUceCapability()295 public RcsContactUceCapability getLatestRcsContactUceCapability() throws ImsException { 296 Future<RcsContactUceCapability> future = mExecutorService.submit(() -> { 297 checkUceControllerState(); 298 return mUceController.getLatestRcsContactUceCapability(); 299 }); 300 301 try { 302 return future.get(); 303 } catch (ExecutionException | InterruptedException e) { 304 Log.w(LOG_TAG, "getLatestRcsContactUceCapability exception: " + e); 305 Throwable cause = e.getCause(); 306 if (cause instanceof ImsException) { 307 throw (ImsException) cause; 308 } 309 return null; 310 } 311 } 312 313 /** 314 * 315 * @return The last PIDF XML sent to the IMS stack to be published. 316 */ getLastPidfXml()317 public String getLastPidfXml() throws ImsException { 318 Future<String> future = mExecutorService.submit(() -> { 319 checkUceControllerState(); 320 return mUceController.getLastPidfXml(); 321 }); 322 323 try { 324 return future.get(); 325 } catch (ExecutionException | InterruptedException e) { 326 Log.w(LOG_TAG, "getLastPidfXml exception: " + e); 327 Throwable cause = e.getCause(); 328 if (cause instanceof ImsException) { 329 throw (ImsException) cause; 330 } 331 return null; 332 } 333 } 334 335 /** 336 * Remove UCE requests cannot be sent to the network status. 337 * @return true if this command is successful. 338 */ removeUceRequestDisallowedStatus()339 public boolean removeUceRequestDisallowedStatus() throws ImsException { 340 Future<Boolean> future = mExecutorService.submit(() -> { 341 if (mUceController == null) { 342 throw new ImsException("UCE controller is null", 343 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 344 } 345 mUceController.removeRequestDisallowedStatus(); 346 return true; 347 }); 348 349 try { 350 return future.get(); 351 } catch (ExecutionException | InterruptedException e) { 352 Log.w(LOG_TAG, "removeUceRequestDisallowedStatus exception: " + e); 353 Throwable cause = e.getCause(); 354 if (cause instanceof ImsException) { 355 throw (ImsException) cause; 356 } 357 return false; 358 } 359 } 360 361 /** 362 * Set the timeout for contact capabilities request. 363 * @param timeoutAfterMs How long when the capabilities request will time up. 364 * @return true if this command is successful. 365 */ setCapabilitiesRequestTimeout(long timeoutAfterMs)366 public boolean setCapabilitiesRequestTimeout(long timeoutAfterMs) throws ImsException { 367 Future<Boolean> future = mExecutorService.submit(() -> { 368 if (mUceController == null) { 369 throw new ImsException("UCE controller is null", 370 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 371 } 372 mUceController.setCapabilitiesRequestTimeout(timeoutAfterMs); 373 return true; 374 }); 375 376 try { 377 return future.get(); 378 } catch (ExecutionException | InterruptedException e) { 379 Log.w(LOG_TAG, "setCapabilitiesRequestTimeout exception: " + e); 380 Throwable cause = e.getCause(); 381 if (cause instanceof ImsException) { 382 throw (ImsException) cause; 383 } 384 return false; 385 } 386 } 387 388 /** 389 * Register the Publish state changed callback. 390 * 391 * @throws ImsException if the ImsService connected to this controller is currently down. 392 */ registerPublishStateCallback(IRcsUcePublishStateCallback c)393 public void registerPublishStateCallback(IRcsUcePublishStateCallback c) throws ImsException { 394 Future future = mExecutorService.submit(() -> { 395 checkUceControllerState(); 396 mUceController.registerPublishStateCallback(c); 397 return true; 398 }); 399 400 try { 401 future.get(); 402 } catch (ExecutionException | InterruptedException e) { 403 Log.w(LOG_TAG, "registerPublishStateCallback exception: " + e); 404 Throwable cause = e.getCause(); 405 if (cause instanceof ImsException) { 406 throw (ImsException) cause; 407 } 408 } 409 } 410 411 /** 412 * Unregister the existing publish state changed callback. 413 */ unregisterPublishStateCallback(IRcsUcePublishStateCallback c)414 public void unregisterPublishStateCallback(IRcsUcePublishStateCallback c) { 415 Future future = mExecutorService.submit(() -> { 416 if (checkUceControllerState()) { 417 mUceController.unregisterPublishStateCallback(c); 418 } 419 return true; 420 }); 421 422 try { 423 future.get(); 424 } catch (ExecutionException | InterruptedException e) { 425 Log.w(LOG_TAG, "unregisterPublishStateCallback exception: " + e); 426 } 427 } 428 429 /** 430 * Initialize the UceController instance associated with the given subscription ID. 431 * The existing UceController will be destroyed if the original subscription ID is different 432 * from the new subscription ID. 433 * If the new subscription ID is invalid, the UceController instance will be null. 434 */ initUceController(int newSubId)435 private void initUceController(int newSubId) { 436 Log.d(LOG_TAG, "initUceController: newSubId=" + newSubId + ", current UceController subId=" 437 + ((mUceController == null) ? "null" : mUceController.getSubId())); 438 if (mUceController == null) { 439 // Create new UceController only when the subscription ID is valid. 440 if (SubscriptionManager.isValidSubscriptionId(newSubId)) { 441 mUceController = new UceController(mContext, newSubId); 442 } 443 } else if (mUceController.getSubId() != newSubId) { 444 // The subscription ID is updated. Remove the old UceController instance. 445 mUceController.onDestroy(); 446 mUceController = null; 447 // Create new UceController only when the subscription ID is valid. 448 if (SubscriptionManager.isValidSubscriptionId(newSubId)) { 449 mUceController = new UceController(mContext, newSubId); 450 } 451 } 452 } 453 checkUceControllerState()454 private boolean checkUceControllerState() throws ImsException { 455 if (mUceController == null || mUceController.isUnavailable()) { 456 throw new ImsException("UCE controller is unavailable", 457 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 458 } 459 return true; 460 } 461 462 /** 463 * Get the UceController instance. 464 * <p> 465 * Used for testing ONLY. 466 */ 467 @VisibleForTesting getUceController()468 public UceController getUceController() { 469 return mUceController; 470 } 471 472 @Override dump(PrintWriter printWriter)473 public void dump(PrintWriter printWriter) { 474 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 475 pw.println("UceControllerManager" + "[" + mSlotId + "]:"); 476 pw.increaseIndent(); 477 if (mUceController != null) { 478 mUceController.dump(pw); 479 } else { 480 pw.println("UceController is null."); 481 } 482 pw.decreaseIndent(); 483 } 484 } 485