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