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