Move VVM activation state to device protected storage

VVM activation state is required to suppress legacy voicemail notification. It should not be shown if VVM is activated. Before this CL this information is stored in credential protected storage, and cannot be accessed right after boot. Telephony might have a stale VM count and will attempt to refresh the notification after reboot, which will not be suppressed.

In this CL the activation state is moved to device protected storage. Other account info are sensitive and remain in credential protected storage.

Test: TH

PiperOrigin-RevId: 159492498
Change-Id: I918546cd9874e47c4aa96889aa35916ca6c59890
diff --git a/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java b/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java
index a7cfc0c..b86ce82 100644
--- a/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java
+++ b/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java
@@ -82,10 +82,9 @@
       return;
     }
 
-    if (UserManagerCompat.isUserUnlocked(context)
-        && VoicemailComponent.get(context)
-            .getVoicemailClient()
-            .isActivated(context, phoneAccountHandle)) {
+    if (VoicemailComponent.get(context)
+        .getVoicemailClient()
+        .isActivated(context, phoneAccountHandle)) {
       LogUtil.i(
           "LegacyVoicemailNotificationReceiver.onReceive",
           "visual voicemail is activated, ignoring notification");
diff --git a/java/com/android/voicemail/impl/sync/VvmAccountManager.java b/java/com/android/voicemail/impl/sync/VvmAccountManager.java
index cc4e31f..2625fba 100644
--- a/java/com/android/voicemail/impl/sync/VvmAccountManager.java
+++ b/java/com/android/voicemail/impl/sync/VvmAccountManager.java
@@ -15,14 +15,20 @@
  */
 package com.android.voicemail.impl.sync;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.os.Build.VERSION_CODES;
+import android.os.UserManager;
 import android.support.annotation.MainThread;
 import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.util.ArraySet;
 import com.android.dialer.common.Assert;
+import com.android.dialer.common.PerAccountSharedPreferences;
 import com.android.dialer.common.concurrent.ThreadUtil;
+import com.android.dialer.util.DialerUtils;
 import com.android.voicemail.impl.OmtpConstants;
 import com.android.voicemail.impl.VisualVoicemailPreferences;
 import com.android.voicemail.impl.VoicemailStatus;
@@ -40,6 +46,7 @@
  * #removeAccount(Context, PhoneAccountHandle)} should be called to clear the connection information
  * and allow reactivation.
  */
+@TargetApi(VERSION_CODES.O)
 public class VvmAccountManager {
   public static final String TAG = "VvmAccountManager";
 
@@ -49,7 +56,7 @@
     void onActivationStateChanged(PhoneAccountHandle phoneAccountHandle, boolean isActivated);
   }
 
-  private static final String IS_ACCOUNT_ACTIVATED = "is_account_activated";
+  @VisibleForTesting static final String IS_ACCOUNT_ACTIVATED = "is_account_activated";
 
   private static Set<Listener> listeners = new ArraySet<>();
 
@@ -57,7 +64,8 @@
       Context context, PhoneAccountHandle phoneAccountHandle, StatusMessage statusMessage) {
     VisualVoicemailPreferences preferences =
         new VisualVoicemailPreferences(context, phoneAccountHandle);
-    statusMessage.putStatus(preferences.edit()).putBoolean(IS_ACCOUNT_ACTIVATED, true).apply();
+    statusMessage.putStatus(preferences.edit()).apply();
+    setAccountActivated(context, phoneAccountHandle, true);
 
     ThreadUtil.postOnUiThread(
         () -> {
@@ -69,10 +77,10 @@
 
   public static void removeAccount(Context context, PhoneAccountHandle phoneAccount) {
     VoicemailStatus.disable(context, phoneAccount);
+    setAccountActivated(context, phoneAccount, false);
     VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(context, phoneAccount);
     preferences
         .edit()
-        .putBoolean(IS_ACCOUNT_ACTIVATED, false)
         .putString(OmtpConstants.IMAP_USER_NAME, null)
         .putString(OmtpConstants.IMAP_PASSWORD, null)
         .apply();
@@ -86,7 +94,9 @@
 
   public static boolean isAccountActivated(Context context, PhoneAccountHandle phoneAccount) {
     Assert.isNotNull(phoneAccount);
-    VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(context, phoneAccount);
+    PerAccountSharedPreferences preferences =
+        getPreferenceForActivationState(context, phoneAccount);
+    migrateActivationState(context, preferences, phoneAccount);
     return preferences.getBoolean(IS_ACCOUNT_ACTIVATED, false);
   }
 
@@ -113,4 +123,47 @@
     Assert.isMainThread();
     listeners.remove(listener);
   }
+
+  /**
+   * The activation state is moved from credential protected storage to device protected storage
+   * after v10, so it can be checked under FBE. The state should be migrated to avoid reactivation.
+   */
+  private static void migrateActivationState(
+      Context context,
+      PerAccountSharedPreferences deviceProtectedPreference,
+      PhoneAccountHandle phoneAccountHandle) {
+    if (!context.getSystemService(UserManager.class).isUserUnlocked()) {
+      return;
+    }
+    if (deviceProtectedPreference.contains(IS_ACCOUNT_ACTIVATED)) {
+      return;
+    }
+
+    PerAccountSharedPreferences credentialProtectedPreference =
+        new VisualVoicemailPreferences(context, phoneAccountHandle);
+
+    deviceProtectedPreference
+        .edit()
+        .putBoolean(
+            IS_ACCOUNT_ACTIVATED,
+            credentialProtectedPreference.getBoolean(IS_ACCOUNT_ACTIVATED, false))
+        .apply();
+  }
+
+  private static void setAccountActivated(
+      Context context, PhoneAccountHandle phoneAccountHandle, boolean activated) {
+    Assert.isNotNull(phoneAccountHandle);
+    getPreferenceForActivationState(context, phoneAccountHandle)
+        .edit()
+        .putBoolean(IS_ACCOUNT_ACTIVATED, activated)
+        .apply();
+  }
+
+  private static PerAccountSharedPreferences getPreferenceForActivationState(
+      Context context, PhoneAccountHandle phoneAccountHandle) {
+    return new PerAccountSharedPreferences(
+        context,
+        phoneAccountHandle,
+        DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(context));
+  }
 }