Create API in BackupManagerService for work profile serial id mapping.

The launcher needs to know the serial id of the ancenstral device's work
profile  and the serial id of the current device's work profile in order
to properly perform a restore.

Test: atest BackupManagerService
Bug: 111301511

Change-Id: Ia929dcc2cb599f935183be1820b1c45f2d6e1de7
diff --git a/api/current.txt b/api/current.txt
index 417ae80..03b384b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7252,6 +7252,7 @@
     ctor public BackupManager(android.content.Context);
     method public void dataChanged();
     method public static void dataChanged(String);
+    method @Nullable public android.os.UserHandle getUserForAncestralSerialNumber(long);
     method @Deprecated public int requestRestore(android.app.backup.RestoreObserver);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index bf3c0a2..d7a9779 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -711,6 +711,7 @@
     method @Deprecated public int requestRestore(android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor);
     method @Deprecated @RequiresPermission(android.Manifest.permission.BACKUP) public String selectBackupTransport(String);
     method @RequiresPermission(android.Manifest.permission.BACKUP) public void selectBackupTransport(android.content.ComponentName, android.app.backup.SelectBackupTransportCallback);
+    method @RequiresPermission(android.Manifest.permission.BACKUP) public void setAncestralSerialNumber(long);
     method @RequiresPermission(android.Manifest.permission.BACKUP) public void setAutoRestore(boolean);
     method @RequiresPermission(android.Manifest.permission.BACKUP) public void setBackupEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.BACKUP) public void updateTransportAttributes(android.content.ComponentName, String, @Nullable android.content.Intent, String, @Nullable android.content.Intent, @Nullable String);
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index a6f6d06..868fbfe 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -751,6 +751,47 @@
     }
 
     /**
+     * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
+     * serial number of the its ancestral work profile or {@code null} if there is none.
+     *
+     * <p> The ancestral serial number will have a corresponding {@link UserHandle} if the device
+     * has a work profile that was restored from another work profile with serial number
+     * {@code ancestralSerialNumber}.
+     *
+     * @see UserManager#getSerialNumberForUser(UserHandle)
+     */
+    @Nullable
+    public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+        if (sService != null) {
+            try {
+                return sService.getUserForAncestralSerialNumber(ancestralSerialNumber);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getUserForAncestralSerialNumber() couldn't connect");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Sets the ancestral work profile for the calling user.
+     *
+     * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+     * callers profile.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public void setAncestralSerialNumber(long ancestralSerialNumber) {
+        if (sService != null) {
+            try {
+                sService.setAncestralSerialNumber(ancestralSerialNumber);
+            } catch (RemoteException e) {
+                Log.e(TAG, "setAncestralSerialNumber() couldn't connect");
+            }
+        }
+    }
+
+    /**
      * Returns an {@link Intent} for the specified transport's configuration UI.
      * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
      * Intent, String)}.
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index eda8981..8386c72 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -22,6 +22,7 @@
 import android.app.backup.IRestoreSession;
 import android.app.backup.ISelectBackupTransportCallback;
 import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
 import android.content.Intent;
 import android.content.ComponentName;
 
@@ -685,4 +686,24 @@
      * {@link android.app.backup.IBackupManager.cancelBackups} for the calling user id.
      */
     void cancelBackups();
+
+    /**
+     * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the serial
+     * number of the it's ancestral work profile.
+     *
+     * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
+     * and it corresponds to the profile that was used to restore to the callers profile.
+     */
+    UserHandle getUserForAncestralSerialNumber(in long ancestralSerialNumber);
+
+    /**
+     * Sets the ancestral work profile for the calling user.
+     *
+     * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+     * callers profile.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     */
+    void setAncestralSerialNumber(in long ancestralSerialNumber);
+
 }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 14322ec..0dd1ded 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -41,6 +41,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -433,8 +434,46 @@
                 getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
 
         return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.getConfigurationIntent(transportName);
+            ? null
+            : userBackupManagerService.getConfigurationIntent(transportName);
+    }
+
+    /**
+     * Sets the ancestral work profile for the calling user.
+     *
+     * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+     * callers profile.
+     */
+    public void setAncestralSerialNumber(long ancestralSerialNumber) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(
+                        Binder.getCallingUserHandle().getIdentifier(),
+                        "setAncestralSerialNumber()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber);
+        }
+    }
+
+    /**
+     * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
+     * serial number of the its ancestral work profile.
+     *
+     * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
+     * and it corresponds to the profile that was used to restore to the callers profile.
+     */
+    @Nullable
+    public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+        for (UserHandle handle : mContext.getSystemService(UserManager.class).getUserProfiles()) {
+            UserBackupManagerService userBackupManagerService = getServiceUsers().get(
+                    handle.getIdentifier());
+            if (userBackupManagerService != null) {
+                if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
+                    return handle;
+                }
+            }
+        }
+        return null;
     }
 
     /**
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 87872e8..17e9b35 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -760,6 +760,21 @@
     }
 
     @Override
+    @Nullable public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+        if (mService != null) {
+            return mService.getUserForAncestralSerialNumber(ancestralSerialNumber);
+        }
+        return null;
+    }
+
+    @Override
+    public void setAncestralSerialNumber(long ancestralSerialNumber) {
+        if (mService != null) {
+            mService.setAncestralSerialNumber(ancestralSerialNumber);
+        }
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
         int userId = binderGetCallingUserId();
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 8b2c1b9..b2afbc3 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -244,6 +244,8 @@
     private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
+    private static final String SERIAL_ID_FILE = "serial_id";
+
     private final @UserIdInt int mUserId;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private final TransportManager mTransportManager;
@@ -360,6 +362,8 @@
     private Set<String> mAncestralPackages = null;
     private long mAncestralToken = 0;
     private long mCurrentToken = 0;
+    @Nullable private File mAncestralSerialNumberFile;
+
 
     /**
      * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
@@ -2308,6 +2312,55 @@
         }
     }
 
+    /**
+     * Sets the work profile serial number of the ancestral work profile.
+     */
+    public void setAncestralSerialNumber(long ancestralSerialNumber) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "setAncestralSerialNumber");
+        Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber);
+        try (RandomAccessFile af = getAncestralSerialNumberFile()) {
+            af.writeLong(ancestralSerialNumber);
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to write to work profile serial mapping file:", e);
+        }
+    }
+
+    /**
+     * Returns the work profile serial number of the ancestral device. This will be set by
+     * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set.
+     */
+    public long getAncestralSerialNumber() {
+        try (RandomAccessFile af = getAncestralSerialNumberFile()) {
+            return af.readLong();
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to write to work profile serial number file:", e);
+            return -1;
+        }
+    }
+
+    private RandomAccessFile getAncestralSerialNumberFile() throws FileNotFoundException {
+        if (mAncestralSerialNumberFile == null) {
+            mAncestralSerialNumberFile = new File(
+                UserBackupManagerFiles.getBaseStateDir(getUserId()),
+                SERIAL_ID_FILE);
+            if (!mAncestralSerialNumberFile.exists()) {
+                try {
+                    mAncestralSerialNumberFile.createNewFile();
+                } catch (IOException e) {
+                    Slog.w(TAG, "serial number mapping file creation failed", e);
+                }
+            }
+        }
+        return new RandomAccessFile(mAncestralSerialNumberFile, "rwd");
+    }
+
+    @VisibleForTesting
+    void setAncestralSerialNumberFile(File ancestralSerialNumberFile) {
+        mAncestralSerialNumberFile = ancestralSerialNumberFile;
+    }
+
+
     /** Clear the given package's backup data from the current transport. */
     public void clearBackupData(String transportName, String packageName) {
         if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index b8db3f3..37909c3 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -62,6 +62,7 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 
 /** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
@@ -1516,8 +1517,7 @@
     public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
         BackupManagerService backupManagerService =
                 createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
-        File testFile = new File(mContext.getFilesDir(), "test");
-        testFile.createNewFile();
+        File testFile = createTestFile();
         FileDescriptor fileDescriptor = new FileDescriptor();
         PrintWriter printWriter = new PrintWriter(testFile);
         String[] args = {"1", "2"};
@@ -1531,8 +1531,7 @@
     @Test
     public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
         BackupManagerService backupManagerService = createService();
-        File testFile = new File(mContext.getFilesDir(), "test");
-        testFile.createNewFile();
+        File testFile = createTestFile();
         FileDescriptor fileDescriptor = new FileDescriptor();
         PrintWriter printWriter = new PrintWriter(testFile);
         String[] args = {"1", "2"};
@@ -1542,6 +1541,12 @@
         verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
     }
 
+    private File createTestFile() throws IOException {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        return testFile;
+    }
+
     private BackupManagerService createService() {
         mShadowContext.grantPermissions(BACKUP);
         return new BackupManagerService(
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 427aed7..c8d1eb4 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -76,6 +76,7 @@
 import org.robolectric.shadows.ShadowPackageManager;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -1158,6 +1159,58 @@
     }
 
     /**
+     * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns {@code -1}
+     * when value not set.
+     */
+    @Test
+    public void testGetAncestralSerialNumber_notSet_returnsMinusOne() {
+        UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
+
+        assertThat(service.getAncestralSerialNumber()).isEqualTo(-1L);
+    }
+
+    /**
+     * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns correct value
+     * when value set.
+     */
+    @Test
+    public void testGetAncestralSerialNumber_set_returnsCorrectValue() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
+        service.setAncestralSerialNumberFile(createTestFile());
+
+        long testSerialNumber = 20L;
+        service.setAncestralSerialNumber(testSerialNumber);
+
+        assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber);
+    }
+
+    /**
+     * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns correct value
+     * when value set.
+     */
+    @Test
+    public void testGetAncestralSerialNumber_setTwice_returnsCorrectValue() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
+        service.setAncestralSerialNumberFile(createTestFile());
+
+        long testSerialNumber = 20L;
+        long testSerialNumber2 = 21L;
+        service.setAncestralSerialNumber(testSerialNumber);
+        service.setAncestralSerialNumber(testSerialNumber2);
+
+        assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2);
+    }
+
+    private File createTestFile() throws IOException {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        return testFile;
+    }
+
+
+    /**
      * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
      * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
      */