1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.managedprovisioning.preprovisioning; 18 19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS; 20 21 import android.annotation.IntDef; 22 import android.annotation.MainThread; 23 import android.content.Intent; 24 25 import androidx.lifecycle.LiveData; 26 import androidx.lifecycle.MutableLiveData; 27 import androidx.lifecycle.ViewModel; 28 import androidx.lifecycle.ViewModelProvider; 29 30 import com.android.managedprovisioning.ManagedProvisioningBaseApplication; 31 import com.android.managedprovisioning.analytics.TimeLogger; 32 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException; 33 import com.android.managedprovisioning.common.ProvisionLogger; 34 import com.android.managedprovisioning.model.ProvisioningParams; 35 import com.android.managedprovisioning.parser.MessageParser; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.Objects; 40 41 /** 42 * A {@link ViewModel} which maintains data related to preprovisioning. 43 */ 44 public final class PreProvisioningViewModel extends ViewModel { 45 static final int STATE_PREPROVISIONING_INITIALIZING = 1; 46 static final int STATE_PREPROVISIONING_INITIALIZED = 2; 47 static final int STATE_GETTING_PROVISIONING_MODE = 3; 48 static final int STATE_SHOWING_USER_CONSENT = 4; 49 static final int STATE_PROVISIONING_STARTED = 5; 50 static final int STATE_PROVISIONING_FINALIZED = 6; 51 52 @Retention(RetentionPolicy.SOURCE) 53 @IntDef({STATE_PREPROVISIONING_INITIALIZING, 54 STATE_GETTING_PROVISIONING_MODE, 55 STATE_SHOWING_USER_CONSENT, 56 STATE_PROVISIONING_STARTED, 57 STATE_PROVISIONING_FINALIZED}) 58 private @interface PreProvisioningState {} 59 60 private ProvisioningParams mParams; 61 private final MessageParser mMessageParser; 62 private final TimeLogger mTimeLogger; 63 private final EncryptionController mEncryptionController; 64 private final MutableLiveData<Integer> mState = 65 new MutableLiveData<>(STATE_PREPROVISIONING_INITIALIZING); 66 PreProvisioningViewModel( TimeLogger timeLogger, MessageParser messageParser, EncryptionController encryptionController)67 PreProvisioningViewModel( 68 TimeLogger timeLogger, 69 MessageParser messageParser, 70 EncryptionController encryptionController) { 71 mMessageParser = Objects.requireNonNull(messageParser); 72 mTimeLogger = Objects.requireNonNull(timeLogger); 73 mEncryptionController = Objects.requireNonNull(encryptionController); 74 } 75 76 /** 77 * Updates state after provisioning has completed 78 */ 79 @MainThread onReturnFromProvisioning()80 public void onReturnFromProvisioning() { 81 setState(STATE_PROVISIONING_FINALIZED); 82 } 83 84 /** 85 * Handles state when the admin-integrated flow was initiated 86 */ 87 @MainThread onAdminIntegratedFlowInitiated()88 public void onAdminIntegratedFlowInitiated() { 89 setState(STATE_GETTING_PROVISIONING_MODE); 90 } 91 92 /** 93 * Handles state when user consent is shown 94 */ 95 @MainThread onShowUserConsent()96 public void onShowUserConsent() { 97 setState(STATE_SHOWING_USER_CONSENT); 98 } 99 100 /** 101 * Handles state when provisioning is started after the user consent 102 */ 103 @MainThread onProvisioningStartedAfterUserConsent()104 public void onProvisioningStartedAfterUserConsent() { 105 setState(STATE_PROVISIONING_STARTED); 106 } 107 108 /** 109 * Handles state when provisioning has initiated 110 */ 111 @MainThread onProvisioningInitiated()112 public void onProvisioningInitiated() { 113 setState(STATE_PREPROVISIONING_INITIALIZED); 114 } 115 116 /** 117 * Returns the state as a {@link LiveData}. 118 */ getState()119 LiveData<Integer> getState() { 120 return mState; 121 } 122 123 /** 124 * Loads the {@link ProvisioningParams} if they haven't been loaded before. 125 * 126 * @throws IllegalProvisioningArgumentException if the intent extras are invalid 127 */ loadParamsIfNecessary(Intent intent)128 void loadParamsIfNecessary(Intent intent) throws IllegalProvisioningArgumentException { 129 if (mParams == null) { 130 mParams = loadProvisioningParams(intent); 131 } 132 } 133 134 /** 135 * Returns the {@link ProvisioningParams} associated with this provisioning session. 136 */ getParams()137 ProvisioningParams getParams() { 138 if (mParams == null) { 139 throw new IllegalStateException("Trying to get params without loading them first. " 140 + "Did you run loadParamsIfNecessary(Intent)?"); 141 } 142 return mParams; 143 } 144 145 /** 146 * Replaces the cached {@link ProvisioningParams} with {@code params}. 147 */ updateParams(ProvisioningParams params)148 void updateParams(ProvisioningParams params) { 149 if (params == null) { 150 throw new IllegalArgumentException("Cannot update params to null."); 151 } 152 mParams = params; 153 } 154 155 /** 156 * Returns the cached {@link TimeLogger} instance. 157 */ getTimeLogger()158 TimeLogger getTimeLogger() { 159 return mTimeLogger; 160 } 161 162 /** 163 * Returns the cached {@link EncryptionController} instance. 164 */ getEncryptionController()165 EncryptionController getEncryptionController() { 166 return mEncryptionController; 167 } 168 loadProvisioningParams(Intent intent)169 private ProvisioningParams loadProvisioningParams(Intent intent) 170 throws IllegalProvisioningArgumentException { 171 return mMessageParser.parse(intent); 172 } 173 174 /** 175 * Sets the state which updates all the listening obervables. 176 * <p>This method must be called from the UI thread. 177 */ 178 @MainThread setState(@reProvisioningState int state)179 private void setState(@PreProvisioningState int state) { 180 if (mState.getValue() != state) { 181 ProvisionLogger.logi( 182 "Setting preprovisioning state to " + state + ", previous state was " 183 + mState.getValue()); 184 mState.setValue(state); 185 } else { 186 ProvisionLogger.logi( 187 "Attempt to set preprovisioning state to the same state " + mState); 188 } 189 } 190 191 static class PreProvisioningViewModelFactory implements ViewModelProvider.Factory { 192 private final ManagedProvisioningBaseApplication mApplication; 193 PreProvisioningViewModelFactory(ManagedProvisioningBaseApplication application)194 PreProvisioningViewModelFactory(ManagedProvisioningBaseApplication application) { 195 mApplication = application; 196 } 197 198 @Override create(Class<T> modelClass)199 public <T extends ViewModel> T create(Class<T> modelClass) { 200 if (!PreProvisioningViewModel.class.isAssignableFrom(modelClass)) { 201 throw new IllegalArgumentException("Invalid class for creating a " 202 + "PreProvisioningViewModel: " + modelClass); 203 } 204 return (T) new PreProvisioningViewModel( 205 new TimeLogger(mApplication, PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS), 206 new MessageParser(mApplication), 207 mApplication.getEncryptionController()); 208 } 209 } 210 } 211