/* * Copyright 2016, 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.provisioning; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS; import static com.android.internal.util.Preconditions.checkNotNull; import static java.util.Objects.requireNonNull; import android.annotation.IntDef; import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; import android.view.ViewGroup; import androidx.annotation.VisibleForTesting; import com.android.managedprovisioning.ManagedProvisioningScreens; import com.android.managedprovisioning.R; import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences; import com.android.managedprovisioning.common.PolicyComplianceUtils; import com.android.managedprovisioning.common.ProvisionLogger; import com.android.managedprovisioning.common.SettingsFacade; import com.android.managedprovisioning.common.ThemeHelper; import com.android.managedprovisioning.common.ThemeHelper.DefaultNightModeChecker; import com.android.managedprovisioning.common.ThemeHelper.DefaultSetupWizardBridge; import com.android.managedprovisioning.common.Utils; import com.android.managedprovisioning.finalization.PreFinalizationController; import com.android.managedprovisioning.finalization.UserProvisioningStateHelper; import com.android.managedprovisioning.model.ProvisioningParams; import com.android.managedprovisioning.provisioning.TransitionAnimationHelper.TransitionAnimationCallback; import com.android.managedprovisioning.provisioning.TransitionAnimationHelper.TransitionAnimationStateManager; import com.airbnb.lottie.LottieAnimationView; import com.google.android.setupcompat.logging.ScreenKey; import com.google.android.setupcompat.logging.SetupMetric; import com.google.android.setupcompat.logging.SetupMetricsLogger; import com.google.android.setupcompat.util.WizardManagerHelper; import com.google.android.setupdesign.util.Partner; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Progress activity shown whilst provisioning is ongoing. * *

This activity registers for updates of the provisioning process from the * {@link ProvisioningManager}. It shows progress updates as provisioning progresses and handles * showing of cancel and error dialogs.

*/ public class ProvisioningActivity extends AbstractProvisioningActivity implements TransitionAnimationCallback, TransitionAnimationStateManager { private static final int RESULT_CODE_COMPLETE_DEVICE_FINANCE = 121; /* * Returned after the work profile has been completed. Note this is before launching the DPC. */ @VisibleForTesting static final int RESULT_CODE_WORK_PROFILE_CREATED = 122; /* * Returned after the device owner has been set. Note this is before launching the DPC. */ @VisibleForTesting static final int RESULT_CODE_DEVICE_OWNER_SET = 123; static final int PROVISIONING_MODE_WORK_PROFILE = 1; static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 2; static final int PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE = 3; static final int PROVISIONING_MODE_FINANCED_DEVICE = 4; static final int PROVISIONING_MODE_WORK_PROFILE_ON_ORG_OWNED_DEVICE = 5; private static final String SETUP_METRIC_PROVISIONING_SCREEN_NAME = "ShowProvisioningScreens"; private static String SETUP_METRIC_PROVISIONING_ENDED_NAME = "ProvisioningEnded"; private ViewGroup mButtonFooterContainer; @IntDef(prefix = { "PROVISIONING_MODE_" }, value = { PROVISIONING_MODE_WORK_PROFILE, PROVISIONING_MODE_FULLY_MANAGED_DEVICE, PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE, PROVISIONING_MODE_FINANCED_DEVICE, PROVISIONING_MODE_WORK_PROFILE_ON_ORG_OWNED_DEVICE }) @Retention(RetentionPolicy.SOURCE) @interface ProvisioningMode {} private static final Map PROVISIONING_MODE_TO_PROGRESS_LABEL = Map.of( PROVISIONING_MODE_WORK_PROFILE, R.string.work_profile_provisioning_progress_label, PROVISIONING_MODE_FULLY_MANAGED_DEVICE, R.string.fully_managed_device_provisioning_progress_label, PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE, R.string.fully_managed_device_provisioning_progress_label, PROVISIONING_MODE_FINANCED_DEVICE, R.string.just_a_sec, PROVISIONING_MODE_WORK_PROFILE_ON_ORG_OWNED_DEVICE, R.string.work_profile_provisioning_progress_label); private UserProvisioningStateHelper mUserProvisioningStateHelper; private PolicyComplianceUtils mPolicyComplianceUtils; private ProvisioningManager mProvisioningManager; private ProvisioningActivityBridge mBridge; public ProvisioningActivity() { this( /* provisioningManager */ null, // defined in getProvisioningManager() new Utils(), /* userProvisioningStateHelper */ null, // defined in onCreate() new PolicyComplianceUtils(), new SettingsFacade(), new ThemeHelper(new DefaultNightModeChecker(), new DefaultSetupWizardBridge())); } @VisibleForTesting public ProvisioningActivity(ProvisioningManager provisioningManager, Utils utils, UserProvisioningStateHelper userProvisioningStateHelper, PolicyComplianceUtils policyComplianceUtils, SettingsFacade settingsFacade, ThemeHelper themeHelper) { super(utils, settingsFacade, themeHelper); mProvisioningManager = provisioningManager; mUserProvisioningStateHelper = userProvisioningStateHelper; mPolicyComplianceUtils = checkNotNull(policyComplianceUtils); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupMetricScreenName = SETUP_METRIC_PROVISIONING_SCREEN_NAME; mScreenKey = ScreenKey.of(setupMetricScreenName, this); SetupMetricsLogger.logMetrics(this, mScreenKey, SetupMetric.ofImpression(setupMetricScreenName)); mBridge = createBridge(); mBridge.initiateUi(/* activity= */ this); // assign this Activity as the view store owner to access saved state and receive updates getProvisioningManager().setViewModelStoreOwner(this); if (mUserProvisioningStateHelper == null) { mUserProvisioningStateHelper = new UserProvisioningStateHelper(this); } if (mState == STATE_PROVISIONING_FINALIZED) { updateProvisioningFinalizedScreen(); } writeSharedPreferences(); } private void writeSharedPreferences() { ManagedProvisioningSharedPreferences sharedPreferences = new ManagedProvisioningSharedPreferences(this); sharedPreferences.writeNavigationBarColor(getWindow().getNavigationBarColor()); sharedPreferences.writeNavigationBarDividerColor( getWindow().getNavigationBarDividerColor()); sharedPreferences.writeTextPrimaryColor(mUtils.getTextPrimaryColor(this)); sharedPreferences.writeTextSecondaryColor(mUtils.getTextSecondaryColor(this)); sharedPreferences.writeBackgroundColor(mUtils.getBackgroundColor(this)); sharedPreferences.writeAccentColor(mUtils.getAccentColor(this)); sharedPreferences.writeNotificationBackgroundColor( Partner.getColor(this, R.color.setup_notification_bg_color)); } protected ProvisioningActivityBridge createBridge() { return ProvisioningActivityBridgeImpl.builder() .setParams(mParams) .setUtils(mUtils) .setProvisioningMode(getProvisioningMode()) .setProvisioningManager(getProvisioningManager()) .setTransitionAnimationCallback(this) .setInitializeLayoutParamsConsumer( ProvisioningActivity.this::initializeLayoutParams) .setShouldSkipEducationScreens(shouldSkipEducationScreens()) .setProgressLabelResId(getProgressLabelResId()) .setBridgeCallbacks(createCallbacks()) .setStateManager(this) .build(); } protected Integer getProgressLabelResId() { return PROVISIONING_MODE_TO_PROGRESS_LABEL.get(getProvisioningMode()); } protected final ProvisioningActivityBridgeCallbacks createCallbacks() { return new ProvisioningActivityBridgeCallbacks() { @Override public void onNextButtonClicked() { ProvisioningActivity.this.onNextButtonClicked(); } @Override public void onAbortButtonClicked() { ProvisioningActivity.this.onAbortButtonClicked(); } @Override public boolean isProvisioningFinalized() { return mState == STATE_PROVISIONING_FINALIZED; } }; } @Override protected ProvisioningManager getProvisioningManager() { if (mProvisioningManager == null) { mProvisioningManager = ProvisioningManager.getInstance(this); } return mProvisioningManager; } @VisibleForTesting protected void setProvisioningManager(ProvisioningManager provisioningManager) { mProvisioningManager = requireNonNull(provisioningManager); } @Override public void preFinalizationCompleted() { if (mState == STATE_PROVISIONING_COMPLETED || mState == STATE_PROVISIONING_FINALIZED) { return; } if (!validatePolicyComplianceExists()) { ProvisionLogger.loge("POLICY_COMPLIANCE handler not implemented by the admin app."); error(R.string.cant_set_up_device, R.string.contact_your_admin_for_help, /* resetRequired */ mParams.isOrganizationOwnedProvisioning); return; } ProvisionLogger.logi("ProvisioningActivity pre-finalization completed"); // TODO(183094412): Decouple state from AbstractProvisioningActivity mState = STATE_PROVISIONING_COMPLETED; if (shouldSkipEducationScreens() || mBridge.shouldShowButtonsWhenPreProvisioningCompletes()) { updateProvisioningFinalizedScreen(); } } // Enforces DPCs to implement the POLICY_COMPLIANCE handler for NFC and financed device // provisioning, since we no longer set up the DPC on setup wizard's exit procedure. // No need to verify it for the other flows, as that was already done earlier. // TODO(b/177849035): Remove financed device-specific logic private boolean validatePolicyComplianceExists() { if (!mUtils.isFinancedDeviceAction(mParams.provisioningAction)) { return true; } return mPolicyComplianceUtils.isPolicyComplianceActivityResolvableForManagedUser( this, mParams, mUtils); } protected final void updateProvisioningFinalizedScreen() { mBridge.onProvisioningFinalized(/* activity= */ this); SetupMetricsLogger.logMetrics(this, mScreenKey, SetupMetric.ofWaitingEnd(SETUP_METRIC_PROVISIONING_ENDED_NAME)); // TODO(183094412): Decouple state from AbstractProvisioningActivity mState = STATE_PROVISIONING_FINALIZED; } @VisibleForTesting protected void onNextButtonClicked() { markDeviceManagementEstablishedAndFinish(); } @VisibleForTesting protected void onAbortButtonClicked() { final Intent intent = new Intent(this, getActivityForScreen(ManagedProvisioningScreens.RESET_AND_RETURN_DEVICE)); WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); getTransitionHelper().startActivityWithTransition(this, intent); } private void finishActivity() { if (mParams.provisioningAction.equals(ACTION_PROVISION_FINANCED_DEVICE)) { setResult(RESULT_CODE_COMPLETE_DEVICE_FINANCE); } else { setResult(Activity.RESULT_OK); } getTransitionHelper().finishActivity(this); } private void markDeviceManagementEstablishedAndFinish() { new PreFinalizationController(this, mUserProvisioningStateHelper) .deviceManagementEstablished(mParams); if (mParams.flowType == ProvisioningParams.FLOW_TYPE_ADMIN_INTEGRATED) { if (mUtils.isProfileOwnerAction(mParams.provisioningAction)) { setResult(RESULT_CODE_WORK_PROFILE_CREATED); } else if (mUtils.isDeviceOwnerAction(mParams.provisioningAction)) { setResult(RESULT_CODE_DEVICE_OWNER_SET); } else if (mUtils.isFinancedDeviceAction(mParams.provisioningAction)) { setResult(RESULT_CODE_COMPLETE_DEVICE_FINANCE); } getTransitionHelper().finishActivity(this); } else { finishActivity(); } } @Override protected int getMetricsCategory() { return PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS; } @Override protected void decideCancelProvisioningDialog() { // TODO(b/213306538): Improve behaviour when cancelling BYOD mid-provisioning if (!mParams.isOrganizationOwnedProvisioning) { return; } if (getUtils().isDeviceOwnerAction(mParams.provisioningAction) || mParams.isOrganizationOwnedProvisioning) { showCancelProvisioningDialog(/* resetRequired = */true); } else { showCancelProvisioningDialog(/* resetRequired = */false); } } @Override protected void onStart() { super.onStart(); mBridge.onStart(this); } @Override protected void onStop() { super.onStop(); mBridge.onStop(); // remove this Activity as the view store owner to avoid memory leaks if (isFinishing()) { getProvisioningManager().clearViewModelStoreOwner(); } } @Override public void onAllTransitionsShown() { if (mState == STATE_PROVISIONING_COMPLETED) { updateProvisioningFinalizedScreen(); } } @Override public void onAnimationSetup(LottieAnimationView animationView) { getThemeHelper().setupAnimationDynamicColors(this, animationView, getIntent()); } @Override public void saveState(TransitionAnimationHelper.TransitionAnimationState state) { getProvisioningManager().saveTransitionAnimationState(state); } @Override public TransitionAnimationHelper.TransitionAnimationState restoreState() { return getProvisioningManager().restoreTransitionAnimationState(); } @Override protected boolean isWaitingScreen() { return shouldSkipEducationScreens(); } protected @ProvisioningMode int getProvisioningMode() { int provisioningMode = 0; final boolean isProfileOwnerAction = mUtils.isProfileOwnerAction(mParams.provisioningAction); if (isProfileOwnerAction) { if (getSystemService(DevicePolicyManager.class).isDeviceManaged()) { provisioningMode = PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE; } else if (mParams.isOrganizationOwnedProvisioning) { provisioningMode = PROVISIONING_MODE_WORK_PROFILE_ON_ORG_OWNED_DEVICE; } else { provisioningMode = PROVISIONING_MODE_WORK_PROFILE; } } else if (mUtils.isDeviceOwnerAction(mParams.provisioningAction)) { provisioningMode = PROVISIONING_MODE_FULLY_MANAGED_DEVICE; } else if (mUtils.isFinancedDeviceAction(mParams.provisioningAction)) { provisioningMode = PROVISIONING_MODE_FINANCED_DEVICE; } return provisioningMode; } protected boolean shouldSkipEducationScreens() { return mParams.skipEducationScreens || getProvisioningMode() == PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE || getProvisioningMode() == PROVISIONING_MODE_FINANCED_DEVICE; } }