• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.settings.wifi.dpp;
18 
19 import static android.os.UserManager.DISALLOW_ADD_WIFI_CONFIG;
20 
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.net.Uri;
25 import android.net.wifi.WifiConfiguration;
26 import android.net.wifi.WifiInfo;
27 import android.net.wifi.WifiManager;
28 import android.os.Bundle;
29 import android.os.UserManager;
30 import android.provider.Settings;
31 import android.util.EventLog;
32 import android.util.Log;
33 
34 import androidx.annotation.VisibleForTesting;
35 import androidx.fragment.app.FragmentTransaction;
36 
37 import com.android.settings.R;
38 import com.android.settings.flags.Flags;
39 import com.android.settings.overlay.FeatureFactory;
40 
41 import java.util.List;
42 
43 /**
44  * To provision "other" device with specified Wi-Fi network.
45  *
46  * Uses different intents to specify different provisioning ways.
47  *
48  * For intent action {@code ACTION_CONFIGURATOR_QR_CODE_SCANNER} and
49  * {@code android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_GENERATOR}, specify the Wi-Fi network to be
50  * provisioned in:
51  *
52  * {@code WifiDppUtils.EXTRA_WIFI_SECURITY}
53  * {@code WifiDppUtils.EXTRA_WIFI_SSID}
54  * {@code WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY}
55  * {@code WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID}
56  *
57  * For intent action {@link Settings#ACTION_PROCESS_WIFI_EASY_CONNECT_URI}, specify Wi-Fi
58  * Easy Connect bootstrapping information string in Intent's data URI.
59  */
60 public class WifiDppConfiguratorActivity extends WifiDppBaseActivity implements
61         WifiNetworkConfig.Retriever,
62         WifiDppQrCodeScannerFragment.OnScanWifiDppSuccessListener,
63         WifiDppAddDeviceFragment.OnClickChooseDifferentNetworkListener,
64         WifiNetworkListFragment.OnChooseNetworkListener {
65 
66     private static final String TAG = "WifiDppConfiguratorActivity";
67 
68     static final String ACTION_CONFIGURATOR_QR_CODE_SCANNER =
69             "android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_SCANNER";
70     static final String ACTION_CONFIGURATOR_QR_CODE_GENERATOR =
71             "android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_GENERATOR";
72 
73     // Key for Bundle usage
74     private static final String KEY_QR_CODE = "key_qr_code";
75     private static final String KEY_WIFI_SECURITY = "key_wifi_security";
76     private static final String KEY_WIFI_SSID = "key_wifi_ssid";
77     private static final String KEY_WIFI_PRESHARED_KEY = "key_wifi_preshared_key";
78     private static final String KEY_WIFI_HIDDEN_SSID = "key_wifi_hidden_ssid";
79     private static final String KEY_WIFI_NETWORK_ID = "key_wifi_network_id";
80     private static final String KEY_IS_HOTSPOT = "key_is_hotspot";
81 
82     /** The Wi-Fi network which will be configured */
83     private WifiNetworkConfig mWifiNetworkConfig;
84 
85     /** The Wi-Fi DPP QR code from intent ACTION_PROCESS_WIFI_EASY_CONNECT_URI */
86     private WifiQrCode mWifiDppQrCode;
87 
88     /**
89      * The remote device's band support obtained as an (optional) extra
90      * EXTRA_EASY_CONNECT_BAND_LIST from the intent ACTION_PROCESS_WIFI_EASY_CONNECT_URI.
91      *
92      * The band support is provided as IEEE 802.11 Global Operating Classes. There may be a single
93      * or multiple operating classes specified. The array may also be a null if the extra wasn't
94      * specified.
95      */
96     private int[] mWifiDppRemoteBandSupport;
97 
98     @Override
getMetricsCategory()99     public int getMetricsCategory() {
100         return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR;
101     }
102 
103     @Override
onCreate(Bundle savedInstanceState)104     protected void onCreate(Bundle savedInstanceState) {
105         super.onCreate(savedInstanceState);
106         if (!isAddWifiConfigAllowed(getApplicationContext())) {
107             finish();
108             return;
109         }
110 
111         if (savedInstanceState != null) {
112             String qrCode = savedInstanceState.getString(KEY_QR_CODE);
113 
114             mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(qrCode);
115 
116             final String security = savedInstanceState.getString(KEY_WIFI_SECURITY);
117             final String ssid = savedInstanceState.getString(KEY_WIFI_SSID);
118             final String preSharedKey = savedInstanceState.getString(KEY_WIFI_PRESHARED_KEY);
119             final boolean hiddenSsid = savedInstanceState.getBoolean(KEY_WIFI_HIDDEN_SSID);
120             final int networkId = savedInstanceState.getInt(KEY_WIFI_NETWORK_ID);
121             final boolean isHotspot = savedInstanceState.getBoolean(KEY_IS_HOTSPOT);
122 
123             mWifiNetworkConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid,
124                     preSharedKey, hiddenSsid, networkId, isHotspot);
125         }
126     }
127 
128     @Override
handleIntent(Intent intent)129     protected void handleIntent(Intent intent) {
130         if (!isAddWifiConfigAllowed(getApplicationContext())) {
131             finish();
132             return;
133         }
134         if (isGuestUser(getApplicationContext())) {
135             Log.e(TAG, "Guest user is not allowed to configure Wi-Fi!");
136             EventLog.writeEvent(0x534e4554, "224772890", -1 /* UID */, "User is a guest");
137             finish();
138             return;
139         }
140 
141         String action = intent != null ? intent.getAction() : null;
142         if (action == null) {
143             finish();
144             return;
145         }
146 
147         boolean cancelActivity = false;
148         WifiNetworkConfig config;
149         switch (action) {
150             case ACTION_CONFIGURATOR_QR_CODE_SCANNER:
151                 config = WifiNetworkConfig.getValidConfigOrNull(intent);
152                 if (config == null) {
153                     cancelActivity = true;
154                 } else {
155                     mWifiNetworkConfig = config;
156                     showQrCodeScannerFragment();
157                 }
158                 break;
159             case ACTION_CONFIGURATOR_QR_CODE_GENERATOR:
160                 config = WifiNetworkConfig.getValidConfigOrNull(intent);
161                 if (config == null) {
162                     cancelActivity = true;
163                 } else {
164                     mWifiNetworkConfig = config;
165                     showQrCodeGeneratorFragment();
166                 }
167                 break;
168             case Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_URI:
169                 WifiDppUtils.showLockScreen(this,
170                         () -> handleActionProcessWifiEasyConnectUriIntent(intent));
171                 break;
172             default:
173                 cancelActivity = true;
174                 Log.e(TAG, "Launch with an invalid action");
175         }
176 
177         if (cancelActivity) {
178             finish();
179         }
180     }
181 
handleActionProcessWifiEasyConnectUriIntent(Intent intent)182     private void handleActionProcessWifiEasyConnectUriIntent(Intent intent) {
183         final Uri uri = intent.getData();
184         final String uriString = (uri == null) ? null : uri.toString();
185         mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(uriString);
186         mWifiDppRemoteBandSupport = intent.getIntArrayExtra(
187                 Settings.EXTRA_EASY_CONNECT_BAND_LIST); // returns null if none
188         final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this);
189         if (!isDppSupported) {
190             Log.e(TAG,
191                     "ACTION_PROCESS_WIFI_EASY_CONNECT_URI for a device that doesn't "
192                             + "support Wifi DPP - use WifiManager#isEasyConnectSupported");
193         }
194         if (mWifiDppQrCode == null) {
195             Log.e(TAG, "ACTION_PROCESS_WIFI_EASY_CONNECT_URI with null URI!");
196         }
197         if (mWifiDppQrCode == null || !isDppSupported) {
198             finish();
199         } else {
200             final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull();
201             if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) {
202                 showChooseSavedWifiNetworkFragment(/* addToBackStack */ false);
203             } else {
204                 mWifiNetworkConfig = connectedConfig;
205                 showAddDeviceFragment(/* addToBackStack */ false);
206             }
207         }
208     }
209 
210     @VisibleForTesting
showQrCodeScannerFragment()211     void showQrCodeScannerFragment() {
212         WifiDppQrCodeScannerFragment fragment =
213                 (WifiDppQrCodeScannerFragment) mFragmentManager.findFragmentByTag(
214                         WifiDppUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
215 
216         if (fragment == null) {
217             fragment = new WifiDppQrCodeScannerFragment();
218         } else {
219             if (fragment.isVisible()) {
220                 return;
221             }
222 
223             // When the fragment in back stack but not on top of the stack, we can simply pop
224             // stack because current fragment transactions are arranged in an order
225             mFragmentManager.popBackStackImmediate();
226             return;
227         }
228         final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
229 
230         fragmentTransaction.replace(R.id.fragment_container, fragment,
231                 WifiDppUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
232         fragmentTransaction.commit();
233     }
234 
showQrCodeGeneratorFragment()235     private void showQrCodeGeneratorFragment() {
236         WifiDppQrCodeGeneratorFragment fragment =
237                 (WifiDppQrCodeGeneratorFragment) mFragmentManager.findFragmentByTag(
238                         WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR);
239 
240         if (fragment == null) {
241             if (Flags.enableWifiSharingRuntimeFragment()) {
242                 fragment = FeatureFactory.getFeatureFactory().getWifiFeatureProvider()
243                     .getWifiDppQrCodeGeneratorFragment();
244             } else {
245                 fragment = new WifiDppQrCodeGeneratorFragment();
246             }
247         } else {
248             if (fragment.isVisible()) {
249                 return;
250             }
251 
252             // When the fragment in back stack but not on top of the stack, we can simply pop
253             // stack because current fragment transactions are arranged in an order
254             mFragmentManager.popBackStackImmediate();
255             return;
256         }
257         final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
258 
259         fragmentTransaction.replace(R.id.fragment_container, fragment,
260                 WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR);
261         fragmentTransaction.commit();
262     }
263 
showChooseSavedWifiNetworkFragment(boolean addToBackStack)264     private void showChooseSavedWifiNetworkFragment(boolean addToBackStack) {
265         WifiDppChooseSavedWifiNetworkFragment fragment =
266                 (WifiDppChooseSavedWifiNetworkFragment) mFragmentManager.findFragmentByTag(
267                         WifiDppUtils.TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK);
268 
269         if (fragment == null) {
270             fragment = new WifiDppChooseSavedWifiNetworkFragment();
271         } else {
272             if (fragment.isVisible()) {
273                 return;
274             }
275 
276             // When the fragment in back stack but not on top of the stack, we can simply pop
277             // stack because current fragment transactions are arranged in an order
278             mFragmentManager.popBackStackImmediate();
279             return;
280         }
281         final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
282 
283         fragmentTransaction.replace(R.id.fragment_container, fragment,
284                 WifiDppUtils.TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK);
285         if (addToBackStack) {
286             fragmentTransaction.addToBackStack(/* name */ null);
287         }
288         fragmentTransaction.commit();
289     }
290 
showAddDeviceFragment(boolean addToBackStack)291     private void showAddDeviceFragment(boolean addToBackStack) {
292         WifiDppAddDeviceFragment fragment =
293                 (WifiDppAddDeviceFragment) mFragmentManager.findFragmentByTag(
294                         WifiDppUtils.TAG_FRAGMENT_ADD_DEVICE);
295 
296         if (fragment == null) {
297             fragment = new WifiDppAddDeviceFragment();
298         } else {
299             if (fragment.isVisible()) {
300                 return;
301             }
302 
303             // When the fragment in back stack but not on top of the stack, we can simply pop
304             // stack because current fragment transactions are arranged in an order
305             mFragmentManager.popBackStackImmediate();
306             return;
307         }
308         final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
309 
310         fragmentTransaction.replace(R.id.fragment_container, fragment,
311                 WifiDppUtils.TAG_FRAGMENT_ADD_DEVICE);
312         if (addToBackStack) {
313             fragmentTransaction.addToBackStack(/* name */ null);
314         }
315         fragmentTransaction.commit();
316     }
317 
318     @Override
getWifiNetworkConfig()319     public WifiNetworkConfig getWifiNetworkConfig() {
320         return mWifiNetworkConfig;
321     }
322 
getWifiDppQrCode()323     WifiQrCode getWifiDppQrCode() {
324         return mWifiDppQrCode;
325     }
326 
327     @VisibleForTesting
setWifiNetworkConfig(WifiNetworkConfig config)328     boolean setWifiNetworkConfig(WifiNetworkConfig config) {
329         if(!WifiNetworkConfig.isValidConfig(config)) {
330             return false;
331         } else {
332             mWifiNetworkConfig = new WifiNetworkConfig(config);
333             return true;
334         }
335     }
336 
337     @VisibleForTesting
setWifiDppQrCode(WifiQrCode wifiQrCode)338     boolean setWifiDppQrCode(WifiQrCode wifiQrCode) {
339         if (wifiQrCode == null) {
340             return false;
341         }
342 
343         if (!WifiQrCode.SCHEME_DPP.equals(wifiQrCode.getScheme())) {
344             return false;
345         }
346 
347         mWifiDppQrCode = new WifiQrCode(wifiQrCode.getQrCode());
348         return true;
349     }
350 
351     @Override
onScanWifiDppSuccess(WifiQrCode wifiQrCode)352     public void onScanWifiDppSuccess(WifiQrCode wifiQrCode) {
353         mWifiDppQrCode = wifiQrCode;
354 
355         showAddDeviceFragment(/* addToBackStack */ true);
356     }
357 
358     @Override
onClickChooseDifferentNetwork()359     public void onClickChooseDifferentNetwork() {
360         showChooseSavedWifiNetworkFragment(/* addToBackStack */ true);
361     }
362 
363     @Override
onSaveInstanceState(Bundle outState)364     public void onSaveInstanceState(Bundle outState) {
365         if (mWifiDppQrCode != null) {
366             outState.putString(KEY_QR_CODE, mWifiDppQrCode.getQrCode());
367         }
368 
369         if (mWifiNetworkConfig != null) {
370             outState.putString(KEY_WIFI_SECURITY, mWifiNetworkConfig.getSecurity());
371             outState.putString(KEY_WIFI_SSID, mWifiNetworkConfig.getSsid());
372             outState.putString(KEY_WIFI_PRESHARED_KEY, mWifiNetworkConfig.getPreSharedKey());
373             outState.putBoolean(KEY_WIFI_HIDDEN_SSID, mWifiNetworkConfig.getHiddenSsid());
374             outState.putInt(KEY_WIFI_NETWORK_ID, mWifiNetworkConfig.getNetworkId());
375             outState.putBoolean(KEY_IS_HOTSPOT, mWifiNetworkConfig.isHotspot());
376         }
377 
378         super.onSaveInstanceState(outState);
379     }
380 
381     @Override
onChooseNetwork(WifiNetworkConfig wifiNetworkConfig)382     public void onChooseNetwork(WifiNetworkConfig wifiNetworkConfig) {
383         mWifiNetworkConfig = new WifiNetworkConfig(wifiNetworkConfig);
384 
385         showAddDeviceFragment(/* addToBackStack */ true);
386     }
387 
getConnectedWifiNetworkConfigOrNull()388     private WifiNetworkConfig getConnectedWifiNetworkConfigOrNull() {
389         final WifiManager wifiManager = getSystemService(WifiManager.class);
390         if (!wifiManager.isWifiEnabled()) {
391             return null;
392         }
393 
394         final WifiInfo connectionInfo = wifiManager.getConnectionInfo();
395         if (connectionInfo == null) {
396             return null;
397         }
398 
399         final int connectionNetworkId = connectionInfo.getNetworkId();
400         final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks();
401         for (WifiConfiguration wifiConfiguration : configs) {
402             if (wifiConfiguration.networkId == connectionNetworkId) {
403                 return WifiNetworkConfig.getValidConfigOrNull(
404                     WifiDppUtils.getSecurityString(wifiConfiguration),
405                     wifiConfiguration.getPrintableSsid(),
406                     wifiConfiguration.preSharedKey,
407                     wifiConfiguration.hiddenSSID,
408                     wifiConfiguration.networkId,
409                     /* isHotspot */ false);
410             }
411         }
412 
413         return null;
414     }
415 
isGuestUser(Context context)416     private static boolean isGuestUser(Context context) {
417         if (context == null) return false;
418         final UserManager userManager = context.getSystemService(UserManager.class);
419         if (userManager == null) return false;
420         return userManager.isGuestUser();
421     }
422 
423     @VisibleForTesting
isAddWifiConfigAllowed(Context context)424     static boolean isAddWifiConfigAllowed(Context context) {
425         UserManager userManager = context.getSystemService(UserManager.class);
426         if (userManager != null && userManager.hasUserRestriction(DISALLOW_ADD_WIFI_CONFIG)) {
427             Log.e(TAG, "The user is not allowed to add Wi-Fi configuration.");
428             return false;
429         }
430         return true;
431     }
432 }
433