Make activity launch retries reusable.

Bug: 208268987
Test: manual
Test: atest ManagedProvisioningRoboTest
Test: atest ManagedProvisioningTest
Test: RetryLauncherViewModelTest
Test: atest DeviceManagementRoleHolderUpdaterHelperTest

Change-Id: I215f3439cd7f64bc19fc114e62bada61bb5d9633
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0ce1ae6..c820fa7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -296,7 +296,7 @@
             android:immersive="true" />
 
         <activity
-            android:name=".preprovisioning.RoleHolderUpdaterLauncherActivity"
+            android:name=".common.RetryLaunchActivity"
             android:theme="@style/SudThemeGlifV3.DayNight"
             android:immersive="true" />
 
diff --git a/proguard.flags b/proguard.flags
index e35818f..42015b2 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -16,16 +16,16 @@
 -keep public class com.android.managedprovisioning.common.ViewModelEvent {
     public *;
 }
--keep class com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel {
+-keep class com.android.managedprovisioning.common.RetryLaunchViewModel {
     public *;
     *;
 }
 
--keep public class com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel$CanLaunchRoleHolderUpdaterChecker {
+-keep public class com.android.managedprovisioning.common.RetryLaunchViewModel$CanLaunchActivityChecker {
     public *;
 }
 
--keep public class com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel$Config {
+-keep public class com.android.managedprovisioning.common.RetryLaunchViewModel$Config {
     public *;
 }
 
diff --git a/src/com/android/managedprovisioning/ManagedProvisioningScreens.java b/src/com/android/managedprovisioning/ManagedProvisioningScreens.java
index c949d56..0a92bef 100644
--- a/src/com/android/managedprovisioning/ManagedProvisioningScreens.java
+++ b/src/com/android/managedprovisioning/ManagedProvisioningScreens.java
@@ -29,5 +29,5 @@
     FINALIZATION_INSIDE_SUW,
     TERMS,
     FINANCED_DEVICE_LANDING,
-    ROLE_HOLDER_UPDATER_LAUNCHER
+    RETRY_LAUNCH
 }
diff --git a/src/com/android/managedprovisioning/ScreenManager.java b/src/com/android/managedprovisioning/ScreenManager.java
index 90cd94d..53b7c77 100644
--- a/src/com/android/managedprovisioning/ScreenManager.java
+++ b/src/com/android/managedprovisioning/ScreenManager.java
@@ -26,18 +26,18 @@
 import static com.android.managedprovisioning.ManagedProvisioningScreens.PRE_PROVISIONING_VIA_NFC;
 import static com.android.managedprovisioning.ManagedProvisioningScreens.PROVISIONING;
 import static com.android.managedprovisioning.ManagedProvisioningScreens.RESET_AND_RETURN_DEVICE;
-import static com.android.managedprovisioning.ManagedProvisioningScreens.ROLE_HOLDER_UPDATER_LAUNCHER;
+import static com.android.managedprovisioning.ManagedProvisioningScreens.RETRY_LAUNCH;
 import static com.android.managedprovisioning.ManagedProvisioningScreens.TERMS;
 import static com.android.managedprovisioning.ManagedProvisioningScreens.WEB;
 
 import android.app.Activity;
 
+import com.android.managedprovisioning.common.RetryLaunchActivity;
 import com.android.managedprovisioning.finalization.FinalizationInsideSuwActivity;
 import com.android.managedprovisioning.preprovisioning.EncryptDeviceActivity;
 import com.android.managedprovisioning.preprovisioning.PostEncryptionActivity;
 import com.android.managedprovisioning.preprovisioning.PreProvisioningActivity;
 import com.android.managedprovisioning.preprovisioning.PreProvisioningActivityViaNfc;
-import com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterLauncherActivity;
 import com.android.managedprovisioning.preprovisioning.WebActivity;
 import com.android.managedprovisioning.preprovisioning.terms.TermsActivity;
 import com.android.managedprovisioning.provisioning.AdminIntegratedFlowPrepareActivity;
@@ -106,7 +106,7 @@
         map.put(FINALIZATION_INSIDE_SUW, FinalizationInsideSuwActivity.class);
         map.put(TERMS, TermsActivity.class);
         map.put(FINANCED_DEVICE_LANDING, FinancedDeviceLandingActivity.class);
-        map.put(ROLE_HOLDER_UPDATER_LAUNCHER, RoleHolderUpdaterLauncherActivity.class);
+        map.put(RETRY_LAUNCH, RetryLaunchActivity.class);
         return map;
     }
 
diff --git a/src/com/android/managedprovisioning/common/DeviceManagementRoleHolderUpdaterHelper.java b/src/com/android/managedprovisioning/common/DeviceManagementRoleHolderUpdaterHelper.java
index 6e7e148..751c24c 100644
--- a/src/com/android/managedprovisioning/common/DeviceManagementRoleHolderUpdaterHelper.java
+++ b/src/com/android/managedprovisioning/common/DeviceManagementRoleHolderUpdaterHelper.java
@@ -57,6 +57,7 @@
      * Creates an intent to be used to launch the role holder updater.
      */
     public Intent createRoleHolderUpdaterIntent() {
-        return new Intent(DevicePolicyManager.ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER);
+        return new Intent(DevicePolicyManager.ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER)
+                .setPackage(mRoleHolderUpdaterPackageName);
     }
 }
diff --git a/src/com/android/managedprovisioning/common/RetryLaunchActivity.java b/src/com/android/managedprovisioning/common/RetryLaunchActivity.java
new file mode 100644
index 0000000..b7b2199
--- /dev/null
+++ b/src/com/android/managedprovisioning/common/RetryLaunchActivity.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.managedprovisioning.common;
+
+import static java.util.Objects.requireNonNull;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import androidx.annotation.Nullable;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.android.managedprovisioning.R;
+import com.android.managedprovisioning.common.RetryLaunchViewModel.Config;
+import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityEvent;
+import com.android.managedprovisioning.common.RetryLaunchViewModel.RetryLaunchViewModelFactory;
+
+/**
+ * An {@link Activity} which tries to start the {@link #EXTRA_INTENT_TO_LAUNCH} intent
+ * {@link #EXTRA_MAX_RETRIES} times every {@link #EXTRA_RETRY_PERIOD_MS} milliseconds.
+ *
+ * <p>This {@link Activity} is meant to be used in cases where there is a possibility the {@link
+ * #EXTRA_INTENT_TO_LAUNCH} intent may not be available the first time, for example if the app
+ * that resolves the {@link Intent} is getting updated.
+ *
+ * <p>This {@link Activity} forwards the result code of the {@link Activity} that was resolved from
+ * the {@link #EXTRA_INTENT_TO_LAUNCH}. Upon failure to launch the {@link Intent}, this {@link
+ * Activity} returns {@link #RESULT_CANCELED}.
+ */
+public final class RetryLaunchActivity extends SetupGlifLayoutActivity {
+
+    /**
+     * A {@link Parcelable} extra describing the {@link Intent} to be launched.
+     *
+     * <p>This is a required extra. Not supplying it will throw an {@link IllegalStateException}.
+     */
+    public static final String EXTRA_INTENT_TO_LAUNCH =
+            "com.android.managedprovisioning.extra.INTENT_TO_LAUNCH";
+
+    /**
+     * An {@code int} extra which determines how many times to retry the activity relaunch.
+     *
+     * <p>Must be a non-negative number.
+     *
+     * <p>Default value is {@code 3}.
+     */
+    public static final String EXTRA_MAX_RETRIES =
+            "com.android.managedprovisioning.extra.MAX_RETRIES";
+
+    /**
+     * A {@code long} extra which determines how long to wait between each retry, in milliseconds.
+     *
+     * <p>Must be a non-negative number.
+     *
+     * <p>Default value is {@code 60000} (one minute).
+     */
+    public static final String EXTRA_RETRY_PERIOD_MS =
+            "com.android.managedprovisioning.extra.RETRY_PERIOD_MS";
+
+    private static final int DEFAULT_MAX_RETRIES = 3;
+
+    private static final long DEFAULT_RETRY_PERIOD_MS = 60_000L;
+
+    private static final int LAUNCH_ACTIVITY_REQUEST_CODE = 1;
+
+    private RetryLaunchViewModel mViewModel;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (!getIntent().hasExtra(EXTRA_INTENT_TO_LAUNCH)) {
+            throw new IllegalStateException("EXTRA_INTENT must be supplied.");
+        }
+        Intent activityIntent = getIntent().getParcelableExtra(EXTRA_INTENT_TO_LAUNCH);
+        requireNonNull(activityIntent);
+
+        Config config = createConfigFromIntent(getIntent());
+        mViewModel = createViewModelWithIntent(activityIntent, config);
+        setupViewModelObservation();
+
+        if (savedInstanceState == null) {
+            mViewModel.tryStartActivity();
+        }
+    }
+
+    private Config createConfigFromIntent(Intent intent) {
+        final int maxRetries = getMaxRetries(intent);
+        final long retryPeriodMs = getRetryPeriodMs(intent);
+        return new Config() {
+            @Override
+            public long getLaunchActivityRetryMillis() {
+                return retryPeriodMs;
+            }
+
+            @Override
+            public int getLaunchActivityMaxRetries() {
+                return maxRetries;
+            }
+        };
+    }
+
+    private int getMaxRetries(Intent intent) {
+        int maxRetries = intent.getIntExtra(EXTRA_MAX_RETRIES, DEFAULT_MAX_RETRIES);
+        if (maxRetries < 0) {
+            ProvisionLogger.loge("Invalid value passed for " + EXTRA_MAX_RETRIES + ". Expected a "
+                    + "non-negative value but got " + maxRetries);
+            maxRetries = DEFAULT_MAX_RETRIES;
+        }
+        return maxRetries;
+    }
+
+    private long getRetryPeriodMs(Intent intent) {
+        long retryPeriodMs = intent.getLongExtra(EXTRA_RETRY_PERIOD_MS, DEFAULT_RETRY_PERIOD_MS);
+        if (retryPeriodMs < 0) {
+            ProvisionLogger.loge("Invalid value passed for " + EXTRA_RETRY_PERIOD_MS + ". Expected "
+                    + "a non-negative value but got " + retryPeriodMs);
+            retryPeriodMs = DEFAULT_RETRY_PERIOD_MS;
+        }
+        return retryPeriodMs;
+    }
+
+    private void setupViewModelObservation() {
+        mViewModel.observeViewModelEvents().observe(this, viewModelEvent -> {
+            switch (viewModelEvent.getType()) {
+                case RetryLaunchViewModel.VIEW_MODEL_EVENT_LAUNCH_UPDATER:
+                    launchActivity(((LaunchActivityEvent) viewModelEvent).getIntent());
+                    break;
+                case RetryLaunchViewModel.VIEW_MODEL_EVENT_WAITING_FOR_RETRY:
+                    initializeUi();
+                    break;
+                case RetryLaunchViewModel.VIEW_MODEL_EVENT_LAUNCH_FAILURE:
+                    finishWithCancelResult();
+                    break;
+            }
+        });
+    }
+
+    private RetryLaunchViewModel createViewModelWithIntent(Intent activityIntent, Config config) {
+        return new ViewModelProvider(this,
+                new RetryLaunchViewModelFactory(
+                        getApplication(),
+                        activityIntent,
+                        config,
+                        mUtils))
+                .get(RetryLaunchViewModel.class);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mViewModel.stopLaunchRetries();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        if (requestCode != LAUNCH_ACTIVITY_REQUEST_CODE) {
+            super.onActivityResult(requestCode, resultCode, data);
+            return;
+        }
+        ProvisionLogger.logi("Retry launcher activity result code: " + resultCode);
+        finishWithResult(resultCode);
+    }
+
+    private void initializeUi() {
+        // TODO(b/208822780): Decide strings for this screen
+        int headerResId = R.string.downloading_administrator_header;
+        int titleResId = R.string.setup_device_progress;
+        initializeLayoutParams(R.layout.empty_loading_layout, headerResId);
+        setTitle(titleResId);
+    }
+
+    private void launchActivity(Intent intent) {
+        getTransitionHelper().startActivityForResultWithTransition(
+                this,
+                intent,
+                LAUNCH_ACTIVITY_REQUEST_CODE);
+    }
+
+    private void finishWithResult(int resultCode) {
+        setResult(resultCode);
+        getTransitionHelper().finishActivity(this);
+    }
+
+    private void finishWithCancelResult() {
+        finishWithResult(RESULT_CANCELED);
+    }
+}
diff --git a/src/com/android/managedprovisioning/common/RetryLaunchViewModel.java b/src/com/android/managedprovisioning/common/RetryLaunchViewModel.java
new file mode 100644
index 0000000..47d6706
--- /dev/null
+++ b/src/com/android/managedprovisioning/common/RetryLaunchViewModel.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.managedprovisioning.common;
+
+import static com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityFailureEvent.REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES;
+
+import static java.util.Objects.requireNonNull;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.util.Objects;
+
+final class RetryLaunchViewModel extends AndroidViewModel {
+    static final int VIEW_MODEL_EVENT_LAUNCH_UPDATER = 1;
+    static final int VIEW_MODEL_EVENT_LAUNCH_FAILURE = 2;
+    static final int VIEW_MODEL_EVENT_WAITING_FOR_RETRY = 3;
+
+    private final MutableLiveData<ViewModelEvent> mObservableEvents = new MutableLiveData<>();
+    private final Runnable mRunnable = RetryLaunchViewModel.this::tryStartActivity;
+    private final Handler mHandler;
+    private final CanLaunchActivityChecker mCanLaunchActivityChecker;
+    private final Config mConfig;
+    private final Intent mActivityIntent;
+
+    private int mNumberOfStartUpdaterTries = 0;
+
+    RetryLaunchViewModel(
+            @NonNull Application application,
+            Intent activityIntent,
+            Handler handler,
+            CanLaunchActivityChecker canLaunchActivityChecker,
+            Config config) {
+        super(application);
+        mActivityIntent = requireNonNull(activityIntent);
+        mHandler = requireNonNull(handler);
+        mCanLaunchActivityChecker = requireNonNull(canLaunchActivityChecker);
+        mConfig = requireNonNull(config);
+    }
+
+    MutableLiveData<ViewModelEvent> observeViewModelEvents() {
+        return mObservableEvents;
+    }
+
+    /**
+     * Tries to start the role holder updater.
+     * <ol>
+     * <li>If the activity can be launched, it is launched.</li>
+     * <li>If the activity cannot be currently launched (e.g. if the app it belongs to is being
+     * updated), then we schedule a retry, up to {@link Config#getLaunchActivityMaxRetries()}
+     * times total.</li>
+     * <li>If we exceed the max retry thresholds, we post a failure event.</li>
+     * </ol>
+     *
+     * @see LaunchActivityEvent
+     * @see LaunchActivityFailureEvent
+     */
+    void tryStartActivity() {
+        boolean canLaunchActivity = mCanLaunchActivityChecker.canLaunchActivity(
+                getApplication().getApplicationContext(), mActivityIntent);
+        if (canLaunchActivity) {
+            launchActivity(mActivityIntent);
+        } else {
+            ProvisionLogger.loge("Cannot launch activity " + mActivityIntent.getAction());
+            tryRescheduleActivityLaunch();
+        }
+    }
+
+    void stopLaunchRetries() {
+        mHandler.removeCallbacks(mRunnable);
+    }
+
+    /**
+     * Tries to reschedule the role holder updater launch.
+     */
+    private void tryRescheduleActivityLaunch() {
+        if (canRetryLaunchActivity(mNumberOfStartUpdaterTries)) {
+            scheduleRetryLaunchActivity();
+            mObservableEvents.postValue(new LaunchActivityWaitingForRetryEvent());
+        } else {
+            ProvisionLogger.loge("Exceeded maximum number of activity launch retries.");
+            mObservableEvents.postValue(
+                    new LaunchActivityFailureEvent(
+                            REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES));
+        }
+    }
+
+    private boolean canRetryLaunchActivity(int numTries) {
+        return numTries < mConfig.getLaunchActivityMaxRetries();
+    }
+
+    private void launchActivity(Intent intent) {
+        mObservableEvents.postValue(new LaunchActivityEvent(intent));
+    }
+
+    private void scheduleRetryLaunchActivity() {
+        mHandler.postDelayed(mRunnable, mConfig.getLaunchActivityRetryMillis());
+        mNumberOfStartUpdaterTries++;
+    }
+
+    static class LaunchActivityEvent extends ViewModelEvent {
+        private final Intent mIntent;
+
+        LaunchActivityEvent(Intent intent) {
+            super(VIEW_MODEL_EVENT_LAUNCH_UPDATER);
+            mIntent = requireNonNull(intent);
+        }
+
+        Intent getIntent() {
+            return mIntent;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof LaunchActivityEvent)) return false;
+            LaunchActivityEvent that = (LaunchActivityEvent) o;
+            return Objects.equals(mIntent.getAction(), that.mIntent.getAction())
+                    && Objects.equals(mIntent.getExtras(), that.mIntent.getExtras());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mIntent.getAction(), mIntent.getExtras());
+        }
+
+        @Override
+        public String toString() {
+            return "LaunchActivityEvent{"
+                    + "mIntent=" + mIntent + '}';
+        }
+    }
+
+    static class LaunchActivityFailureEvent extends ViewModelEvent {
+        static final int REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES = 1;
+
+        private final int mReason;
+
+        LaunchActivityFailureEvent(int reason) {
+            super(VIEW_MODEL_EVENT_LAUNCH_FAILURE);
+            mReason = reason;
+        }
+
+        int getReason() {
+            return mReason;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof LaunchActivityFailureEvent)) return false;
+            LaunchActivityFailureEvent that = (LaunchActivityFailureEvent) o;
+            return mReason == that.mReason;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mReason);
+        }
+
+        @Override
+        public String toString() {
+            return "LaunchActivityFailureEvent{"
+                    + "mReason=" + mReason + '}';
+        }
+    }
+
+    static class LaunchActivityWaitingForRetryEvent extends ViewModelEvent {
+        LaunchActivityWaitingForRetryEvent() {
+            super(VIEW_MODEL_EVENT_WAITING_FOR_RETRY);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof LaunchActivityWaitingForRetryEvent)) return false;
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(VIEW_MODEL_EVENT_WAITING_FOR_RETRY);
+        }
+
+        @Override
+        public String toString() {
+            return "LaunchActivityWaitingForRetryEvent{}";
+        }
+    }
+
+    static class RetryLaunchViewModelFactory implements ViewModelProvider.Factory {
+        private final Application mApplication;
+        private final Intent mActivityIntent;
+        private final Config mConfig;
+        private final Utils mUtils;
+
+        RetryLaunchViewModelFactory(
+                Application application,
+                Intent activityIntent,
+                Config config,
+                Utils utils) {
+            mApplication = requireNonNull(application);
+            mActivityIntent = requireNonNull(activityIntent);
+            mConfig = requireNonNull(config);
+            mUtils = requireNonNull(utils);
+        }
+
+        @Override
+        public <T extends ViewModel> T create(Class<T> aClass) {
+            return (T) new RetryLaunchViewModel(
+                    mApplication,
+                    mActivityIntent,
+                    new Handler(Looper.getMainLooper()),
+                    new DefaultCanLaunchActivityChecker(mUtils),
+                    mConfig);
+        }
+    }
+
+    interface CanLaunchActivityChecker {
+        boolean canLaunchActivity(Context context, Intent intent);
+    }
+
+    interface Config {
+        long getLaunchActivityRetryMillis();
+
+        int getLaunchActivityMaxRetries();
+    }
+
+    static class DefaultCanLaunchActivityChecker implements CanLaunchActivityChecker {
+
+        private final Utils mUtils;
+
+        DefaultCanLaunchActivityChecker(Utils utils) {
+            mUtils = requireNonNull(utils);
+        }
+
+        @Override
+        public boolean canLaunchActivity(Context context, Intent intent) {
+            return mUtils.canResolveIntentAsUser(context, intent, UserHandle.USER_SYSTEM);
+        }
+
+    }
+}
diff --git a/src/com/android/managedprovisioning/finalization/FinalizationForwarderActivity.java b/src/com/android/managedprovisioning/finalization/FinalizationForwarderActivity.java
index 1bd544e..deaa222 100644
--- a/src/com/android/managedprovisioning/finalization/FinalizationForwarderActivity.java
+++ b/src/com/android/managedprovisioning/finalization/FinalizationForwarderActivity.java
@@ -16,15 +16,20 @@
 
 package com.android.managedprovisioning.finalization;
 
+import static com.android.managedprovisioning.ManagedProvisioningScreens.RETRY_LAUNCH;
+
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 
+import com.android.managedprovisioning.ManagedProvisioningBaseApplication;
+import com.android.managedprovisioning.ManagedProvisioningScreens;
 import com.android.managedprovisioning.common.DefaultPackageInstallChecker;
 import com.android.managedprovisioning.common.DeviceManagementRoleHolderHelper;
 import com.android.managedprovisioning.common.DeviceManagementRoleHolderHelper.DefaultResolveIntentChecker;
 import com.android.managedprovisioning.common.DeviceManagementRoleHolderHelper.DefaultRoleHolderStubChecker;
 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
+import com.android.managedprovisioning.common.RetryLaunchActivity;
 import com.android.managedprovisioning.common.RoleHolderProvider;
 import com.android.managedprovisioning.common.SharedPreferences;
 import com.android.managedprovisioning.common.TransitionHelper;
@@ -75,12 +80,25 @@
 
     @Override
     public void startRoleHolderFinalization() {
+        Intent intent = new Intent(this, getActivityForScreen(RETRY_LAUNCH));
+        intent.putExtra(
+                RetryLaunchActivity.EXTRA_INTENT_TO_LAUNCH,
+                mFinalizationController.createRoleHolderFinalizationIntent(this));
         mTransitionHelper.startActivityForResultWithTransition(
                 this,
-                mFinalizationController.createRoleHolderFinalizationIntent(this),
+                intent,
                 START_DEVICE_MANAGEMENT_ROLE_HOLDER_FINALIZATION_REQUEST_CODE);
     }
 
+    protected Class<? extends Activity> getActivityForScreen(ManagedProvisioningScreens screen) {
+        return getBaseApplication().getActivityClassForScreen(screen);
+    }
+
+    private ManagedProvisioningBaseApplication getBaseApplication() {
+        return ((ManagedProvisioningBaseApplication) getApplication());
+    }
+
+
     private FinalizationForwarderController createFinalizationController() {
         DeviceManagementRoleHolderHelper roleHolderHelper = new DeviceManagementRoleHolderHelper(
                 RoleHolderProvider.DEFAULT.getPackageName(this),
diff --git a/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivity.java b/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivity.java
index b6498db..254219a 100644
--- a/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivity.java
+++ b/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivity.java
@@ -16,10 +16,12 @@
 
 package com.android.managedprovisioning.preprovisioning;
 
+import static android.app.admin.DevicePolicyManager.RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR;
 import static android.content.res.Configuration.UI_MODE_NIGHT_MASK;
 import static android.content.res.Configuration.UI_MODE_NIGHT_YES;
 
-import static com.android.managedprovisioning.ManagedProvisioningScreens.ROLE_HOLDER_UPDATER_LAUNCHER;
+import static com.android.managedprovisioning.ManagedProvisioningScreens.RETRY_LAUNCH;
+import static com.android.managedprovisioning.common.RetryLaunchActivity.EXTRA_INTENT_TO_LAUNCH;
 import static com.android.managedprovisioning.model.ProvisioningParams.FLOW_TYPE_LEGACY;
 import static com.android.managedprovisioning.preprovisioning.PreProvisioningViewModel.STATE_PREPROVISIONING_INITIALIZING;
 import static com.android.managedprovisioning.preprovisioning.PreProvisioningViewModel.STATE_SHOWING_USER_CONSENT;
@@ -47,10 +49,13 @@
 import com.android.managedprovisioning.analytics.MetricsWriterFactory;
 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
 import com.android.managedprovisioning.common.AccessibilityContextMenuMaker;
+import com.android.managedprovisioning.common.DefaultPackageInstallChecker;
+import com.android.managedprovisioning.common.DeviceManagementRoleHolderUpdaterHelper;
 import com.android.managedprovisioning.common.GetProvisioningModeUtils;
 import com.android.managedprovisioning.common.LogoUtils;
 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
 import com.android.managedprovisioning.common.ProvisionLogger;
+import com.android.managedprovisioning.common.RetryLaunchActivity;
 import com.android.managedprovisioning.common.RoleHolderProvider;
 import com.android.managedprovisioning.common.RoleHolderUpdaterProvider;
 import com.android.managedprovisioning.common.SettingsFacade;
@@ -303,7 +308,13 @@
                 }
                 break;
             case START_DEVICE_MANAGEMENT_ROLE_HOLDER_UPDATER_REQUEST_CODE:
-                mController.startAppropriateProvisioning(getIntent());
+                if (resultCode == RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR
+                        && mController.canRetryRoleHolderUpdate()) {
+                    startRoleHolderUpdater();
+                    mController.incrementRoleHolderUpdateRetryCount();
+                } else {
+                    mController.startAppropriateProvisioning(getIntent());
+                }
                 break;
             case START_DEVICE_MANAGEMENT_ROLE_HOLDER_PROVISIONING_REQUEST_CODE:
                 ProvisionLogger.logw("Role holder returned result code " + resultCode);
@@ -440,7 +451,13 @@
 
     @Override
     public void startRoleHolderUpdater() {
-        Intent intent = new Intent(this, getActivityForScreen(ROLE_HOLDER_UPDATER_LAUNCHER));
+        DeviceManagementRoleHolderUpdaterHelper roleHolderUpdaterHelper =
+                new DeviceManagementRoleHolderUpdaterHelper(
+                        mRoleHolderUpdaterProvider.getPackageName(this),
+                        new DefaultPackageInstallChecker(mUtils));
+        Intent intent = new Intent(this, getActivityForScreen(RETRY_LAUNCH));
+        intent.putExtra(
+                EXTRA_INTENT_TO_LAUNCH, roleHolderUpdaterHelper.createRoleHolderUpdaterIntent());
         getTransitionHelper().startActivityForResultWithTransition(
                  this,
                 intent,
@@ -449,9 +466,11 @@
 
     @Override
     public void startRoleHolderProvisioning(Intent intent) {
+        Intent retryLaunchIntent = new Intent(this, getActivityForScreen(RETRY_LAUNCH));
+        retryLaunchIntent.putExtra(RetryLaunchActivity.EXTRA_INTENT_TO_LAUNCH, intent);
         getTransitionHelper().startActivityForResultWithTransition(
                 /* activity= */ this,
-                intent,
+                retryLaunchIntent,
                 START_DEVICE_MANAGEMENT_ROLE_HOLDER_PROVISIONING_REQUEST_CODE);
     }
 
diff --git a/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityController.java b/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityController.java
index ce37694..03d670e 100644
--- a/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityController.java
+++ b/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityController.java
@@ -111,6 +111,7 @@
 import com.android.managedprovisioning.model.ProvisioningParams.FlowType;
 import com.android.managedprovisioning.parser.DisclaimerParser;
 import com.android.managedprovisioning.parser.DisclaimersParserImpl;
+import com.android.managedprovisioning.preprovisioning.PreProvisioningViewModel.DefaultConfig;
 import com.android.managedprovisioning.preprovisioning.PreProvisioningViewModel.PreProvisioningViewModelFactory;
 
 import java.util.IllformedLocaleException;
@@ -155,7 +156,8 @@
                 new ViewModelProvider(
                         activity,
                         new PreProvisioningViewModelFactory(
-                                (ManagedProvisioningBaseApplication) activity.getApplication()))
+                                (ManagedProvisioningBaseApplication) activity.getApplication(),
+                                new DefaultConfig()))
                                         .get(PreProvisioningViewModel.class),
                 DisclaimersParserImpl::new,
                 new DeviceManagementRoleHolderHelper(
@@ -980,6 +982,14 @@
         return mViewModel.getState();
     }
 
+    void incrementRoleHolderUpdateRetryCount() {
+        mViewModel.incrementRoleHolderUpdateRetryCount();
+    }
+
+    boolean canRetryRoleHolderUpdate() {
+        return mViewModel.canRetryRoleHolderUpdate();
+    }
+
     private void showProvisioningErrorAndClose(String action, int provisioningPreCondition) {
         // Try to show an error message explaining why provisioning is not allowed.
         switch (action) {
diff --git a/src/com/android/managedprovisioning/preprovisioning/PreProvisioningViewModel.java b/src/com/android/managedprovisioning/preprovisioning/PreProvisioningViewModel.java
index 5481ea1..5633a8a 100644
--- a/src/com/android/managedprovisioning/preprovisioning/PreProvisioningViewModel.java
+++ b/src/com/android/managedprovisioning/preprovisioning/PreProvisioningViewModel.java
@@ -18,6 +18,8 @@
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.IntDef;
 import android.annotation.MainThread;
 import android.content.Intent;
@@ -36,7 +38,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
 
 /**
  * A {@link ViewModel} which maintains data related to preprovisioning.
@@ -49,6 +50,7 @@
     static final int STATE_PROVISIONING_STARTED = 5;
     static final int STATE_PROVISIONING_FINALIZED = 6;
 
+    private static final int DEFAULT_MAX_ROLE_HOLDER_UPDATE_RETRIES = 3;
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({STATE_PREPROVISIONING_INITIALIZING,
             STATE_GETTING_PROVISIONING_MODE,
@@ -57,20 +59,25 @@
             STATE_PROVISIONING_FINALIZED})
     private @interface PreProvisioningState {}
 
+
     private ProvisioningParams mParams;
     private final MessageParser mMessageParser;
     private final TimeLogger mTimeLogger;
     private final EncryptionController mEncryptionController;
     private final MutableLiveData<Integer> mState =
             new MutableLiveData<>(STATE_PREPROVISIONING_INITIALIZING);
+    private final Config mConfig;
+    private int mRoleHolderUpdateRetries = 1;
 
     PreProvisioningViewModel(
             TimeLogger timeLogger,
             MessageParser messageParser,
-            EncryptionController encryptionController) {
-        mMessageParser = Objects.requireNonNull(messageParser);
-        mTimeLogger = Objects.requireNonNull(timeLogger);
-        mEncryptionController = Objects.requireNonNull(encryptionController);
+            EncryptionController encryptionController,
+            Config config) {
+        mMessageParser = requireNonNull(messageParser);
+        mTimeLogger = requireNonNull(timeLogger);
+        mEncryptionController = requireNonNull(encryptionController);
+        mConfig = requireNonNull(config);
     }
 
     /**
@@ -188,11 +195,34 @@
         }
     }
 
+    void incrementRoleHolderUpdateRetryCount() {
+        mRoleHolderUpdateRetries++;
+    }
+
+    boolean canRetryRoleHolderUpdate() {
+        return mRoleHolderUpdateRetries < mConfig.getMaxRoleHolderUpdateRetries();
+    }
+
+    interface Config {
+        int getMaxRoleHolderUpdateRetries();
+    }
+
+    static class DefaultConfig implements Config {
+        @Override
+        public int getMaxRoleHolderUpdateRetries() {
+            return DEFAULT_MAX_ROLE_HOLDER_UPDATE_RETRIES;
+        }
+    }
+
     static class PreProvisioningViewModelFactory implements ViewModelProvider.Factory {
         private final ManagedProvisioningBaseApplication mApplication;
+        private final Config mConfig;
 
-        PreProvisioningViewModelFactory(ManagedProvisioningBaseApplication application) {
-            mApplication = application;
+        PreProvisioningViewModelFactory(
+                ManagedProvisioningBaseApplication application,
+                Config config) {
+            mApplication = requireNonNull(application);
+            mConfig = requireNonNull(config);
         }
 
         @Override
@@ -204,7 +234,8 @@
             return (T) new PreProvisioningViewModel(
                     new TimeLogger(mApplication, PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS),
                     new MessageParser(mApplication),
-                    mApplication.getEncryptionController());
+                    mApplication.getEncryptionController(),
+                    mConfig);
         }
     }
 }
diff --git a/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterLauncherActivity.java b/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterLauncherActivity.java
deleted file mode 100644
index ca27df1..0000000
--- a/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterLauncherActivity.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.managedprovisioning.preprovisioning;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-import androidx.annotation.Nullable;
-import androidx.lifecycle.ViewModelProvider;
-
-import com.android.managedprovisioning.R;
-import com.android.managedprovisioning.common.DefaultPackageInstallChecker;
-import com.android.managedprovisioning.common.DeviceManagementRoleHolderUpdaterHelper;
-import com.android.managedprovisioning.common.ProvisionLogger;
-import com.android.managedprovisioning.common.RoleHolderUpdaterProvider;
-import com.android.managedprovisioning.common.SetupGlifLayoutActivity;
-import com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel.LaunchRoleHolderUpdaterEvent;
-import com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel.RoleHolderUpdaterViewModelFactory;
-
-/**
- * An {@link android.app.Activity} which handles the launch of the device management role holder
- * updater.
- *
- * Upon successful update of the device management role holder, {@link #RESULT_OK} is returned,
- * otherwise the result is {@link #RESULT_CANCELED}.
- *
- * If the device management role holder updater is not currently available (for example, if it is
- * getting updated at this time), there is retry logic in place. If none of the retries works,
- * {@link #RESULT_CANCELED} is returned.
- */
-public final class RoleHolderUpdaterLauncherActivity extends SetupGlifLayoutActivity {
-
-    private static final int LAUNCH_ROLE_HOLDER_UPDATER_REQUEST_CODE = 1;
-
-    private RoleHolderUpdaterViewModel mViewModel;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mViewModel = createViewModel();
-        setupViewModelObservation();
-
-        initializeUi();
-
-        if (savedInstanceState == null) {
-            mViewModel.tryStartRoleHolderUpdater();
-        }
-    }
-
-    private void setupViewModelObservation() {
-        mViewModel.observeViewModelEvents().observe(this, viewModelEvent -> {
-            switch (viewModelEvent.getType()) {
-                case RoleHolderUpdaterViewModel.VIEW_MODEL_EVENT_LAUNCH_UPDATER:
-                    launchRoleHolderUpdater(
-                            ((LaunchRoleHolderUpdaterEvent) viewModelEvent).getIntent());
-                    break;
-                case RoleHolderUpdaterViewModel.VIEW_MODEL_EVENT_LAUNCH_FAILURE:
-                    finishWithCancelResult();
-                    break;
-            }
-        });
-    }
-
-    private RoleHolderUpdaterViewModel createViewModel() {
-        DeviceManagementRoleHolderUpdaterHelper roleHolderUpdaterHelper =
-                new DeviceManagementRoleHolderUpdaterHelper(
-                        RoleHolderUpdaterProvider.DEFAULT.getPackageName(this),
-                        new DefaultPackageInstallChecker(mUtils));
-        return new ViewModelProvider(this,
-                new RoleHolderUpdaterViewModelFactory(
-                        getApplication(), mUtils, roleHolderUpdaterHelper))
-                .get(RoleHolderUpdaterViewModel.class);
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        mViewModel.stopLaunchRetries();
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
-        if (requestCode != LAUNCH_ROLE_HOLDER_UPDATER_REQUEST_CODE) {
-            super.onActivityResult(requestCode, resultCode, data);
-            return;
-        }
-        ProvisionLogger.logi("Device management role holder updater result code: "
-                + resultCode);
-        if (resultCode == RESULT_OK) {
-            finishWithOkResult();
-        } else {
-            mViewModel.tryStartRoleHolderUpdater();
-        }
-    }
-
-    private void initializeUi() {
-        int headerResId = R.string.downloading_administrator_header;
-        int titleResId = R.string.setup_device_progress;
-        initializeLayoutParams(R.layout.empty_loading_layout, headerResId);
-        setTitle(titleResId);
-    }
-
-    private void launchRoleHolderUpdater(Intent intent) {
-        getTransitionHelper().startActivityForResultWithTransition(
-                this,
-                intent,
-                LAUNCH_ROLE_HOLDER_UPDATER_REQUEST_CODE);
-    }
-
-    private void finishWithResult(int resultCode) {
-        setResult(resultCode);
-        getTransitionHelper().finishActivity(this);
-    }
-
-    private void finishWithOkResult() {
-        finishWithResult(RESULT_OK);
-    }
-
-    private void finishWithCancelResult() {
-        finishWithResult(RESULT_CANCELED);
-    }
-}
diff --git a/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterViewModel.java b/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterViewModel.java
deleted file mode 100644
index a3c97cd..0000000
--- a/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterViewModel.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.managedprovisioning.preprovisioning;
-
-import static com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel.LaunchRoleHolderUpdaterFailureEvent.REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATER_LAUNCH_RETRIES;
-import static com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel.LaunchRoleHolderUpdaterFailureEvent.REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATE_RETRIES;
-
-import static java.util.Objects.requireNonNull;
-
-import android.app.Application;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-
-import com.android.managedprovisioning.common.DeviceManagementRoleHolderUpdaterHelper;
-import com.android.managedprovisioning.common.ProvisionLogger;
-import com.android.managedprovisioning.common.Utils;
-import com.android.managedprovisioning.common.ViewModelEvent;
-
-import java.util.Objects;
-
-final class RoleHolderUpdaterViewModel extends AndroidViewModel {
-    static final int VIEW_MODEL_EVENT_LAUNCH_UPDATER = 1;
-    static final int VIEW_MODEL_EVENT_LAUNCH_FAILURE = 2;
-
-    private static final int LAUNCH_ROLE_HOLDER_UPDATER_RETRY_PERIOD_MS = 60 * 1000;
-    private static final int LAUNCH_ROLE_HOLDER_UPDATER_MAX_RETRIES = 3;
-    private static final int ROLE_HOLDER_UPDATE_MAX_RETRIES = 3;
-
-    private final MutableLiveData<ViewModelEvent> mObservableEvents = new MutableLiveData<>();
-    private final Runnable mRunnable = RoleHolderUpdaterViewModel.this::tryStartRoleHolderUpdater;
-    private final Handler mHandler;
-    private final CanLaunchRoleHolderUpdaterChecker mCanLaunchRoleHolderUpdaterChecker;
-    private final Config mConfig;
-    private final DeviceManagementRoleHolderUpdaterHelper mRoleHolderUpdaterHelper;
-
-    private int mNumberOfStartUpdaterTries = 0;
-    private int mNumberOfUpdateTries = 0;
-
-    RoleHolderUpdaterViewModel(
-            @NonNull Application application,
-            Handler handler,
-            CanLaunchRoleHolderUpdaterChecker canLaunchRoleHolderUpdaterChecker,
-            Config config,
-            DeviceManagementRoleHolderUpdaterHelper roleHolderUpdaterHelper) {
-        super(application);
-        mHandler = requireNonNull(handler);
-        mCanLaunchRoleHolderUpdaterChecker = requireNonNull(canLaunchRoleHolderUpdaterChecker);
-        mConfig = requireNonNull(config);
-        mRoleHolderUpdaterHelper = requireNonNull(roleHolderUpdaterHelper);
-    }
-
-    MutableLiveData<ViewModelEvent> observeViewModelEvents() {
-        return mObservableEvents;
-    }
-
-    /**
-     * Tries to start the role holder updater.
-     * <ol>
-     * <li>If the role holder updater can be launched, it is launched. It then tries to download the
-     * role holder. If it fails to download it, it tries {@link #ROLE_HOLDER_UPDATE_MAX_RETRIES}
-     * times total.</li>
-     * <li>If the role holder updater cannot be currently launched (e.g. if it's being updated
-     * itself), then we schedule a retry, up to {@link #LAUNCH_ROLE_HOLDER_UPDATER_MAX_RETRIES}
-     * times total.</li>
-     * <li>If we exceed either of the max retry thresholds, we post a failure event.</li>
-     * </ol>
-     *
-     * @see LaunchRoleHolderUpdaterEvent
-     * @see LaunchRoleHolderUpdaterFailureEvent
-     */
-    void tryStartRoleHolderUpdater() {
-        Intent intent = mRoleHolderUpdaterHelper.createRoleHolderUpdaterIntent();
-        boolean canLaunchRoleHolderUpdater =
-                mCanLaunchRoleHolderUpdaterChecker.canLaunchRoleHolderUpdater(
-                        getApplication().getApplicationContext(), intent);
-        if (canLaunchRoleHolderUpdater
-                && canRetryUpdate(mNumberOfUpdateTries)) {
-            launchRoleHolderUpdater(intent);
-        } else if (canLaunchRoleHolderUpdater) {
-            ProvisionLogger.loge("Exceeded maximum number of update retries.");
-            mObservableEvents.postValue(
-                    new LaunchRoleHolderUpdaterFailureEvent(
-                            REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATE_RETRIES));
-        } else {
-            ProvisionLogger.loge("Cannot launch role holder updater.");
-            tryRescheduleRoleHolderUpdater();
-        }
-    }
-
-    void stopLaunchRetries() {
-        mHandler.removeCallbacks(mRunnable);
-    }
-
-    /**
-     * Tries to reschedule the role holder updater launch.
-     */
-    private void tryRescheduleRoleHolderUpdater() {
-        if (canRetryLaunchRoleHolderUpdater(mNumberOfStartUpdaterTries)) {
-            scheduleRetryLaunchRoleHolderUpdater();
-        } else {
-            ProvisionLogger.loge("Exceeded maximum number of role holder updater launch retries.");
-            mObservableEvents.postValue(
-                    new LaunchRoleHolderUpdaterFailureEvent(
-                            REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATER_LAUNCH_RETRIES));
-        }
-    }
-
-    private boolean canRetryUpdate(int numTries) {
-        return numTries < mConfig.getRoleHolderUpdateMaxRetries();
-    }
-
-    private boolean canRetryLaunchRoleHolderUpdater(int numTries) {
-        return numTries < mConfig.getLaunchRoleHolderMaxRetries();
-    }
-
-    private void launchRoleHolderUpdater(Intent intent) {
-        mObservableEvents.postValue(new LaunchRoleHolderUpdaterEvent(intent));
-        mNumberOfUpdateTries++;
-    }
-
-    private void scheduleRetryLaunchRoleHolderUpdater() {
-        mHandler.postDelayed(mRunnable, mConfig.getLaunchRoleHolderUpdaterPeriodMillis());
-        mNumberOfStartUpdaterTries++;
-    }
-
-    static class LaunchRoleHolderUpdaterEvent extends ViewModelEvent {
-        private final Intent mIntent;
-
-        LaunchRoleHolderUpdaterEvent(Intent intent) {
-            super(VIEW_MODEL_EVENT_LAUNCH_UPDATER);
-            mIntent = requireNonNull(intent);
-        }
-
-        Intent getIntent() {
-            return mIntent;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof LaunchRoleHolderUpdaterEvent)) return false;
-            LaunchRoleHolderUpdaterEvent that = (LaunchRoleHolderUpdaterEvent) o;
-            return Objects.equals(mIntent.getAction(), that.mIntent.getAction())
-                    && Objects.equals(mIntent.getExtras(), that.mIntent.getExtras());
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mIntent.getAction(), mIntent.getExtras());
-        }
-
-        @Override
-        public String toString() {
-            return "LaunchRoleHolderUpdaterEvent{"
-                    + "mIntent=" + mIntent + '}';
-        }
-    }
-
-    static class LaunchRoleHolderUpdaterFailureEvent extends ViewModelEvent {
-        static final int REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATE_RETRIES = 1;
-        static final int REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATER_LAUNCH_RETRIES = 2;
-
-        private final int mReason;
-
-        LaunchRoleHolderUpdaterFailureEvent(int reason) {
-            super(VIEW_MODEL_EVENT_LAUNCH_FAILURE);
-            mReason = reason;
-        }
-
-        int getReason() {
-            return mReason;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof LaunchRoleHolderUpdaterFailureEvent)) return false;
-            LaunchRoleHolderUpdaterFailureEvent that = (LaunchRoleHolderUpdaterFailureEvent) o;
-            return mReason == that.mReason;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mReason);
-        }
-
-        @Override
-        public String toString() {
-            return "LaunchRoleHolderUpdaterFailureEvent{"
-                    + "mReason=" + mReason + '}';
-        }
-    }
-
-    static class RoleHolderUpdaterViewModelFactory implements ViewModelProvider.Factory {
-        private final Application mApplication;
-        private final Utils mUtils;
-        private final DeviceManagementRoleHolderUpdaterHelper mRoleHolderUpdaterHelper;
-
-        RoleHolderUpdaterViewModelFactory(
-                Application application,
-                Utils utils,
-                DeviceManagementRoleHolderUpdaterHelper roleHolderUpdaterHelper) {
-            mApplication = requireNonNull(application);
-            mRoleHolderUpdaterHelper = requireNonNull(roleHolderUpdaterHelper);
-            mUtils = requireNonNull(utils);
-        }
-
-        @Override
-        public <T extends ViewModel> T create(Class<T> aClass) {
-            DefaultConfig config = new DefaultConfig();
-            return (T) new RoleHolderUpdaterViewModel(
-                    mApplication,
-                    new Handler(Looper.getMainLooper()),
-                    new DefaultCanLaunchRoleHolderUpdaterChecker(mUtils),
-                    config,
-                    mRoleHolderUpdaterHelper);
-        }
-    }
-
-    interface CanLaunchRoleHolderUpdaterChecker {
-        boolean canLaunchRoleHolderUpdater(Context context, Intent intent);
-    }
-
-    interface Config {
-        int getLaunchRoleHolderUpdaterPeriodMillis();
-
-        int getLaunchRoleHolderMaxRetries();
-
-        int getRoleHolderUpdateMaxRetries();
-    }
-
-    static class DefaultConfig implements Config {
-
-        @Override
-        public int getLaunchRoleHolderUpdaterPeriodMillis() {
-            return LAUNCH_ROLE_HOLDER_UPDATER_RETRY_PERIOD_MS;
-        }
-
-        @Override
-        public int getLaunchRoleHolderMaxRetries() {
-            return LAUNCH_ROLE_HOLDER_UPDATER_MAX_RETRIES;
-        }
-
-        @Override
-        public int getRoleHolderUpdateMaxRetries() {
-            return ROLE_HOLDER_UPDATE_MAX_RETRIES;
-        }
-    }
-
-    static class DefaultCanLaunchRoleHolderUpdaterChecker implements
-            CanLaunchRoleHolderUpdaterChecker {
-
-        private final Utils mUtils;
-
-        DefaultCanLaunchRoleHolderUpdaterChecker(Utils utils) {
-            mUtils = requireNonNull(utils);
-        }
-
-        @Override
-        public boolean canLaunchRoleHolderUpdater(Context context, Intent intent) {
-            return mUtils.canResolveIntentAsUser(context, intent, UserHandle.USER_SYSTEM);
-        }
-
-    }
-}
diff --git a/tests/instrumentation/src/com/android/managedprovisioning/ScreenManagerTest.java b/tests/instrumentation/src/com/android/managedprovisioning/ScreenManagerTest.java
index 7340c47..875f058 100644
--- a/tests/instrumentation/src/com/android/managedprovisioning/ScreenManagerTest.java
+++ b/tests/instrumentation/src/com/android/managedprovisioning/ScreenManagerTest.java
@@ -26,7 +26,7 @@
 import static com.android.managedprovisioning.ManagedProvisioningScreens.PRE_PROVISIONING_VIA_NFC;
 import static com.android.managedprovisioning.ManagedProvisioningScreens.PROVISIONING;
 import static com.android.managedprovisioning.ManagedProvisioningScreens.RESET_AND_RETURN_DEVICE;
-import static com.android.managedprovisioning.ManagedProvisioningScreens.ROLE_HOLDER_UPDATER_LAUNCHER;
+import static com.android.managedprovisioning.ManagedProvisioningScreens.RETRY_LAUNCH;
 import static com.android.managedprovisioning.ManagedProvisioningScreens.TERMS;
 import static com.android.managedprovisioning.ManagedProvisioningScreens.WEB;
 
@@ -42,13 +42,13 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.managedprovisioning.common.RetryLaunchActivity;
 import com.android.managedprovisioning.finalization.FinalizationForwarderActivity;
 import com.android.managedprovisioning.finalization.FinalizationInsideSuwActivity;
 import com.android.managedprovisioning.preprovisioning.EncryptDeviceActivity;
 import com.android.managedprovisioning.preprovisioning.PostEncryptionActivity;
 import com.android.managedprovisioning.preprovisioning.PreProvisioningActivity;
 import com.android.managedprovisioning.preprovisioning.PreProvisioningActivityViaNfc;
-import com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterLauncherActivity;
 import com.android.managedprovisioning.preprovisioning.WebActivity;
 import com.android.managedprovisioning.preprovisioning.terms.TermsActivity;
 import com.android.managedprovisioning.provisioning.AdminIntegratedFlowPrepareActivity;
@@ -115,8 +115,8 @@
                 .isEqualTo(TermsActivity.class);
         assertThat(screenManager.getActivityClassForScreen(FINANCED_DEVICE_LANDING))
                 .isEqualTo(FinancedDeviceLandingActivity.class);
-        assertThat(screenManager.getActivityClassForScreen(ROLE_HOLDER_UPDATER_LAUNCHER))
-                .isEqualTo(RoleHolderUpdaterLauncherActivity.class);
+        assertThat(screenManager.getActivityClassForScreen(RETRY_LAUNCH))
+                .isEqualTo(RetryLaunchActivity.class);
     }
 
     @Test
@@ -205,7 +205,7 @@
         map.put(FINALIZATION_INSIDE_SUW, Activity.class);
         map.put(TERMS, Activity.class);
         map.put(FINANCED_DEVICE_LANDING, Activity.class);
-        map.put(ROLE_HOLDER_UPDATER_LAUNCHER, Activity.class);
+        map.put(RETRY_LAUNCH, Activity.class);
         return map;
     }
 
diff --git a/tests/instrumentation/src/com/android/managedprovisioning/common/DeviceManagementRoleHolderUpdaterHelperTest.java b/tests/instrumentation/src/com/android/managedprovisioning/common/DeviceManagementRoleHolderUpdaterHelperTest.java
index f5b6e80..e9b1d97 100644
--- a/tests/instrumentation/src/com/android/managedprovisioning/common/DeviceManagementRoleHolderUpdaterHelperTest.java
+++ b/tests/instrumentation/src/com/android/managedprovisioning/common/DeviceManagementRoleHolderUpdaterHelperTest.java
@@ -45,7 +45,8 @@
     private static final String ROLE_HOLDER_UPDATER_EMPTY_PACKAGE_NAME = "";
     private static final String ROLE_HOLDER_UPDATER_NULL_PACKAGE_NAME = null;
     private static final Intent ROLE_HOLDER_UPDATER_INTENT =
-            new Intent(DevicePolicyManager.ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER);
+            new Intent(DevicePolicyManager.ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER)
+                    .setPackage(ROLE_HOLDER_UPDATER_PACKAGE_NAME);
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
 
diff --git a/tests/instrumentation/src/com/android/managedprovisioning/common/RetryLaunchViewModelTest.java b/tests/instrumentation/src/com/android/managedprovisioning/common/RetryLaunchViewModelTest.java
new file mode 100644
index 0000000..23c1395
--- /dev/null
+++ b/tests/instrumentation/src/com/android/managedprovisioning/common/RetryLaunchViewModelTest.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.managedprovisioning.common;
+
+import static com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityFailureEvent.REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Application;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.bedstead.nene.utils.Poll;
+import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityEvent;
+import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityFailureEvent;
+import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityWaitingForRetryEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.stream.Collectors;
+
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class RetryLaunchViewModelTest {
+    private static final int LAUNCH_ROLE_HOLDER_UPDATER_PERIOD_MILLIS = 100;
+    private static final int NO_EVENT_TIMEOUT_MILLIS = 200;
+    private static final int LAUNCH_ROLE_HOLDER_MAX_RETRIES = 1;
+    private static final int ROLE_HOLDER_UPDATE_MAX_RETRIES = 1;
+    private static final LaunchActivityEvent
+            LAUNCH_ROLE_HOLDER_UPDATER_EVENT = createLaunchRoleHolderUpdaterEvent();
+    private static final LaunchActivityFailureEvent
+            EXCEED_MAX_NUMBER_LAUNCH_RETRIES_EVENT = createExceedMaxNumberLaunchRetriesEvent();
+    private static final LaunchActivityWaitingForRetryEvent WAITING_FOR_RETRY_EVENT =
+            createWaitingForRetryEvent();
+    private static final String TEST_DEVICE_MANAGEMENT_ROLE_HOLDER_UPDATER_PACKAGE_NAME =
+            "com.devicemanagementroleholderupdater.test.package";
+
+    private final Context mApplicationContext = ApplicationProvider.getApplicationContext();
+    private Handler mHandler;
+    private TestConfig mTestConfig;
+    private boolean mCanLaunchRoleHolderUpdater = true;
+    private RetryLaunchViewModel mViewModel;
+    private Queue<ViewModelEvent> mEvents;
+    private Utils mUtils = new Utils();
+
+    @Before
+    public void setUp() {
+        mTestConfig = new TestConfig(
+                LAUNCH_ROLE_HOLDER_UPDATER_PERIOD_MILLIS,
+                LAUNCH_ROLE_HOLDER_MAX_RETRIES,
+                ROLE_HOLDER_UPDATE_MAX_RETRIES);
+        mCanLaunchRoleHolderUpdater = true;
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> mHandler = new Handler(Looper.myLooper()));
+        mViewModel = createViewModel();
+        mEvents = subscribeToViewModelEvents();
+    }
+
+    @Test
+    public void tryStartRoleHolderUpdater_launchUpdater_works() {
+        mViewModel.tryStartActivity();
+        blockUntilNextUiThreadCycle();
+
+        assertThat(mEvents).containsExactly(LAUNCH_ROLE_HOLDER_UPDATER_EVENT);
+    }
+
+    @Test
+    public void tryStartRoleHolderUpdater_rescheduleLaunchUpdater_works() {
+        mTestConfig.launchRoleHolderMaxRetries = 2;
+        mCanLaunchRoleHolderUpdater = false;
+
+        mViewModel.tryStartActivity();
+        mCanLaunchRoleHolderUpdater = true;
+
+        pollForEvents(
+                mEvents,
+                WAITING_FOR_RETRY_EVENT,
+                LAUNCH_ROLE_HOLDER_UPDATER_EVENT);
+    }
+
+    @Test
+    public void tryStartRoleHolderUpdater_rescheduleLaunchUpdater_exceedsMaxRetryLimit_fails() {
+        mTestConfig.roleHolderUpdateMaxRetries = 1;
+        mCanLaunchRoleHolderUpdater = false;
+
+        mViewModel.tryStartActivity();
+
+        pollForEvents(
+                mEvents,
+                WAITING_FOR_RETRY_EVENT,
+                EXCEED_MAX_NUMBER_LAUNCH_RETRIES_EVENT);
+    }
+
+    @Test
+    public void stopLaunchRetries_works() {
+        mTestConfig.roleHolderUpdateMaxRetries = 1;
+        mCanLaunchRoleHolderUpdater = false;
+
+        mViewModel.tryStartActivity();
+        mViewModel.stopLaunchRetries();
+
+        pollForEvents(mEvents, WAITING_FOR_RETRY_EVENT);
+    }
+
+    private void pollForEvents(
+            Queue<ViewModelEvent> actualEvents,
+            ViewModelEvent... expectedEvents) {
+        for (ViewModelEvent nextExpectedEvent : expectedEvents) {
+            Poll.forValue("CapturedViewModelEvents", () -> actualEvents)
+                    .toMeet(actualEventsQueue -> !actualEventsQueue.isEmpty())
+                    .errorOnFail("Expected CapturedViewModelEvents to contain exactly "
+                            + Arrays.stream(expectedEvents)
+                            .map(Object::toString).collect(Collectors.joining()))
+                    .await();
+            assertThat(actualEvents.remove()).isEqualTo(nextExpectedEvent);
+        }
+        pollForNoEvent(actualEvents);
+    }
+
+    private void pollForNoEvent(Queue<ViewModelEvent> capturedViewModelEvents) {
+        // TODO(b/208237942): A pattern for testing that something does not happen
+        assertThat(Poll.forValue("CapturedViewModelEvents", () -> capturedViewModelEvents)
+                .toMeet(viewModelEvents -> !viewModelEvents.isEmpty())
+                .timeout(Duration.ofMillis(NO_EVENT_TIMEOUT_MILLIS))
+                .await())
+                .isEmpty();
+    }
+
+    private static LaunchActivityFailureEvent createExceedMaxNumberLaunchRetriesEvent() {
+        return new LaunchActivityFailureEvent(
+                REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES);
+    }
+
+    private static LaunchActivityWaitingForRetryEvent createWaitingForRetryEvent() {
+        return new LaunchActivityWaitingForRetryEvent();
+    }
+
+    private static LaunchActivityEvent createLaunchRoleHolderUpdaterEvent() {
+        return new LaunchActivityEvent(createUpdateDeviceManagementRoleHolderIntent());
+    }
+
+    private Queue<ViewModelEvent> subscribeToViewModelEvents() {
+        Queue<ViewModelEvent> capturedViewModelEvents = new ConcurrentLinkedQueue<>();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> mViewModel.observeViewModelEvents()
+                        .observeForever(capturedViewModelEvents::add));
+        return capturedViewModelEvents;
+    }
+
+    private void blockUntilNextUiThreadCycle() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {});
+    }
+
+    private RetryLaunchViewModel createViewModel() {
+        return new RetryLaunchViewModel(
+                (Application) mApplicationContext,
+                createUpdateDeviceManagementRoleHolderIntent(),
+                mHandler,
+                (context, intent) -> mCanLaunchRoleHolderUpdater,
+                mTestConfig);
+    }
+
+    private static Intent createUpdateDeviceManagementRoleHolderIntent() {
+        return new Intent(DevicePolicyManager.ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER)
+                .setPackage(TEST_DEVICE_MANAGEMENT_ROLE_HOLDER_UPDATER_PACKAGE_NAME);
+    }
+
+    private static final class TestConfig implements RetryLaunchViewModel.Config {
+        public int launchRoleHolderUpdaterPeriodMillis;
+        public int launchRoleHolderMaxRetries;
+        public int roleHolderUpdateMaxRetries;
+
+        TestConfig(
+                int launchRoleHolderUpdaterPeriodMillis,
+                int launchRoleHolderMaxRetries,
+                int roleHolderUpdateMaxRetries) {
+            this.launchRoleHolderUpdaterPeriodMillis = launchRoleHolderUpdaterPeriodMillis;
+            this.launchRoleHolderMaxRetries = launchRoleHolderMaxRetries;
+            this.roleHolderUpdateMaxRetries = roleHolderUpdateMaxRetries;
+        }
+
+        @Override
+        public long getLaunchActivityRetryMillis() {
+            return launchRoleHolderUpdaterPeriodMillis;
+        }
+
+        @Override
+        public int getLaunchActivityMaxRetries() {
+            return launchRoleHolderMaxRetries;
+        }
+
+    }
+}
diff --git a/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityControllerTest.java b/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityControllerTest.java
index 07b39f1..bd0fb1d 100644
--- a/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityControllerTest.java
+++ b/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityControllerTest.java
@@ -274,7 +274,7 @@
         mViewModel = new PreProvisioningViewModel(
                 mTimeLogger,
                 mMessageParser,
-                mEncryptionController);
+                mEncryptionController, new PreProvisioningViewModel.DefaultConfig());
 
         mController = createControllerWithRoleHolderUpdaterNotPresent();
 
diff --git a/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityTest.java b/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityTest.java
index f511cf9..d253c63 100644
--- a/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityTest.java
+++ b/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningActivityTest.java
@@ -105,7 +105,8 @@
                                 new PreProvisioningViewModel(
                                         new TimeLogger(activity, 0 /* category */),
                                         new MessageParser(activity),
-                                        TestUtils.createEncryptionController(activity)),
+                                        TestUtils.createEncryptionController(activity),
+                                        new PreProvisioningViewModel.DefaultConfig()),
                                 DisclaimersParserImpl::new,
                                 roleHolderHelper,
                                 roleHolderUpdaterHelper
diff --git a/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningViewModelTest.java b/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningViewModelTest.java
index d8a6d98..078a948 100644
--- a/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningViewModelTest.java
+++ b/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/PreProvisioningViewModelTest.java
@@ -60,7 +60,7 @@
         mViewModel = new PreProvisioningViewModel(
                 mTimeLogger,
                 messageParser,
-                mEncryptionController);
+                mEncryptionController, new PreProvisioningViewModel.DefaultConfig());
     }
 
     @Test
@@ -143,4 +143,20 @@
                 IllegalProvisioningArgumentException.class,
                 () -> mViewModel.loadParamsIfNecessary(invalidIntent));
     }
+
+    @Test
+    public void canRetryRoleHolderUpdate_noTries_works() {
+        mViewModel.incrementRoleHolderUpdateRetryCount();
+
+        assertThat(mViewModel.canRetryRoleHolderUpdate()).isTrue();
+    }
+
+    @Test
+    public void canRetryRoleHolderUpdate_threeTries_isFalse() {
+        mViewModel.incrementRoleHolderUpdateRetryCount();
+        mViewModel.incrementRoleHolderUpdateRetryCount();
+        mViewModel.incrementRoleHolderUpdateRetryCount();
+
+        assertThat(mViewModel.canRetryRoleHolderUpdate()).isFalse();
+    }
 }
diff --git a/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterViewModelTest.java b/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterViewModelTest.java
deleted file mode 100644
index 16c9ba1..0000000
--- a/tests/instrumentation/src/com/android/managedprovisioning/preprovisioning/RoleHolderUpdaterViewModelTest.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.managedprovisioning.preprovisioning;
-
-import static com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel.LaunchRoleHolderUpdaterFailureEvent.REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATER_LAUNCH_RETRIES;
-import static com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel.LaunchRoleHolderUpdaterFailureEvent.REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATE_RETRIES;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Application;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.bedstead.nene.utils.Poll;
-import com.android.managedprovisioning.common.DefaultPackageInstallChecker;
-import com.android.managedprovisioning.common.DeviceManagementRoleHolderUpdaterHelper;
-import com.android.managedprovisioning.common.RoleHolderUpdaterProvider;
-import com.android.managedprovisioning.common.Utils;
-import com.android.managedprovisioning.common.ViewModelEvent;
-import com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel.LaunchRoleHolderUpdaterEvent;
-import com.android.managedprovisioning.preprovisioning.RoleHolderUpdaterViewModel.LaunchRoleHolderUpdaterFailureEvent;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.time.Duration;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-
-@SmallTest
-@RunWith(JUnit4.class)
-public class RoleHolderUpdaterViewModelTest {
-    private static final int LAUNCH_ROLE_HOLDER_UPDATER_PERIOD_MILLIS = 100;
-    private static final int NO_EVENT_TIMEOUT_MILLIS = 200;
-    private static final int LAUNCH_ROLE_HOLDER_MAX_RETRIES = 1;
-    private static final int ROLE_HOLDER_UPDATE_MAX_RETRIES = 1;
-    private static final LaunchRoleHolderUpdaterEvent
-            LAUNCH_ROLE_HOLDER_UPDATER_EVENT = createLaunchRoleHolderUpdaterEvent();
-    private static final LaunchRoleHolderUpdaterFailureEvent
-            EXCEED_MAX_NUMBER_LAUNCH_RETRIES_EVENT = createExceedMaxNumberLaunchRetriesEvent();
-    private static final LaunchRoleHolderUpdaterFailureEvent
-            EXCEED_MAX_NUMBER_UPDATE_RETRIES_EVENT = createExceedMaxNumberUpdateRetriesEvent();
-
-    private final Context mApplicationContext = ApplicationProvider.getApplicationContext();
-    private Handler mHandler;
-    private TestConfig mTestConfig;
-    private boolean mCanLaunchRoleHolderUpdater = true;
-    private RoleHolderUpdaterViewModel mViewModel;
-    private Set<ViewModelEvent> mEvents;
-    private Utils mUtils = new Utils();
-
-    @Before
-    public void setUp() {
-        mTestConfig = new TestConfig(
-                LAUNCH_ROLE_HOLDER_UPDATER_PERIOD_MILLIS,
-                LAUNCH_ROLE_HOLDER_MAX_RETRIES,
-                ROLE_HOLDER_UPDATE_MAX_RETRIES);
-        mCanLaunchRoleHolderUpdater = true;
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> mHandler = new Handler(Looper.myLooper()));
-        mViewModel = createViewModel();
-        mEvents = subscribeToViewModelEvents();
-    }
-
-    @Test
-    public void tryStartRoleHolderUpdater_launchUpdater_works() {
-        mViewModel.tryStartRoleHolderUpdater();
-        blockUntilNextUiThreadCycle();
-
-        assertThat(mEvents).containsExactly(LAUNCH_ROLE_HOLDER_UPDATER_EVENT);
-    }
-
-    @Test
-    public void tryStartRoleHolderUpdater_exceedsMaxRetryLimit_fails() {
-        mTestConfig.launchRoleHolderMaxRetries = 1;
-
-        mViewModel.tryStartRoleHolderUpdater();
-        blockUntilNextUiThreadCycle();
-        mViewModel.tryStartRoleHolderUpdater();
-        blockUntilNextUiThreadCycle();
-
-        assertThat(mEvents)
-                .containsExactly(
-                        LAUNCH_ROLE_HOLDER_UPDATER_EVENT,
-                        EXCEED_MAX_NUMBER_UPDATE_RETRIES_EVENT);
-    }
-
-    @Test
-    public void tryStartRoleHolderUpdater_rescheduleLaunchUpdater_works() {
-        mTestConfig.launchRoleHolderMaxRetries = 2;
-        mCanLaunchRoleHolderUpdater = false;
-
-        mViewModel.tryStartRoleHolderUpdater();
-        mCanLaunchRoleHolderUpdater = true;
-
-        pollForEvent(mEvents, LAUNCH_ROLE_HOLDER_UPDATER_EVENT);
-    }
-
-    @Test
-    public void tryStartRoleHolderUpdater_rescheduleLaunchUpdater_exceedsMaxRetryLimit_fails() {
-        mTestConfig.roleHolderUpdateMaxRetries = 1;
-        mCanLaunchRoleHolderUpdater = false;
-
-        mViewModel.tryStartRoleHolderUpdater();
-
-        pollForEvent(mEvents, EXCEED_MAX_NUMBER_LAUNCH_RETRIES_EVENT);
-    }
-
-    @Test
-    public void stopLaunchRetries_works() {
-        mTestConfig.roleHolderUpdateMaxRetries = 1;
-        mCanLaunchRoleHolderUpdater = false;
-
-        mViewModel.tryStartRoleHolderUpdater();
-        mViewModel.stopLaunchRetries();
-
-        pollForNoEvent(mEvents);
-    }
-
-    private void pollForEvent(
-            Set<ViewModelEvent> capturedViewModelEvents, ViewModelEvent viewModelEvent) {
-        Poll.forValue("CapturedViewModelEvents", () -> capturedViewModelEvents)
-                .toMeet(viewModelEvents -> viewModelEvents.size() == 1
-                                && viewModelEvents.contains(viewModelEvent))
-                .errorOnFail("Expected CapturedViewModelEvents to contain only " + viewModelEvent)
-                .await();
-    }
-
-    private void pollForNoEvent(Set<ViewModelEvent> capturedViewModelEvents) {
-        // TODO(b/208237942): A pattern for testing that something does not happen
-        assertThat(Poll.forValue("CapturedViewModelEvents", () -> capturedViewModelEvents)
-                .toMeet(viewModelEvents -> !viewModelEvents.isEmpty())
-                .timeout(Duration.ofMillis(NO_EVENT_TIMEOUT_MILLIS))
-                .await())
-                .isEqualTo(Collections.emptySet());
-    }
-
-    private static LaunchRoleHolderUpdaterFailureEvent createExceedMaxNumberUpdateRetriesEvent() {
-        return new LaunchRoleHolderUpdaterFailureEvent(
-                REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATE_RETRIES);
-    }
-
-    private static LaunchRoleHolderUpdaterFailureEvent createExceedMaxNumberLaunchRetriesEvent() {
-        return new LaunchRoleHolderUpdaterFailureEvent(
-                REASON_EXCEEDED_MAXIMUM_NUMBER_UPDATER_LAUNCH_RETRIES);
-    }
-
-    private static LaunchRoleHolderUpdaterEvent createLaunchRoleHolderUpdaterEvent() {
-        Intent intent = new Intent(DevicePolicyManager.ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER);
-        return new LaunchRoleHolderUpdaterEvent(intent);
-    }
-
-    private Set<ViewModelEvent> subscribeToViewModelEvents() {
-        Set<ViewModelEvent> capturedViewModelEvents =
-                Collections.newSetFromMap(new ConcurrentHashMap<>());
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> mViewModel.observeViewModelEvents()
-                        .observeForever(capturedViewModelEvents::add));
-        return capturedViewModelEvents;
-    }
-
-    private void blockUntilNextUiThreadCycle() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {});
-    }
-
-    private RoleHolderUpdaterViewModel createViewModel() {
-        return new RoleHolderUpdaterViewModel(
-                (Application) mApplicationContext,
-                mHandler,
-                (context, intent) -> mCanLaunchRoleHolderUpdater,
-                mTestConfig,
-                new DeviceManagementRoleHolderUpdaterHelper(
-                        RoleHolderUpdaterProvider.DEFAULT.getPackageName(mApplicationContext),
-                        new DefaultPackageInstallChecker(mUtils)));
-    }
-
-    private static final class TestConfig implements RoleHolderUpdaterViewModel.Config {
-        public int launchRoleHolderUpdaterPeriodMillis;
-        public int launchRoleHolderMaxRetries;
-        public int roleHolderUpdateMaxRetries;
-
-        TestConfig(
-                int launchRoleHolderUpdaterPeriodMillis,
-                int launchRoleHolderMaxRetries,
-                int roleHolderUpdateMaxRetries) {
-            this.launchRoleHolderUpdaterPeriodMillis = launchRoleHolderUpdaterPeriodMillis;
-            this.launchRoleHolderMaxRetries = launchRoleHolderMaxRetries;
-            this.roleHolderUpdateMaxRetries = roleHolderUpdateMaxRetries;
-        }
-
-        @Override
-        public int getLaunchRoleHolderUpdaterPeriodMillis() {
-            return launchRoleHolderUpdaterPeriodMillis;
-        }
-
-        @Override
-        public int getLaunchRoleHolderMaxRetries() {
-            return launchRoleHolderMaxRetries;
-        }
-
-        @Override
-        public int getRoleHolderUpdateMaxRetries() {
-            return roleHolderUpdateMaxRetries;
-        }
-    }
-}