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