blob: b5e509158930f66139528851bd1002c1f6d9ed18 [file] [log] [blame]
/*
* Copyright 2019, 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.finalization;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static com.android.managedprovisioning.finalization.FinalizationController.PROVISIONING_FINALIZED_RESULT_CHILD_ACTIVITY_LAUNCHED;
import static com.android.managedprovisioning.finalization.FinalizationController.PROVISIONING_FINALIZED_RESULT_WAIT_FOR_WORK_PROFILE_AVAILABLE;
import static com.android.managedprovisioning.provisioning.Constants.PROVISIONING_SERVICE_INTENT;
import android.app.Activity;
import android.app.BackgroundServiceStartNotAllowedException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.StrictMode;
import android.os.UserHandle;
import android.os.UserManager;
import android.view.WindowManager;
import com.android.managedprovisioning.ManagedProvisioningBaseApplication;
import com.android.managedprovisioning.common.ProvisionLogger;
import com.android.managedprovisioning.common.TransitionHelper;
/**
* Instances of this base class manage interactions with a Device Policy Controller app after it has
* been set up as either a Device Owner or a Profile Owner.
*
* Once we are sure that the DPC app has had an opportunity to run its own setup activities, we
* record in the system that provisioning has been finalized.
*
* Different instances of this class will be tailored to run at different points in the Setup
* Wizard user flows.
*/
public abstract class FinalizationActivityBase extends Activity {
private static final String CONTROLLER_STATE_KEY = "controller_state";
private final TransitionHelper mTransitionHelper;
private FinalizationController mFinalizationController;
private boolean mIsReceiverRegistered;
private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!ACTION_USER_UNLOCKED.equals(intent.getAction())) {
return;
}
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, /* defaultValue= */ -1);
UserManager userManager = getSystemService(UserManager.class);
if (!userManager.isManagedProfile(userId)) {
return;
}
tryFinalizeProvisioning();
unregisterUserUnlockedReceiver();
}
};
FinalizationActivityBase(TransitionHelper transitionHelper) {
mTransitionHelper = transitionHelper;
}
@Override
public final void onCreate(Bundle savedInstanceState) {
// TODO(b/123987694): Investigate use of buffered i/o for provisioning params file
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.permitUnbufferedIo()
.build());
mTransitionHelper.applyContentScreenTransitions(this);
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mFinalizationController = createFinalizationController();
if (mFinalizationController.shouldKeepScreenOn()) {
ManagedProvisioningBaseApplication application =
(ManagedProvisioningBaseApplication) getApplication();
application.markKeepScreenOn();
application.maybeKeepScreenOn(this);
}
if (savedInstanceState != null) {
final Bundle controllerState = savedInstanceState.getBundle(CONTROLLER_STATE_KEY);
if (controllerState != null) {
mFinalizationController.restoreInstanceState(controllerState);
}
// If savedInstanceState is not null, we already executed the logic below, so don't do
// it again now. We likely just need to wait for a child activity to complete, so we
// can execute an onActivityResult() callback before finishing this activity.
return;
}
tryFinalizeProvisioning();
}
@Override
protected void onStart() {
super.onStart();
try {
getApplicationContext().startService(PROVISIONING_SERVICE_INTENT);
} catch (BackgroundServiceStartNotAllowedException e) {
ProvisionLogger.loge(e);
}
}
protected TransitionHelper getTransitionHelper() {
return mTransitionHelper;
}
private void tryFinalizeProvisioning() {
// Register receiver first to avoid race condition when user becomes unlocked after
// the user unlocked check. This will be unregistered if we don't need it
mIsReceiverRegistered = false;
registerUserUnlockedReceiver();
mFinalizationController.provisioningFinalized();
final int result = mFinalizationController.getProvisioningFinalizedResult();
if (PROVISIONING_FINALIZED_RESULT_CHILD_ACTIVITY_LAUNCHED == result) {
onFinalizationCompletedWithChildActivityLaunched();
} else if (PROVISIONING_FINALIZED_RESULT_WAIT_FOR_WORK_PROFILE_AVAILABLE != result) {
if (FinalizationController.PROVISIONING_FINALIZED_RESULT_SKIPPED != result) {
mFinalizationController.commitFinalizedState();
}
setResult(RESULT_OK);
getTransitionHelper().finishActivity(this);
}
if (PROVISIONING_FINALIZED_RESULT_WAIT_FOR_WORK_PROFILE_AVAILABLE != result) {
unregisterUserUnlockedReceiver();
}
}
private void registerUserUnlockedReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_USER_UNLOCKED);
registerReceiverAsUser(
mUserUnlockedReceiver,
UserHandle.ALL,
filter,
/* broadcastPermission= */ null,
/* scheduler= */ null);
mIsReceiverRegistered = true;
}
private void unregisterUserUnlockedReceiver() {
if (!mIsReceiverRegistered) {
return;
}
unregisterReceiver(mUserUnlockedReceiver);
mIsReceiverRegistered = false;
}
@Override
protected final void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
final Bundle controllerState = new Bundle();
outState.putBundle(CONTROLLER_STATE_KEY, controllerState);
mFinalizationController.saveInstanceState(controllerState);
}
@Override
public final void onDestroy() {
mFinalizationController.activityDestroyed(isFinishing());
unregisterUserUnlockedReceiver();
getApplicationContext().stopService(PROVISIONING_SERVICE_INTENT);
super.onDestroy();
}
/**
* Return the finalization controller instance that was constructed in {@link #onCreate}.
*/
protected FinalizationController getFinalizationController() {
return mFinalizationController;
}
/**
* Construct the correct subtype of FinalizationController.
*/
protected abstract FinalizationController createFinalizationController();
/**
* Hook for code that should run when the controller has launched a child activity.
*/
protected abstract void onFinalizationCompletedWithChildActivityLaunched();
}