| /* |
| * 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.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. |
| * |
| * <p>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.</p> |
| */ |
| 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 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<Integer, Integer> PROVISIONING_MODE_TO_PROGRESS_LABEL = |
| Collections.unmodifiableMap(new HashMap<Integer, Integer>() {{ |
| put(PROVISIONING_MODE_WORK_PROFILE, |
| R.string.work_profile_provisioning_progress_label); |
| put(PROVISIONING_MODE_FULLY_MANAGED_DEVICE, |
| R.string.fully_managed_device_provisioning_progress_label); |
| put(PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE, |
| R.string.fully_managed_device_provisioning_progress_label); |
| put(PROVISIONING_MODE_FINANCED_DEVICE, R.string.just_a_sec); |
| put(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); |
| 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); |
| |
| // 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; |
| } |
| } |