blob: 5633a8ade6157987808867efcadfea6ce05903f1 [file] [log] [blame]
/*
* 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.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;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.android.managedprovisioning.ManagedProvisioningBaseApplication;
import com.android.managedprovisioning.analytics.TimeLogger;
import com.android.managedprovisioning.common.IllegalProvisioningArgumentException;
import com.android.managedprovisioning.common.ProvisionLogger;
import com.android.managedprovisioning.model.ProvisioningParams;
import com.android.managedprovisioning.parser.MessageParser;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A {@link ViewModel} which maintains data related to preprovisioning.
*/
public final class PreProvisioningViewModel extends ViewModel {
static final int STATE_PREPROVISIONING_INITIALIZING = 1;
static final int STATE_PREPROVISIONING_INITIALIZED = 2;
static final int STATE_GETTING_PROVISIONING_MODE = 3;
static final int STATE_SHOWING_USER_CONSENT = 4;
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,
STATE_SHOWING_USER_CONSENT,
STATE_PROVISIONING_STARTED,
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,
Config config) {
mMessageParser = requireNonNull(messageParser);
mTimeLogger = requireNonNull(timeLogger);
mEncryptionController = requireNonNull(encryptionController);
mConfig = requireNonNull(config);
}
/**
* Updates state after provisioning has completed
*/
@MainThread
public void onReturnFromProvisioning() {
setState(STATE_PROVISIONING_FINALIZED);
}
/**
* Handles state when the admin-integrated flow was initiated
*/
@MainThread
public void onAdminIntegratedFlowInitiated() {
setState(STATE_GETTING_PROVISIONING_MODE);
}
/**
* Handles state when user consent is shown
*/
@MainThread
public void onShowUserConsent() {
setState(STATE_SHOWING_USER_CONSENT);
}
/**
* Handles state when provisioning is started after the user consent
*/
@MainThread
public void onProvisioningStartedAfterUserConsent() {
setState(STATE_PROVISIONING_STARTED);
}
/**
* Handles state when provisioning has initiated
*/
@MainThread
public void onProvisioningInitiated() {
setState(STATE_PREPROVISIONING_INITIALIZED);
}
/**
* Returns the state as a {@link LiveData}.
*/
LiveData<Integer> getState() {
return mState;
}
/**
* Loads the {@link ProvisioningParams} if they haven't been loaded before.
*
* @throws IllegalProvisioningArgumentException if the intent extras are invalid
*/
void loadParamsIfNecessary(Intent intent) throws IllegalProvisioningArgumentException {
if (mParams == null) {
mParams = loadProvisioningParams(intent);
}
}
/**
* Returns the {@link ProvisioningParams} associated with this provisioning session.
*/
ProvisioningParams getParams() {
if (mParams == null) {
throw new IllegalStateException("Trying to get params without loading them first. "
+ "Did you run loadParamsIfNecessary(Intent)?");
}
return mParams;
}
/**
* Replaces the cached {@link ProvisioningParams} with {@code params}.
*/
void updateParams(ProvisioningParams params) {
if (params == null) {
throw new IllegalArgumentException("Cannot update params to null.");
}
mParams = params;
}
/**
* Returns the cached {@link TimeLogger} instance.
*/
TimeLogger getTimeLogger() {
return mTimeLogger;
}
/**
* Returns the cached {@link EncryptionController} instance.
*/
EncryptionController getEncryptionController() {
return mEncryptionController;
}
private ProvisioningParams loadProvisioningParams(Intent intent)
throws IllegalProvisioningArgumentException {
return mMessageParser.parse(intent);
}
/**
* Sets the state which updates all the listening obervables.
* <p>This method must be called from the UI thread.
*/
@MainThread
private void setState(@PreProvisioningState int state) {
if (mState.getValue() != state) {
ProvisionLogger.logi(
"Setting preprovisioning state to " + state + ", previous state was "
+ mState.getValue());
mState.setValue(state);
} else {
ProvisionLogger.logi(
"Attempt to set preprovisioning state to the same state " + mState);
}
}
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,
Config config) {
mApplication = requireNonNull(application);
mConfig = requireNonNull(config);
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (!PreProvisioningViewModel.class.isAssignableFrom(modelClass)) {
throw new IllegalArgumentException("Invalid class for creating a "
+ "PreProvisioningViewModel: " + modelClass);
}
return (T) new PreProvisioningViewModel(
new TimeLogger(mApplication, PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS),
new MessageParser(mApplication),
mApplication.getEncryptionController(),
mConfig);
}
}
}