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