Fix setRecoverySecretTypes to not always set snapshot pending

Only updates should set snapshot pending. Setting the secret types for
the first time should not set snapshot pending. If it did, then just
initializing the recovery agent would cause a snapshot to be made, even
if it contained no keys. Also, setting the secret types to the same value
as it was previously should not set snapshot pending, for the exact same
reason. If the secret types were to change, however, for some reason,
then a new snapshot must be made, as it may have additional or fewer
layers of protection.

Bug: 74949975
Test: runtest frameworks-services -p \
      com.android.server.locksettings.recoverablekeystore

Change-Id: Ib29d56d5c46e730d9ed457f2d516f84ecb9e53b8
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index f0bcb81..e0487ea 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -382,10 +382,26 @@
         Preconditions.checkNotNull(secretTypes, "secretTypes is null");
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
-        long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
-        if (updatedRows > 0) {
-            mDatabase.setShouldCreateSnapshot(userId, uid, true);
+
+        int[] currentSecretTypes = mDatabase.getRecoverySecretTypes(userId, uid);
+        if (Arrays.equals(secretTypes, currentSecretTypes)) {
+            Log.v(TAG, "Not updating secret types - same as old value.");
+            return;
         }
+
+        long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
+        if (updatedRows < 1) {
+            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR,
+                    "Database error trying to set secret types.");
+        }
+
+        if (currentSecretTypes.length == 0) {
+            Log.i(TAG, "Initialized secret types.");
+            return;
+        }
+
+        Log.i(TAG, "Updated secret types. Snapshot pending.");
+        mDatabase.setShouldCreateSnapshot(userId, uid, true);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 445e50a..d4c3381 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -941,7 +941,7 @@
     }
 
     @Test
-    public void setRecoverySecretTypes() throws Exception {
+    public void setRecoverySecretTypes_updatesSecretTypes() throws Exception {
         int[] types1 = new int[]{11, 2000};
         int[] types2 = new int[]{1, 2, 3};
         int[] types3 = new int[]{};
@@ -960,6 +960,41 @@
     }
 
     @Test
+    public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfIniting() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        int[] secretTypes = new int[] { 101 };
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+    }
+
+    @Test
+    public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfSettingSameValue()
+            throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        int[] secretTypes = new int[] { 101 };
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+    }
+
+    @Test
+    public void setRecoverySecretTypes_setsSnapshotPendingIfUpdatingValue() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 101 });
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 });
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
     public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
         try {
             mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);