Add getCurrentTransportComponent() API

Bug: 73640944
Test: atest RunFrameworksServicesRoboTests
Change-Id: I62193d63367c3b7564ccd41f5b103a7076764e3f
diff --git a/api/system-current.txt b/api/system-current.txt
index 1322a39..242e6f2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -484,6 +484,7 @@
     method public long getAvailableRestoreToken(java.lang.String);
     method public android.content.Intent getConfigurationIntent(java.lang.String);
     method public java.lang.String getCurrentTransport();
+    method public android.content.ComponentName getCurrentTransportComponent();
     method public android.content.Intent getDataManagementIntent(java.lang.String);
     method public java.lang.String getDataManagementLabel(java.lang.String);
     method public java.lang.String getDestinationString(java.lang.String);
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index debc32b..8e92393 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -443,6 +443,27 @@
     }
 
     /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+     * null} if no transport selected or if the transport selected is not registered.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    @Nullable
+    public ComponentName getCurrentTransportComponent() {
+        checkServiceBinder();
+        if (sService != null) {
+            try {
+                return sService.getCurrentTransportComponent();
+            } catch (RemoteException e) {
+                Log.e(TAG, "getCurrentTransportComponent() couldn't connect");
+            }
+        }
+        return null;
+    }
+
+    /**
      * Request a list of all available backup transports' names.
      *
      * @hide
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index f3ca746..1c55d8a 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -244,8 +244,6 @@
      *     {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
      * @throws SecurityException If the UID of the calling process differs from the package UID of
      *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
-     *
-     * @hide
      */
     void updateTransportAttributes(in ComponentName transportComponent, in String name,
             in Intent configurationIntent, in String currentDestinationString,
@@ -257,6 +255,13 @@
      */
     String getCurrentTransport();
 
+     /**
+      * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+      * null} if no transport selected or if the transport selected is not registered.  Callers must
+      * hold the android.permission.BACKUP permission to use this method.
+      */
+    ComponentName getCurrentTransportComponent();
+
     /**
      * Request a list of all available backup transports' names.  Callers must
      * hold the android.permission.BACKUP permission to use this method.
@@ -296,8 +301,6 @@
      *                  the transport's name that is returned by {@link BackupTransport#name()}.
      * @param listener A listener object to get a callback on the transport being selected. It may
      *                 be {@code null}.
-     *
-     * @hide
      */
     void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
 
@@ -364,7 +367,6 @@
      * @param result In the case of a full backup measure operation, the estimated
      *        total file size that would result from the operation. Unused in all other
      *        cases.
-     * {@hide}
      */
     void opComplete(int token, long result);
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index bd51af2..d7db471 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2901,6 +2901,25 @@
         return currentTransport;
     }
 
+    /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+     * null} if no transport selected or if the transport selected is not registered.
+     */
+    @Override
+    @Nullable
+    public ComponentName getCurrentTransportComponent() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            return mTransportManager.getCurrentTransportComponent();
+        } catch (TransportNotRegisteredException e) {
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
     // Report all known, available backup transports
     @Override
     public String[] listAllTransports() {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index aabe7f6..f2219a0 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -16,6 +16,7 @@
 
 package com.android.server.backup;
 
+import android.annotation.Nullable;
 import android.app.IBackupAgent;
 import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
@@ -132,6 +133,10 @@
   // Report the name of the currently active transport
   String getCurrentTransport();
 
+  // Report the component name of the host service of the currently active transport
+  @Nullable
+  ComponentName getCurrentTransportComponent();
+
   // Report all known, available backup transports
   String[] listAllTransports();
 
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 2abeaa6..787d667 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -19,8 +19,8 @@
 import android.annotation.Nullable;
 import android.app.backup.BackupManager;
 import android.app.backup.IBackupManager;
-import android.app.backup.IBackupObserver;
 import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreSession;
 import android.app.backup.ISelectBackupTransportCallback;
@@ -341,6 +341,17 @@
         return (svc != null) ? svc.getCurrentTransport() : null;
     }
 
+    /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or
+     * {@code null} if no transport selected or if the transport selected is not registered.
+     */
+    @Override
+    @Nullable
+    public ComponentName getCurrentTransportComponent() {
+        BackupManagerServiceInterface svc = mService;
+        return (svc != null) ? svc.getCurrentTransportComponent() : null;
+    }
+
     @Override
     public String[] listAllTransports() throws RemoteException {
         BackupManagerServiceInterface svc = mService;
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 64e24d8..ddce6bb 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -176,12 +176,30 @@
         return mTransportWhitelist;
     }
 
+    /** Returns the name of the selected transport or {@code null} if no transport selected. */
     @Nullable
     public String getCurrentTransportName() {
         return mCurrentTransportName;
     }
 
     /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or
+     * {@code null} if no transport selected.
+     *
+     * @throws TransportNotRegisteredException if the selected transport is not registered.
+     */
+    @Nullable
+    public ComponentName getCurrentTransportComponent()
+            throws TransportNotRegisteredException {
+        synchronized (mTransportLock) {
+            if (mCurrentTransportName == null) {
+                return null;
+            }
+            return getRegisteredTransportComponentOrThrowLocked(mCurrentTransportName);
+        }
+    }
+
+    /**
      * Returns the transport name associated with {@code transportComponent}.
      *
      * @throws TransportNotRegisteredException if the transport is not registered.
@@ -325,6 +343,16 @@
     }
 
     @GuardedBy("mTransportLock")
+    private ComponentName getRegisteredTransportComponentOrThrowLocked(String transportName)
+            throws TransportNotRegisteredException {
+        ComponentName transportComponent = getRegisteredTransportComponentLocked(transportName);
+        if (transportComponent == null) {
+            throw new TransportNotRegisteredException(transportName);
+        }
+        return transportComponent;
+    }
+
+    @GuardedBy("mTransportLock")
     private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
             ComponentName transportComponent) throws TransportNotRegisteredException {
         TransportDescription description =
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 340100f..8399aaf 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -76,10 +76,9 @@
 
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
-    manifest = Config.NONE,
-    sdk = 26,
-    shadows = {ShadowAppBackupUtils.class, ShadowBackupPolicyEnforcer.class}
-)
+        manifest = Config.NONE,
+        sdk = 26,
+        shadows = {ShadowAppBackupUtils.class, ShadowBackupPolicyEnforcer.class})
 @SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class BackupManagerServiceTest {
@@ -406,6 +405,51 @@
                 mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
     }
 
+    /* Tests for transport attributes */
+
+    @Test
+    public void testGetCurrentTransportComponent() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent())
+                .thenReturn(mTransport.getTransportComponent());
+        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
+    }
+
+    @Test
+    public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
+        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isNull();
+    }
+
+    @Test
+    public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent())
+                .thenThrow(TransportNotRegisteredException.class);
+        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isNull();
+    }
+
+    @Test
+    public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
+    }
+
     /* Tests for updating transport attributes */
 
     private static final int PACKAGE_UID = 10;
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index c2e7595..051a4a0 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -77,10 +77,9 @@
 
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
-    manifest = Config.NONE,
-    sdk = 26,
-    shadows = {FrameworkShadowContextImpl.class}
-)
+        manifest = Config.NONE,
+        sdk = 26,
+        shadows = {FrameworkShadowContextImpl.class})
 @SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class TransportManagerTest {
@@ -394,6 +393,36 @@
     }
 
     @Test
+    public void testGetCurrentTransportComponent() throws Exception {
+        TransportManager transportManager =
+                createTransportManagerWithRegisteredTransports(mTransportA1);
+
+        ComponentName transportComponent = transportManager.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isEqualTo(mTransportA1.getTransportComponent());
+    }
+
+    @Test
+    public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
+        TransportManager transportManager =
+                createTransportManagerWithRegisteredTransports(null, mTransportA1);
+
+        ComponentName transportComponent = transportManager.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isNull();
+    }
+
+    @Test
+    public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
+        TransportManager transportManager =
+                createTransportManagerWithRegisteredTransports(mTransportA1.unregistered());
+
+        expectThrows(
+                TransportNotRegisteredException.class,
+                transportManager::getCurrentTransportComponent);
+    }
+
+    @Test
     public void testGetTransportClient_forRegisteredTransport() throws Exception {
         TransportManager transportManager =
                 createTransportManagerWithRegisteredTransports(mTransportA1, mTransportA2);
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
index 1fbadd4..5844131 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
@@ -55,6 +55,9 @@
             ShadowPackageManager shadowPackageManager, TransportData... transports)
             throws Exception {
         for (TransportData transport : transports) {
+            if (transport.transportStatus == TransportStatus.UNREGISTERED) {
+                continue;
+            }
             ComponentName transportComponent = transport.getTransportComponent();
             String packageName = transportComponent.getPackageName();
             ResolveInfo resolveInfo = resolveInfo(transportComponent);