1 /* 2 * Copyright (C) 2017 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.server.backup; 18 19 import android.annotation.Nullable; 20 import android.annotation.UserIdInt; 21 import android.annotation.WorkerThread; 22 import android.app.backup.BackupManager; 23 import android.app.backup.BackupTransport; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ResolveInfo; 31 import android.os.Bundle; 32 import android.os.RemoteException; 33 import android.util.ArrayMap; 34 import android.util.ArraySet; 35 import android.util.Slog; 36 37 import com.android.internal.annotations.GuardedBy; 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.internal.backup.IBackupTransport; 40 import com.android.internal.util.Preconditions; 41 import com.android.server.backup.transport.OnTransportRegisteredListener; 42 import com.android.server.backup.transport.TransportClient; 43 import com.android.server.backup.transport.TransportClientManager; 44 import com.android.server.backup.transport.TransportConnectionListener; 45 import com.android.server.backup.transport.TransportNotAvailableException; 46 import com.android.server.backup.transport.TransportNotRegisteredException; 47 import com.android.server.backup.transport.TransportStats; 48 49 import java.io.PrintWriter; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Set; 53 import java.util.function.Consumer; 54 import java.util.function.Predicate; 55 56 /** Handles in-memory bookkeeping of all BackupTransport objects. */ 57 public class TransportManager { 58 private static final String TAG = "BackupTransportManager"; 59 60 @VisibleForTesting 61 public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST"; 62 63 private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST); 64 private final @UserIdInt int mUserId; 65 private final PackageManager mPackageManager; 66 private final Set<ComponentName> mTransportWhitelist; 67 private final TransportClientManager mTransportClientManager; 68 private final TransportStats mTransportStats; 69 private OnTransportRegisteredListener mOnTransportRegisteredListener = (c, n) -> {}; 70 71 /** 72 * Lock for registered transports and currently selected transport. 73 * 74 * <p><b>Warning:</b> No calls to {@link IBackupTransport} or calls that result in transport 75 * code being executed such as {@link TransportClient#connect(String)}} and its variants should 76 * be made with this lock held, risk of deadlock. 77 */ 78 private final Object mTransportLock = new Object(); 79 80 /** @see #getRegisteredTransportNames() */ 81 @GuardedBy("mTransportLock") 82 private final Map<ComponentName, TransportDescription> mRegisteredTransportsDescriptionMap = 83 new ArrayMap<>(); 84 85 @GuardedBy("mTransportLock") 86 @Nullable 87 private volatile String mCurrentTransportName; 88 TransportManager(@serIdInt int userId, Context context, Set<ComponentName> whitelist, String selectedTransport)89 TransportManager(@UserIdInt int userId, Context context, Set<ComponentName> whitelist, 90 String selectedTransport) { 91 mUserId = userId; 92 mPackageManager = context.getPackageManager(); 93 mTransportWhitelist = Preconditions.checkNotNull(whitelist); 94 mCurrentTransportName = selectedTransport; 95 mTransportStats = new TransportStats(); 96 mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats); 97 } 98 99 @VisibleForTesting TransportManager( @serIdInt int userId, Context context, Set<ComponentName> whitelist, String selectedTransport, TransportClientManager transportClientManager)100 TransportManager( 101 @UserIdInt int userId, 102 Context context, 103 Set<ComponentName> whitelist, 104 String selectedTransport, 105 TransportClientManager transportClientManager) { 106 mUserId = userId; 107 mPackageManager = context.getPackageManager(); 108 mTransportWhitelist = Preconditions.checkNotNull(whitelist); 109 mCurrentTransportName = selectedTransport; 110 mTransportStats = new TransportStats(); 111 mTransportClientManager = transportClientManager; 112 } 113 114 /* Sets a listener to be called whenever a transport is registered. */ setOnTransportRegisteredListener(OnTransportRegisteredListener listener)115 public void setOnTransportRegisteredListener(OnTransportRegisteredListener listener) { 116 mOnTransportRegisteredListener = listener; 117 } 118 119 @WorkerThread onPackageAdded(String packageName)120 void onPackageAdded(String packageName) { 121 registerTransportsFromPackage(packageName, transportComponent -> true); 122 } 123 onPackageRemoved(String packageName)124 void onPackageRemoved(String packageName) { 125 synchronized (mTransportLock) { 126 mRegisteredTransportsDescriptionMap.keySet().removeIf(fromPackageFilter(packageName)); 127 } 128 } 129 130 @WorkerThread onPackageChanged(String packageName, String... components)131 void onPackageChanged(String packageName, String... components) { 132 // Unfortunately this can't be atomic because we risk a deadlock if 133 // registerTransportsFromPackage() is put inside the synchronized block 134 Set<ComponentName> transportComponents = new ArraySet<>(components.length); 135 for (String componentName : components) { 136 transportComponents.add(new ComponentName(packageName, componentName)); 137 } 138 synchronized (mTransportLock) { 139 mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains); 140 } 141 registerTransportsFromPackage(packageName, transportComponents::contains); 142 } 143 144 /** 145 * Returns the {@link ComponentName}s of the registered transports. 146 * 147 * <p>A *registered* transport is a transport that satisfies intent with action 148 * android.backup.TRANSPORT_HOST, returns true for {@link #isTransportTrusted(ComponentName)} 149 * and that we have successfully connected to once. 150 */ getRegisteredTransportComponents()151 ComponentName[] getRegisteredTransportComponents() { 152 synchronized (mTransportLock) { 153 return mRegisteredTransportsDescriptionMap 154 .keySet() 155 .toArray(new ComponentName[mRegisteredTransportsDescriptionMap.size()]); 156 } 157 } 158 159 /** 160 * Returns the names of the registered transports. 161 * 162 * @see #getRegisteredTransportComponents() 163 */ getRegisteredTransportNames()164 String[] getRegisteredTransportNames() { 165 synchronized (mTransportLock) { 166 String[] transportNames = new String[mRegisteredTransportsDescriptionMap.size()]; 167 int i = 0; 168 for (TransportDescription description : mRegisteredTransportsDescriptionMap.values()) { 169 transportNames[i] = description.name; 170 i++; 171 } 172 return transportNames; 173 } 174 } 175 176 /** Returns a set with the whitelisted transports. */ getTransportWhitelist()177 Set<ComponentName> getTransportWhitelist() { 178 return mTransportWhitelist; 179 } 180 181 /** Returns the name of the selected transport or {@code null} if no transport selected. */ 182 @Nullable getCurrentTransportName()183 public String getCurrentTransportName() { 184 return mCurrentTransportName; 185 } 186 187 /** 188 * Returns the {@link ComponentName} of the host service of the selected transport or 189 * {@code null} if no transport selected. 190 * 191 * @throws TransportNotRegisteredException if the selected transport is not registered. 192 */ 193 @Nullable getCurrentTransportComponent()194 public ComponentName getCurrentTransportComponent() 195 throws TransportNotRegisteredException { 196 synchronized (mTransportLock) { 197 if (mCurrentTransportName == null) { 198 return null; 199 } 200 return getRegisteredTransportComponentOrThrowLocked(mCurrentTransportName); 201 } 202 } 203 204 /** 205 * Returns the transport name associated with {@code transportComponent}. 206 * 207 * @throws TransportNotRegisteredException if the transport is not registered. 208 */ getTransportName(ComponentName transportComponent)209 public String getTransportName(ComponentName transportComponent) 210 throws TransportNotRegisteredException { 211 synchronized (mTransportLock) { 212 return getRegisteredTransportDescriptionOrThrowLocked(transportComponent).name; 213 } 214 } 215 216 /** 217 * Retrieves the transport dir name of {@code transportComponent}. 218 * 219 * @throws TransportNotRegisteredException if the transport is not registered. 220 */ getTransportDirName(ComponentName transportComponent)221 public String getTransportDirName(ComponentName transportComponent) 222 throws TransportNotRegisteredException { 223 synchronized (mTransportLock) { 224 return getRegisteredTransportDescriptionOrThrowLocked(transportComponent) 225 .transportDirName; 226 } 227 } 228 229 /** 230 * Retrieves the transport dir name of {@code transportName}. 231 * 232 * @throws TransportNotRegisteredException if the transport is not registered. 233 */ getTransportDirName(String transportName)234 public String getTransportDirName(String transportName) throws TransportNotRegisteredException { 235 synchronized (mTransportLock) { 236 return getRegisteredTransportDescriptionOrThrowLocked(transportName).transportDirName; 237 } 238 } 239 240 /** 241 * Retrieves the configuration intent of {@code transportName}. 242 * 243 * @throws TransportNotRegisteredException if the transport is not registered. 244 */ 245 @Nullable getTransportConfigurationIntent(String transportName)246 public Intent getTransportConfigurationIntent(String transportName) 247 throws TransportNotRegisteredException { 248 synchronized (mTransportLock) { 249 return getRegisteredTransportDescriptionOrThrowLocked(transportName) 250 .configurationIntent; 251 } 252 } 253 254 /** 255 * Retrieves the current destination string of {@code transportName}. 256 * 257 * @throws TransportNotRegisteredException if the transport is not registered. 258 */ getTransportCurrentDestinationString(String transportName)259 public String getTransportCurrentDestinationString(String transportName) 260 throws TransportNotRegisteredException { 261 synchronized (mTransportLock) { 262 return getRegisteredTransportDescriptionOrThrowLocked(transportName) 263 .currentDestinationString; 264 } 265 } 266 267 /** 268 * Retrieves the data management intent of {@code transportName}. 269 * 270 * @throws TransportNotRegisteredException if the transport is not registered. 271 */ 272 @Nullable getTransportDataManagementIntent(String transportName)273 public Intent getTransportDataManagementIntent(String transportName) 274 throws TransportNotRegisteredException { 275 synchronized (mTransportLock) { 276 return getRegisteredTransportDescriptionOrThrowLocked(transportName) 277 .dataManagementIntent; 278 } 279 } 280 281 /** 282 * Retrieves the data management label of {@code transportName}. 283 * 284 * @throws TransportNotRegisteredException if the transport is not registered. 285 */ 286 @Nullable getTransportDataManagementLabel(String transportName)287 public CharSequence getTransportDataManagementLabel(String transportName) 288 throws TransportNotRegisteredException { 289 synchronized (mTransportLock) { 290 return getRegisteredTransportDescriptionOrThrowLocked(transportName) 291 .dataManagementLabel; 292 } 293 } 294 295 /* Returns true if the transport identified by {@code transportName} is registered. */ isTransportRegistered(String transportName)296 public boolean isTransportRegistered(String transportName) { 297 synchronized (mTransportLock) { 298 return getRegisteredTransportEntryLocked(transportName) != null; 299 } 300 } 301 302 /** 303 * Execute {@code transportConsumer} for each registered transport passing the transport name. 304 * This is called with an internal lock held, ensuring that the transport will remain registered 305 * while {@code transportConsumer} is being executed. Don't do heavy operations in {@code 306 * transportConsumer}. 307 * 308 * <p><b>Warning:</b> Do NOT make any calls to {@link IBackupTransport} or call any variants of 309 * {@link TransportClient#connect(String)} here, otherwise you risk deadlock. 310 */ forEachRegisteredTransport(Consumer<String> transportConsumer)311 public void forEachRegisteredTransport(Consumer<String> transportConsumer) { 312 synchronized (mTransportLock) { 313 for (TransportDescription transportDescription : 314 mRegisteredTransportsDescriptionMap.values()) { 315 transportConsumer.accept(transportDescription.name); 316 } 317 } 318 } 319 320 /** 321 * Updates given values for the transport already registered and identified with {@param 322 * transportComponent}. If the transport is not registered it will log and return. 323 */ updateTransportAttributes( ComponentName transportComponent, String name, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, @Nullable CharSequence dataManagementLabel)324 public void updateTransportAttributes( 325 ComponentName transportComponent, 326 String name, 327 @Nullable Intent configurationIntent, 328 String currentDestinationString, 329 @Nullable Intent dataManagementIntent, 330 @Nullable CharSequence dataManagementLabel) { 331 synchronized (mTransportLock) { 332 TransportDescription description = 333 mRegisteredTransportsDescriptionMap.get(transportComponent); 334 if (description == null) { 335 Slog.e(TAG, "Transport " + name + " not registered tried to change description"); 336 return; 337 } 338 description.name = name; 339 description.configurationIntent = configurationIntent; 340 description.currentDestinationString = currentDestinationString; 341 description.dataManagementIntent = dataManagementIntent; 342 description.dataManagementLabel = dataManagementLabel; 343 Slog.d(TAG, "Transport " + name + " updated its attributes"); 344 } 345 } 346 347 @GuardedBy("mTransportLock") getRegisteredTransportComponentOrThrowLocked(String transportName)348 private ComponentName getRegisteredTransportComponentOrThrowLocked(String transportName) 349 throws TransportNotRegisteredException { 350 ComponentName transportComponent = getRegisteredTransportComponentLocked(transportName); 351 if (transportComponent == null) { 352 throw new TransportNotRegisteredException(transportName); 353 } 354 return transportComponent; 355 } 356 357 @GuardedBy("mTransportLock") getRegisteredTransportDescriptionOrThrowLocked( ComponentName transportComponent)358 private TransportDescription getRegisteredTransportDescriptionOrThrowLocked( 359 ComponentName transportComponent) throws TransportNotRegisteredException { 360 TransportDescription description = 361 mRegisteredTransportsDescriptionMap.get(transportComponent); 362 if (description == null) { 363 throw new TransportNotRegisteredException(transportComponent); 364 } 365 return description; 366 } 367 368 @GuardedBy("mTransportLock") getRegisteredTransportDescriptionOrThrowLocked( String transportName)369 private TransportDescription getRegisteredTransportDescriptionOrThrowLocked( 370 String transportName) throws TransportNotRegisteredException { 371 TransportDescription description = getRegisteredTransportDescriptionLocked(transportName); 372 if (description == null) { 373 throw new TransportNotRegisteredException(transportName); 374 } 375 return description; 376 } 377 378 @GuardedBy("mTransportLock") 379 @Nullable getRegisteredTransportComponentLocked(String transportName)380 private ComponentName getRegisteredTransportComponentLocked(String transportName) { 381 Map.Entry<ComponentName, TransportDescription> entry = 382 getRegisteredTransportEntryLocked(transportName); 383 return (entry == null) ? null : entry.getKey(); 384 } 385 386 @GuardedBy("mTransportLock") 387 @Nullable getRegisteredTransportDescriptionLocked(String transportName)388 private TransportDescription getRegisteredTransportDescriptionLocked(String transportName) { 389 Map.Entry<ComponentName, TransportDescription> entry = 390 getRegisteredTransportEntryLocked(transportName); 391 return (entry == null) ? null : entry.getValue(); 392 } 393 394 @GuardedBy("mTransportLock") 395 @Nullable getRegisteredTransportEntryLocked( String transportName)396 private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked( 397 String transportName) { 398 for (Map.Entry<ComponentName, TransportDescription> entry : 399 mRegisteredTransportsDescriptionMap.entrySet()) { 400 TransportDescription description = entry.getValue(); 401 if (transportName.equals(description.name)) { 402 return entry; 403 } 404 } 405 return null; 406 } 407 408 /** 409 * Returns a {@link TransportClient} for {@code transportName} or {@code null} if not 410 * registered. 411 * 412 * @param transportName The name of the transport. 413 * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check 414 * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more 415 * details. 416 * @return A {@link TransportClient} or null if not registered. 417 */ 418 @Nullable getTransportClient(String transportName, String caller)419 public TransportClient getTransportClient(String transportName, String caller) { 420 try { 421 return getTransportClientOrThrow(transportName, caller); 422 } catch (TransportNotRegisteredException e) { 423 Slog.w(TAG, "Transport " + transportName + " not registered"); 424 return null; 425 } 426 } 427 428 /** 429 * Returns a {@link TransportClient} for {@code transportName} or throws if not registered. 430 * 431 * @param transportName The name of the transport. 432 * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check 433 * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more 434 * details. 435 * @return A {@link TransportClient}. 436 * @throws TransportNotRegisteredException if the transport is not registered. 437 */ getTransportClientOrThrow(String transportName, String caller)438 public TransportClient getTransportClientOrThrow(String transportName, String caller) 439 throws TransportNotRegisteredException { 440 synchronized (mTransportLock) { 441 ComponentName component = getRegisteredTransportComponentLocked(transportName); 442 if (component == null) { 443 throw new TransportNotRegisteredException(transportName); 444 } 445 return mTransportClientManager.getTransportClient(component, caller); 446 } 447 } 448 449 /** 450 * Returns a {@link TransportClient} for the current transport or {@code null} if not 451 * registered. 452 * 453 * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check 454 * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more 455 * details. 456 * @return A {@link TransportClient} or null if not registered. 457 * @throws IllegalStateException if no transport is selected. 458 */ 459 @Nullable getCurrentTransportClient(String caller)460 public TransportClient getCurrentTransportClient(String caller) { 461 if (mCurrentTransportName == null) { 462 throw new IllegalStateException("No transport selected"); 463 } 464 synchronized (mTransportLock) { 465 return getTransportClient(mCurrentTransportName, caller); 466 } 467 } 468 469 /** 470 * Returns a {@link TransportClient} for the current transport or throws if not registered. 471 * 472 * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check 473 * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more 474 * details. 475 * @return A {@link TransportClient}. 476 * @throws TransportNotRegisteredException if the transport is not registered. 477 * @throws IllegalStateException if no transport is selected. 478 */ getCurrentTransportClientOrThrow(String caller)479 public TransportClient getCurrentTransportClientOrThrow(String caller) 480 throws TransportNotRegisteredException { 481 if (mCurrentTransportName == null) { 482 throw new IllegalStateException("No transport selected"); 483 } 484 synchronized (mTransportLock) { 485 return getTransportClientOrThrow(mCurrentTransportName, caller); 486 } 487 } 488 489 /** 490 * Disposes of the {@link TransportClient}. 491 * 492 * @param transportClient The {@link TransportClient} to be disposed of. 493 * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check 494 * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more 495 * details. 496 */ disposeOfTransportClient(TransportClient transportClient, String caller)497 public void disposeOfTransportClient(TransportClient transportClient, String caller) { 498 mTransportClientManager.disposeOfTransportClient(transportClient, caller); 499 } 500 501 /** 502 * Sets {@code transportName} as selected transport and returns previously selected transport 503 * name. If there was no previous transport it returns null. 504 * 505 * <p>You should NOT call this method in new code. This won't make any checks against {@code 506 * transportName}, putting any operation at risk of a {@link TransportNotRegisteredException} or 507 * another error at the time it's being executed. 508 * 509 * <p>{@link Deprecated} as public, this method can be used as private. 510 */ 511 @Deprecated 512 @Nullable selectTransport(String transportName)513 String selectTransport(String transportName) { 514 synchronized (mTransportLock) { 515 String prevTransport = mCurrentTransportName; 516 mCurrentTransportName = transportName; 517 return prevTransport; 518 } 519 } 520 521 /** 522 * Tries to register the transport if not registered. If successful also selects the transport. 523 * 524 * @param transportComponent Host of the transport. 525 * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} 526 * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. 527 */ 528 @WorkerThread registerAndSelectTransport(ComponentName transportComponent)529 public int registerAndSelectTransport(ComponentName transportComponent) { 530 // If it's already registered we select and return 531 synchronized (mTransportLock) { 532 try { 533 selectTransport(getTransportName(transportComponent)); 534 return BackupManager.SUCCESS; 535 } catch (TransportNotRegisteredException e) { 536 // Fall through and release lock 537 } 538 } 539 540 // We can't call registerTransport() with the transport lock held 541 int result = registerTransport(transportComponent); 542 if (result != BackupManager.SUCCESS) { 543 return result; 544 } 545 synchronized (mTransportLock) { 546 try { 547 selectTransport(getTransportName(transportComponent)); 548 return BackupManager.SUCCESS; 549 } catch (TransportNotRegisteredException e) { 550 Slog.wtf(TAG, "Transport got unregistered"); 551 return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; 552 } 553 } 554 } 555 556 @WorkerThread registerTransports()557 public void registerTransports() { 558 registerTransportsForIntent(mTransportServiceIntent, transportComponent -> true); 559 } 560 561 @WorkerThread registerTransportsFromPackage( String packageName, Predicate<ComponentName> transportComponentFilter)562 private void registerTransportsFromPackage( 563 String packageName, Predicate<ComponentName> transportComponentFilter) { 564 try { 565 mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId); 566 } catch (PackageManager.NameNotFoundException e) { 567 Slog.e(TAG, "Trying to register transports from package not found " + packageName); 568 return; 569 } 570 571 registerTransportsForIntent( 572 new Intent(mTransportServiceIntent).setPackage(packageName), 573 transportComponentFilter.and(fromPackageFilter(packageName))); 574 } 575 576 @WorkerThread registerTransportsForIntent( Intent intent, Predicate<ComponentName> transportComponentFilter)577 private void registerTransportsForIntent( 578 Intent intent, Predicate<ComponentName> transportComponentFilter) { 579 List<ResolveInfo> hosts = 580 mPackageManager.queryIntentServicesAsUser(intent, 0, mUserId); 581 if (hosts == null) { 582 return; 583 } 584 for (ResolveInfo host : hosts) { 585 ComponentName transportComponent = host.serviceInfo.getComponentName(); 586 if (transportComponentFilter.test(transportComponent) 587 && isTransportTrusted(transportComponent)) { 588 registerTransport(transportComponent); 589 } 590 } 591 } 592 593 /** Transport has to be whitelisted and privileged. */ isTransportTrusted(ComponentName transport)594 private boolean isTransportTrusted(ComponentName transport) { 595 if (!mTransportWhitelist.contains(transport)) { 596 Slog.w( 597 TAG, 598 "BackupTransport " + transport.flattenToShortString() + " not whitelisted."); 599 return false; 600 } 601 try { 602 PackageInfo packInfo = 603 mPackageManager.getPackageInfoAsUser(transport.getPackageName(), 0, mUserId); 604 if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) 605 == 0) { 606 Slog.w(TAG, "Transport package " + transport.getPackageName() + " not privileged"); 607 return false; 608 } 609 } catch (PackageManager.NameNotFoundException e) { 610 Slog.w(TAG, "Package not found.", e); 611 return false; 612 } 613 return true; 614 } 615 616 /** 617 * Tries to register transport represented by {@code transportComponent}. 618 * 619 * <p><b>Warning:</b> Don't call this with the transport lock held. 620 * 621 * @param transportComponent Host of the transport that we want to register. 622 * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} 623 * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. 624 */ 625 @WorkerThread registerTransport(ComponentName transportComponent)626 private int registerTransport(ComponentName transportComponent) { 627 checkCanUseTransport(); 628 629 if (!isTransportTrusted(transportComponent)) { 630 return BackupManager.ERROR_TRANSPORT_INVALID; 631 } 632 633 String transportString = transportComponent.flattenToShortString(); 634 String callerLogString = "TransportManager.registerTransport()"; 635 636 Bundle extras = new Bundle(); 637 extras.putBoolean(BackupTransport.EXTRA_TRANSPORT_REGISTRATION, true); 638 639 TransportClient transportClient = 640 mTransportClientManager.getTransportClient( 641 transportComponent, extras, callerLogString); 642 final IBackupTransport transport; 643 try { 644 transport = transportClient.connectOrThrow(callerLogString); 645 } catch (TransportNotAvailableException e) { 646 Slog.e(TAG, "Couldn't connect to transport " + transportString + " for registration"); 647 mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); 648 return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; 649 } 650 651 int result; 652 try { 653 String transportName = transport.name(); 654 String transportDirName = transport.transportDirName(); 655 registerTransport(transportComponent, transport); 656 // If registerTransport() hasn't thrown... 657 Slog.d(TAG, "Transport " + transportString + " registered"); 658 mOnTransportRegisteredListener.onTransportRegistered(transportName, transportDirName); 659 result = BackupManager.SUCCESS; 660 } catch (RemoteException e) { 661 Slog.e(TAG, "Transport " + transportString + " died while registering"); 662 result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE; 663 } 664 665 mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); 666 return result; 667 } 668 669 /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */ registerTransport(ComponentName transportComponent, IBackupTransport transport)670 private void registerTransport(ComponentName transportComponent, IBackupTransport transport) 671 throws RemoteException { 672 checkCanUseTransport(); 673 674 TransportDescription description = 675 new TransportDescription( 676 transport.name(), 677 transport.transportDirName(), 678 transport.configurationIntent(), 679 transport.currentDestinationString(), 680 transport.dataManagementIntent(), 681 transport.dataManagementIntentLabel()); 682 synchronized (mTransportLock) { 683 mRegisteredTransportsDescriptionMap.put(transportComponent, description); 684 } 685 } 686 checkCanUseTransport()687 private void checkCanUseTransport() { 688 Preconditions.checkState( 689 !Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held"); 690 } 691 dumpTransportClients(PrintWriter pw)692 public void dumpTransportClients(PrintWriter pw) { 693 mTransportClientManager.dump(pw); 694 } 695 dumpTransportStats(PrintWriter pw)696 public void dumpTransportStats(PrintWriter pw) { 697 mTransportStats.dump(pw); 698 } 699 fromPackageFilter(String packageName)700 private static Predicate<ComponentName> fromPackageFilter(String packageName) { 701 return transportComponent -> packageName.equals(transportComponent.getPackageName()); 702 } 703 704 private static class TransportDescription { 705 private String name; 706 private final String transportDirName; 707 @Nullable private Intent configurationIntent; 708 private String currentDestinationString; 709 @Nullable private Intent dataManagementIntent; 710 @Nullable private CharSequence dataManagementLabel; 711 TransportDescription( String name, String transportDirName, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, @Nullable CharSequence dataManagementLabel)712 private TransportDescription( 713 String name, 714 String transportDirName, 715 @Nullable Intent configurationIntent, 716 String currentDestinationString, 717 @Nullable Intent dataManagementIntent, 718 @Nullable CharSequence dataManagementLabel) { 719 this.name = name; 720 this.transportDirName = transportDirName; 721 this.configurationIntent = configurationIntent; 722 this.currentDestinationString = currentDestinationString; 723 this.dataManagementIntent = dataManagementIntent; 724 this.dataManagementLabel = dataManagementLabel; 725 } 726 } 727 } 728