• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.car.settings.wifi;
17 
18 import android.annotation.DrawableRes;
19 import android.app.admin.DevicePolicyManager;
20 import android.content.ComponentName;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.net.NetworkCapabilities;
25 import android.net.NetworkInfo;
26 import android.net.wifi.WifiConfiguration;
27 import android.net.wifi.WifiManager;
28 import android.provider.Settings;
29 import android.widget.Toast;
30 
31 import androidx.annotation.StringRes;
32 
33 import com.android.car.settings.R;
34 import com.android.car.settings.common.Logger;
35 import com.android.settingslib.wifi.AccessPoint;
36 
37 import java.util.regex.Pattern;
38 
39 /**
40  * A collections of util functions for WIFI.
41  */
42 public class WifiUtil {
43 
44     private static final Logger LOG = new Logger(WifiUtil.class);
45 
46     /** Value that is returned when we fail to connect wifi. */
47     public static final int INVALID_NET_ID = -1;
48     private static final Pattern HEX_PATTERN = Pattern.compile("^[0-9A-F]+$");
49 
50     @DrawableRes
getIconRes(int state)51     public static int getIconRes(int state) {
52         switch (state) {
53             case WifiManager.WIFI_STATE_ENABLING:
54             case WifiManager.WIFI_STATE_DISABLED:
55                 return R.drawable.ic_settings_wifi_disabled;
56             default:
57                 return R.drawable.ic_settings_wifi;
58         }
59     }
60 
isWifiOn(int state)61     public static boolean isWifiOn(int state) {
62         switch (state) {
63             case WifiManager.WIFI_STATE_ENABLING:
64             case WifiManager.WIFI_STATE_DISABLED:
65                 return false;
66             default:
67                 return true;
68         }
69     }
70 
71     /**
72      * @return 0 if no proper description can be found.
73      */
74     @StringRes
getStateDesc(int state)75     public static Integer getStateDesc(int state) {
76         switch (state) {
77             case WifiManager.WIFI_STATE_ENABLING:
78                 return R.string.wifi_starting;
79             case WifiManager.WIFI_STATE_DISABLING:
80                 return R.string.wifi_stopping;
81             case WifiManager.WIFI_STATE_DISABLED:
82                 return R.string.wifi_disabled;
83             default:
84                 return 0;
85         }
86     }
87 
88     /**
89      * Returns {@Code true} if wifi is available on this device.
90      */
isWifiAvailable(Context context)91     public static boolean isWifiAvailable(Context context) {
92         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
93     }
94 
95     /**
96      * Gets a unique key for a {@link AccessPoint}.
97      */
getKey(AccessPoint accessPoint)98     public static String getKey(AccessPoint accessPoint) {
99         return String.valueOf(accessPoint.hashCode());
100     }
101 
102     /**
103      * This method is a stripped and negated version of WifiConfigStore.canModifyNetwork.
104      *
105      * @param context Context of caller
106      * @param config  The WiFi config.
107      * @return {@code true} if Settings cannot modify the config due to lockDown.
108      */
isNetworkLockedDown(Context context, WifiConfiguration config)109     public static boolean isNetworkLockedDown(Context context, WifiConfiguration config) {
110         if (config == null) {
111             return false;
112         }
113 
114         final DevicePolicyManager dpm =
115                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
116         final PackageManager pm = context.getPackageManager();
117 
118         // Check if device has DPM capability. If it has and dpm is still null, then we
119         // treat this case with suspicion and bail out.
120         if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
121             return true;
122         }
123 
124         boolean isConfigEligibleForLockdown = false;
125         if (dpm != null) {
126             final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
127             if (deviceOwner != null) {
128                 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
129                 try {
130                     final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(),
131                             deviceOwnerUserId);
132                     isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
133                 } catch (PackageManager.NameNotFoundException e) {
134                     // don't care
135                 }
136             }
137         }
138         if (!isConfigEligibleForLockdown) {
139             return false;
140         }
141 
142         final ContentResolver resolver = context.getContentResolver();
143         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
144                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
145         return isLockdownFeatureEnabled;
146     }
147 
148     /**
149      * Returns {@code true} if the provided NetworkCapabilities indicate a captive portal network.
150      */
canSignIntoNetwork(NetworkCapabilities capabilities)151     public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) {
152         return (capabilities != null
153                 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL));
154     }
155 
156     /**
157      * Returns netId. -1 if connection fails.
158      */
connectToAccessPoint(Context context, String ssid, int security, String password, boolean hidden)159     public static int connectToAccessPoint(Context context, String ssid, int security,
160             String password, boolean hidden) {
161         WifiManager wifiManager = context.getSystemService(WifiManager.class);
162         WifiConfiguration wifiConfig = new WifiConfiguration();
163         wifiConfig.SSID = String.format("\"%s\"", ssid);
164         wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
165         wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
166         wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
167         wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
168         wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
169         wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
170         wifiConfig.hiddenSSID = hidden;
171         switch (security) {
172             case AccessPoint.SECURITY_NONE:
173                 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
174                 wifiConfig.allowedAuthAlgorithms.clear();
175                 wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
176                 wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
177                 break;
178             case AccessPoint.SECURITY_WEP:
179                 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
180                 wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
181                 wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
182                 wifiConfig.wepKeys[0] = isHexString(password) ? password
183                         : "\"" + password + "\"";
184                 wifiConfig.wepTxKeyIndex = 0;
185                 break;
186             case AccessPoint.SECURITY_PSK:
187             case AccessPoint.SECURITY_EAP:
188                 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
189                 wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
190                 wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
191                 wifiConfig.preSharedKey = String.format("\"%s\"", password);
192                 break;
193             default:
194                 throw new IllegalArgumentException("invalid security type");
195         }
196         int netId = wifiManager.addNetwork(wifiConfig);
197         // This only means wifiManager failed writing the new wifiConfig to the db. It doesn't mean
198         // the network is invalid.
199         if (netId == INVALID_NET_ID) {
200             Toast.makeText(context, R.string.wifi_failed_connect_message,
201                     Toast.LENGTH_SHORT).show();
202         } else {
203             wifiManager.enableNetwork(netId, true);
204         }
205         return netId;
206     }
207 
208     /** Forget the network specified by {@code accessPoint}. */
forget(Context context, AccessPoint accessPoint)209     public static void forget(Context context, AccessPoint accessPoint) {
210         WifiManager wifiManager = context.getSystemService(WifiManager.class);
211         if (!accessPoint.isSaved()) {
212             if (accessPoint.getNetworkInfo() != null
213                     && accessPoint.getNetworkInfo().getState() != NetworkInfo.State.DISCONNECTED) {
214                 // Network is active but has no network ID - must be ephemeral.
215                 wifiManager.disableEphemeralNetwork(
216                         AccessPoint.convertToQuotedString(accessPoint.getSsidStr()));
217             } else {
218                 // Should not happen, but a monkey seems to trigger it
219                 LOG.e("Failed to forget invalid network " + accessPoint.getConfig());
220                 return;
221             }
222         } else {
223             wifiManager.forget(accessPoint.getConfig().networkId,
224                     new ActionFailedListener(context, R.string.wifi_failed_forget_message));
225         }
226     }
227 
228     /** Returns {@code true} if the access point was disabled due to the wrong password. */
isAccessPointDisabledByWrongPassword(AccessPoint accessPoint)229     public static boolean isAccessPointDisabledByWrongPassword(AccessPoint accessPoint) {
230         WifiConfiguration config = accessPoint.getConfig();
231         if (config == null) {
232             return false;
233         }
234         WifiConfiguration.NetworkSelectionStatus networkStatus =
235                 config.getNetworkSelectionStatus();
236         if (networkStatus == null || networkStatus.isNetworkEnabled()) {
237             return false;
238         }
239         return networkStatus.getNetworkSelectionDisableReason()
240                 == WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD;
241     }
242 
isHexString(String password)243     private static boolean isHexString(String password) {
244         return HEX_PATTERN.matcher(password).matches();
245     }
246 
247     /**
248      * A shared implementation of {@link WifiManager.ActionListener} which shows a failure message
249      * in a toast.
250      */
251     public static class ActionFailedListener implements WifiManager.ActionListener {
252         private final Context mContext;
253         @StringRes
254         private final int mFailureMessage;
255 
ActionFailedListener(Context context, @StringRes int failureMessage)256         public ActionFailedListener(Context context, @StringRes int failureMessage) {
257             mContext = context;
258             mFailureMessage = failureMessage;
259         }
260 
261         @Override
onSuccess()262         public void onSuccess() {
263         }
264 
265         @Override
onFailure(int reason)266         public void onFailure(int reason) {
267             Toast.makeText(mContext, mFailureMessage, Toast.LENGTH_SHORT).show();
268         }
269     }
270 }
271