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;
- }
- }
-}