1 /*
2  * Copyright (C) 2022 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.networkconnection;
18 
19 import static java.util.Objects.requireNonNull;
20 
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.UserHandle;
24 
25 import androidx.annotation.NonNull;
26 import androidx.lifecycle.MutableLiveData;
27 import androidx.lifecycle.ViewModel;
28 import androidx.lifecycle.ViewModelProvider;
29 
30 import com.android.managedprovisioning.common.ErrorWrapper;
31 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException;
32 import com.android.managedprovisioning.common.ProvisionLogger;
33 import com.android.managedprovisioning.common.SettingsFacade;
34 import com.android.managedprovisioning.common.SharedPreferences;
35 import com.android.managedprovisioning.common.Utils;
36 import com.android.managedprovisioning.model.ProvisioningParams;
37 import com.android.managedprovisioning.parser.MessageParser;
38 import com.android.managedprovisioning.provisioning.ProvisioningControllerCallback;
39 import com.android.managedprovisioning.provisioning.ProvisioningManagerHelper;
40 import com.android.managedprovisioning.task.TaskFactory;
41 import com.android.managedprovisioning.util.LazyStringResource;
42 
43 /**
44  * A {@link ViewModel} which manages the state for the network connection establishing screen.
45  */
46 public final class EstablishNetworkConnectionViewModel extends ViewModel implements
47         ProvisioningControllerCallback {
48 
49     public static final int STATE_IDLE = 1;
50     public static final int STATE_CONNECTING = 2;
51     public static final int STATE_SHOW_NETWORK_PICKER = 3;
52     public static final int STATE_ERROR = 4;
53     public static final int STATE_CONNECTED = 5;
54 
55     private final ProvisioningManagerHelper mProvisioningManagerHelper =
56             new ProvisioningManagerHelper();
57     private final MutableLiveData<Integer> mState = new MutableLiveData<>(STATE_IDLE);
58     private final Utils mUtils;
59     private final SettingsFacade mSettingsFacade;
60     private final SharedPreferences mSharedPreferences;
61     private ErrorWrapper mErrorWrapper;
62 
EstablishNetworkConnectionViewModel( Utils utils, SettingsFacade settingsFacade, SharedPreferences sharedPreferences)63     public EstablishNetworkConnectionViewModel(
64             Utils utils,
65             SettingsFacade settingsFacade,
66             SharedPreferences sharedPreferences) {
67         mUtils = requireNonNull(utils);
68         mSettingsFacade = requireNonNull(settingsFacade);
69         mSharedPreferences = requireNonNull(sharedPreferences);
70     }
71 
72     /**
73      * Parses and returns the provisioning extras contained in {@code intent}.
74      */
parseExtras(Context context, Intent intent)75     public ProvisioningParams parseExtras(Context context, Intent intent) {
76         requireNonNull(context);
77         requireNonNull(intent);
78         ProvisioningParams params = null;
79         try {
80             params = new MessageParser(context, mUtils).parse(intent);
81         } catch (IllegalProvisioningArgumentException e) {
82             ProvisionLogger.loge("Error parsing intent extras", e);
83         }
84         return params;
85     }
86 
87     /**
88      * Returns {@link MutableLiveData} describing the state.
89      */
observeState()90     public MutableLiveData<Integer> observeState() {
91         return mState;
92     }
93 
94     /**
95      * Connects to wifi or mobile data if needed.
96      */
connectToNetwork(Context context, ProvisioningParams params)97     public void connectToNetwork(Context context,
98             ProvisioningParams params) {
99         requireNonNull(context);
100         requireNonNull(params);
101         mSharedPreferences.setIsEstablishNetworkConnectionRun(true);
102         var networkCapabilities = mUtils.getActiveNetworkCapabilities(context);
103         if (networkCapabilities != null
104                 && mUtils.isNetworkConnectedToInternetViaEthernet(networkCapabilities)) {
105             updateState(STATE_CONNECTED);
106             return;
107         } else if (params.wifiInfo == null
108                 && !params.useMobileData) {
109             updateState(STATE_SHOW_NETWORK_PICKER);
110             return;
111         }
112         mProvisioningManagerHelper.startNewProvisioningLocked(
113                 EstablishNetworkConnectionController.createInstance(
114                         context,
115                         params,
116                         UserHandle.USER_SYSTEM,
117                         this,
118                         mUtils,
119                         mSettingsFacade,
120                         new TaskFactory()));
121         updateState(STATE_CONNECTING);
122     }
123 
124     @Override
cleanUpCompleted()125     public void cleanUpCompleted() {
126         mProvisioningManagerHelper.clearResourcesLocked();
127     }
128 
129     @Override
provisioningTasksCompleted()130     public void provisioningTasksCompleted() {
131         updateState(STATE_CONNECTED);
132     }
133 
134     @Override
error(int dialogTitleId, int errorMessageId, boolean factoryResetRequired)135     public void error(int dialogTitleId, int errorMessageId, boolean factoryResetRequired) {
136         mErrorWrapper = new ErrorWrapper(dialogTitleId, LazyStringResource.of(errorMessageId),
137                 factoryResetRequired);
138         updateState(STATE_ERROR);
139     }
140 
141     @Override
error(int dialogTitleId, String errorMessage, boolean factoryResetRequired)142     public void error(int dialogTitleId, String errorMessage, boolean factoryResetRequired) {
143         mErrorWrapper = new ErrorWrapper(dialogTitleId, LazyStringResource.of(errorMessage),
144                 factoryResetRequired);
145         updateState(STATE_ERROR);
146     }
147 
updateState(int stateDownloading)148     private void updateState(int stateDownloading) {
149         if (stateDownloading != STATE_ERROR) {
150             mErrorWrapper = null;
151         }
152         mState.postValue(stateDownloading);
153     }
154 
155     /**
156      * Returns an {@link ErrorWrapper} which describes the last error that happened. This will
157      * only be non-{@code null} if {@link #observeState()} returns {@link #STATE_ERROR}.
158      */
getError()159     public ErrorWrapper getError() {
160         return mErrorWrapper;
161     }
162 
163     @Override
preFinalizationCompleted()164     public void preFinalizationCompleted() {
165     }
166 
167     static class EstablishNetworkConnectionViewModelFactory implements ViewModelProvider.Factory {
168         private final Utils mUtils;
169         private final SettingsFacade mSettingsFacade;
170         private final SharedPreferences mSharedPreferences;
171 
EstablishNetworkConnectionViewModelFactory( Utils utils, SettingsFacade settingsFacade, SharedPreferences sharedPreferences)172         EstablishNetworkConnectionViewModelFactory(
173                 Utils utils,
174                 SettingsFacade settingsFacade,
175                 SharedPreferences sharedPreferences) {
176             mUtils = requireNonNull(utils);
177             mSettingsFacade = requireNonNull(settingsFacade);
178             mSharedPreferences = requireNonNull(sharedPreferences);
179         }
180 
181         @NonNull
182         @Override
183         @SuppressWarnings("unchecked")
create(@onNull Class<T> modelClass)184         public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
185             if (!EstablishNetworkConnectionViewModel.class.isAssignableFrom(modelClass)) {
186                 throw new IllegalArgumentException("Invalid class for creating a "
187                         + "EstablishNetworkConnectionViewModel: " + modelClass);
188             }
189             return (T) new EstablishNetworkConnectionViewModel(
190                     mUtils, mSettingsFacade, mSharedPreferences);
191         }
192     }
193 }
194