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