• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car.developeroptions.wifi.dpp;
18 
19 import android.app.ActionBar;
20 import android.app.settings.SettingsEnums;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.net.wifi.WifiConfiguration;
24 import android.net.wifi.WifiInfo;
25 import android.net.wifi.WifiManager;
26 import android.os.Bundle;
27 import android.provider.Settings;
28 import android.util.Log;
29 
30 import androidx.annotation.VisibleForTesting;
31 import androidx.fragment.app.Fragment;
32 import androidx.fragment.app.FragmentManager;
33 import androidx.fragment.app.FragmentTransaction;
34 
35 import com.android.car.developeroptions.R;
36 import com.android.car.developeroptions.core.InstrumentedActivity;
37 
38 import java.util.List;
39 
40 /**
41  * To provision "other" device with specified Wi-Fi network.
42  *
43  * Uses different intents to specify different provisioning ways.
44  *
45  * For intent action {@code ACTION_CONFIGURATOR_QR_CODE_SCANNER} and
46  * {@code android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_GENERATOR}, specify the Wi-Fi network to be
47  * provisioned in:
48  *
49  * {@code WifiDppUtils.EXTRA_WIFI_SECURITY}
50  * {@code WifiDppUtils.EXTRA_WIFI_SSID}
51  * {@code WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY}
52  * {@code WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID}
53  *
54  * For intent action {@link Settings#ACTION_PROCESS_WIFI_EASY_CONNECT_URI}, specify Wi-Fi
55  * Easy Connect bootstrapping information string in Intent's data URI.
56  */
57 public class WifiDppConfiguratorActivity extends InstrumentedActivity implements
58         WifiNetworkConfig.Retriever,
59         WifiDppQrCodeGeneratorFragment.OnQrCodeGeneratorFragmentAddButtonClickedListener,
60         WifiDppQrCodeScannerFragment.OnScanWifiDppSuccessListener,
61         WifiDppAddDeviceFragment.OnClickChooseDifferentNetworkListener,
62         WifiNetworkListFragment.OnChooseNetworkListener {
63 
64     private static final String TAG = "WifiDppConfiguratorActivity";
65 
66     public static final String ACTION_CONFIGURATOR_QR_CODE_SCANNER =
67             "android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_SCANNER";
68     public static final String ACTION_CONFIGURATOR_QR_CODE_GENERATOR =
69             "android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_GENERATOR";
70 
71     // Key for Bundle usage
72     private static final String KEY_QR_CODE = "key_qr_code";
73     private static final String KEY_WIFI_SECURITY = "key_wifi_security";
74     private static final String KEY_WIFI_SSID = "key_wifi_ssid";
75     private static final String KEY_WIFI_PRESHARED_KEY = "key_wifi_preshared_key";
76     private static final String KEY_WIFI_HIDDEN_SSID = "key_wifi_hidden_ssid";
77     private static final String KEY_WIFI_NETWORK_ID = "key_wifi_network_id";
78     private static final String KEY_IS_HOTSPOT = "key_is_hotspot";
79 
80     private FragmentManager mFragmentManager;
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     /** Secret extra that allows fake networks to show in UI for testing purposes */
89     private boolean mIsTest;
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         setContentView(R.layout.wifi_dpp_activity);
101         mFragmentManager = getSupportFragmentManager();
102 
103         if (savedInstanceState != null) {
104             String qrCode = savedInstanceState.getString(KEY_QR_CODE);
105 
106             mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(qrCode);
107 
108             final String security = savedInstanceState.getString(KEY_WIFI_SECURITY);
109             final String ssid = savedInstanceState.getString(KEY_WIFI_SSID);
110             final String preSharedKey = savedInstanceState.getString(KEY_WIFI_PRESHARED_KEY);
111             final boolean hiddenSsid = savedInstanceState.getBoolean(KEY_WIFI_HIDDEN_SSID);
112             final int networkId = savedInstanceState.getInt(KEY_WIFI_NETWORK_ID);
113             final boolean isHotspot = savedInstanceState.getBoolean(KEY_IS_HOTSPOT);
114 
115             mWifiNetworkConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid,
116                     preSharedKey, hiddenSsid, networkId, isHotspot);
117         } else {
118             handleIntent(getIntent());
119         }
120 
121         ActionBar actionBar = getActionBar();
122         if (actionBar != null) {
123             actionBar.setElevation(0);
124             actionBar.setDisplayShowTitleEnabled(false);
125         }
126     }
127 
handleIntent(Intent intent)128     private void handleIntent(Intent intent) {
129         boolean cancelActivity = false;
130         WifiNetworkConfig config;
131         switch (intent.getAction()) {
132             case ACTION_CONFIGURATOR_QR_CODE_SCANNER:
133                 config = WifiNetworkConfig.getValidConfigOrNull(intent);
134                 if (config == null) {
135                     cancelActivity = true;
136                 } else {
137                     mWifiNetworkConfig = config;
138                     showQrCodeScannerFragment(/* addToBackStack= */ false);
139                 }
140                 break;
141             case ACTION_CONFIGURATOR_QR_CODE_GENERATOR:
142                 config = WifiNetworkConfig.getValidConfigOrNull(intent);
143                 if (config == null) {
144                     cancelActivity = true;
145                 } else {
146                     mWifiNetworkConfig = config;
147                     showQrCodeGeneratorFragment();
148                 }
149                 break;
150             case Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_URI:
151                 final Uri uri = intent.getData();
152                 final String uriString = (uri == null) ? null : uri.toString();
153                 mIsTest = intent.getBooleanExtra(WifiDppUtils.EXTRA_TEST, false);
154                 mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(uriString);
155                 final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this);
156                 if (!isDppSupported) {
157                     Log.d(TAG, "Device doesn't support Wifi DPP");
158                 }
159                 if (mWifiDppQrCode == null || !isDppSupported) {
160                     cancelActivity = true;
161                 } else {
162                     final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull();
163                     if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) {
164                         showChooseSavedWifiNetworkFragment(/* addToBackStack */ false);
165                     } else {
166                         mWifiNetworkConfig = connectedConfig;
167                         showAddDeviceFragment(/* addToBackStack */ false);
168                     }
169                 }
170                 break;
171             default:
172                 cancelActivity = true;
173                 Log.e(TAG, "Launch with an invalid action");
174         }
175 
176         if (cancelActivity) {
177             finish();
178         }
179     }
180 
showQrCodeScannerFragment(boolean addToBackStack)181     private void showQrCodeScannerFragment(boolean addToBackStack) {
182         WifiDppQrCodeScannerFragment fragment =
183                 (WifiDppQrCodeScannerFragment) mFragmentManager.findFragmentByTag(
184                         WifiDppUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
185 
186         if (fragment == null) {
187             fragment = new WifiDppQrCodeScannerFragment();
188         } else {
189             if (fragment.isVisible()) {
190                 return;
191             }
192 
193             // When the fragment in back stack but not on top of the stack, we can simply pop
194             // stack because current fragment transactions are arranged in an order
195             mFragmentManager.popBackStackImmediate();
196             return;
197         }
198         final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
199 
200         fragmentTransaction.replace(R.id.fragment_container, fragment,
201                 WifiDppUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
202         if (addToBackStack) {
203             fragmentTransaction.addToBackStack(/* name */ null);
204         }
205         fragmentTransaction.commit();
206     }
207 
showQrCodeGeneratorFragment()208     private void showQrCodeGeneratorFragment() {
209         WifiDppQrCodeGeneratorFragment fragment =
210                 (WifiDppQrCodeGeneratorFragment) mFragmentManager.findFragmentByTag(
211                         WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR);
212 
213         if (fragment == null) {
214             fragment = new WifiDppQrCodeGeneratorFragment();
215         } else {
216             if (fragment.isVisible()) {
217                 return;
218             }
219 
220             // When the fragment in back stack but not on top of the stack, we can simply pop
221             // stack because current fragment transactions are arranged in an order
222             mFragmentManager.popBackStackImmediate();
223             return;
224         }
225         final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
226 
227         fragmentTransaction.replace(R.id.fragment_container, fragment,
228                 WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR);
229         fragmentTransaction.commit();
230     }
231 
showChooseSavedWifiNetworkFragment(boolean addToBackStack)232     private void showChooseSavedWifiNetworkFragment(boolean addToBackStack) {
233         WifiDppChooseSavedWifiNetworkFragment fragment =
234                 (WifiDppChooseSavedWifiNetworkFragment) mFragmentManager.findFragmentByTag(
235                         WifiDppUtils.TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK);
236 
237         if (fragment == null) {
238             fragment = new WifiDppChooseSavedWifiNetworkFragment();
239             if (mIsTest) {
240                 Bundle bundle = new Bundle();
241                 bundle.putBoolean(WifiDppUtils.EXTRA_TEST, true);
242                 fragment.setArguments(bundle);
243             }
244         } else {
245             if (fragment.isVisible()) {
246                 return;
247             }
248 
249             // When the fragment in back stack but not on top of the stack, we can simply pop
250             // stack because current fragment transactions are arranged in an order
251             mFragmentManager.popBackStackImmediate();
252             return;
253         }
254         final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
255 
256         fragmentTransaction.replace(R.id.fragment_container, fragment,
257                 WifiDppUtils.TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK);
258         if (addToBackStack) {
259             fragmentTransaction.addToBackStack(/* name */ null);
260         }
261         fragmentTransaction.commit();
262     }
263 
showAddDeviceFragment(boolean addToBackStack)264     private void showAddDeviceFragment(boolean addToBackStack) {
265         WifiDppAddDeviceFragment fragment =
266                 (WifiDppAddDeviceFragment) mFragmentManager.findFragmentByTag(
267                         WifiDppUtils.TAG_FRAGMENT_ADD_DEVICE);
268 
269         if (fragment == null) {
270             fragment = new WifiDppAddDeviceFragment();
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_ADD_DEVICE);
285         if (addToBackStack) {
286             fragmentTransaction.addToBackStack(/* name */ null);
287         }
288         fragmentTransaction.commit();
289     }
290 
291     @Override
getWifiNetworkConfig()292     public WifiNetworkConfig getWifiNetworkConfig() {
293         return mWifiNetworkConfig;
294     }
295 
getWifiDppQrCode()296     public WifiQrCode getWifiDppQrCode() {
297         return mWifiDppQrCode;
298     }
299 
300     @VisibleForTesting
setWifiNetworkConfig(WifiNetworkConfig config)301     boolean setWifiNetworkConfig(WifiNetworkConfig config) {
302         if(!WifiNetworkConfig.isValidConfig(config)) {
303             return false;
304         } else {
305             mWifiNetworkConfig = new WifiNetworkConfig(config);
306             return true;
307         }
308     }
309 
310     @VisibleForTesting
setWifiDppQrCode(WifiQrCode wifiQrCode)311     boolean setWifiDppQrCode(WifiQrCode wifiQrCode) {
312         if (wifiQrCode == null) {
313             return false;
314         }
315 
316         if (!WifiQrCode.SCHEME_DPP.equals(wifiQrCode.getScheme())) {
317             return false;
318         }
319 
320         mWifiDppQrCode = new WifiQrCode(wifiQrCode.getQrCode());
321         return true;
322     }
323 
324     @Override
onNavigateUp()325     public boolean onNavigateUp() {
326         Fragment fragment = mFragmentManager.findFragmentById(R.id.fragment_container);
327         if (fragment instanceof WifiDppQrCodeGeneratorFragment) {
328             finish();
329             return true;
330         } else if (fragment instanceof WifiDppQrCodeScannerFragment) {
331             mFragmentManager.popBackStackImmediate();
332         }
333 
334         return false;
335     }
336 
337     @Override
onQrCodeGeneratorFragmentAddButtonClicked()338     public void onQrCodeGeneratorFragmentAddButtonClicked() {
339         showQrCodeScannerFragment(/* addToBackStack */ true);
340     }
341 
342     @Override
onScanWifiDppSuccess(WifiQrCode wifiQrCode)343     public void onScanWifiDppSuccess(WifiQrCode wifiQrCode) {
344         mWifiDppQrCode = wifiQrCode;
345 
346         showAddDeviceFragment(/* addToBackStack */ true);
347     }
348 
349     @Override
onClickChooseDifferentNetwork()350     public void onClickChooseDifferentNetwork() {
351         showChooseSavedWifiNetworkFragment(/* addToBackStack */ true);
352     }
353 
354     @Override
onSaveInstanceState(Bundle outState)355     public void onSaveInstanceState(Bundle outState) {
356         if (mWifiDppQrCode != null) {
357             outState.putString(KEY_QR_CODE, mWifiDppQrCode.getQrCode());
358         }
359 
360         if (mWifiNetworkConfig != null) {
361             outState.putString(KEY_WIFI_SECURITY, mWifiNetworkConfig.getSecurity());
362             outState.putString(KEY_WIFI_SSID, mWifiNetworkConfig.getSsid());
363             outState.putString(KEY_WIFI_PRESHARED_KEY, mWifiNetworkConfig.getPreSharedKey());
364             outState.putBoolean(KEY_WIFI_HIDDEN_SSID, mWifiNetworkConfig.getHiddenSsid());
365             outState.putInt(KEY_WIFI_NETWORK_ID, mWifiNetworkConfig.getNetworkId());
366             outState.putBoolean(KEY_IS_HOTSPOT, mWifiNetworkConfig.isHotspot());
367         }
368 
369         super.onSaveInstanceState(outState);
370     }
371 
372     @Override
onChooseNetwork(WifiNetworkConfig wifiNetworkConfig)373     public void onChooseNetwork(WifiNetworkConfig wifiNetworkConfig) {
374         mWifiNetworkConfig = new WifiNetworkConfig(wifiNetworkConfig);
375 
376         showAddDeviceFragment(/* addToBackStack */ true);
377     }
378 
getConnectedWifiNetworkConfigOrNull()379     private WifiNetworkConfig getConnectedWifiNetworkConfigOrNull() {
380         final WifiManager wifiManager = getSystemService(WifiManager.class);
381         if (!wifiManager.isWifiEnabled()) {
382             return null;
383         }
384 
385         final WifiInfo connectionInfo = wifiManager.getConnectionInfo();
386         if (connectionInfo == null) {
387             return null;
388         }
389 
390         final int connectionNetworkId = connectionInfo.getNetworkId();
391         final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks();
392         for (WifiConfiguration wifiConfiguration : configs) {
393             if (wifiConfiguration.networkId == connectionNetworkId) {
394                 return WifiNetworkConfig.getValidConfigOrNull(
395                     WifiDppUtils.getSecurityString(wifiConfiguration),
396                     wifiConfiguration.getPrintableSsid(),
397                     wifiConfiguration.preSharedKey,
398                     /* hiddenSsid */ false,
399                     wifiConfiguration.networkId,
400                     /* isHotspot */ false);
401             }
402         }
403 
404         return null;
405     }
406 }
407