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