1 /*
2  * Copyright (C) 2012 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.tradefed.utils.wifi;
18 import android.app.Activity;
19 import android.app.Instrumentation;
20 import android.content.Context;
21 import android.net.wifi.WifiInfo;
22 import android.net.wifi.WifiManager;
23 import android.os.Build;
24 import android.os.Bundle;
25 import android.text.TextUtils;
26 import android.util.Log;
27 
28 import com.android.tradefed.utils.wifi.WifiConnector.WifiException;
29 
30 /**
31  * An instrumentation class to manipulate Wi-Fi services on device.
32  *
33  * <p>adb shell am instrument -e method (method name) -e arg1 val1 -e arg2 val2 -e arg3 val3 -w
34  * com.android.tradefed.utils.wifi/.WifiUtil
35  */
36 public class WifiUtil extends Instrumentation {
37     // FIXME: document exposed API methods and arguments
38     private static final String TAG = "WifiUtil";
39 
40     private static final String DEFAULT_URL_TO_CHECK = "http://www.google.com";
41     private static final int API_LEVEL_Q_TBD = 29;
42 
43     private Bundle mArguments;
44 
45     static class MissingArgException extends Exception {
MissingArgException(String msg)46         public MissingArgException(String msg) {
47             super(msg);
48         }
49 
fromArg(String arg)50         public static MissingArgException fromArg(String arg) {
51             return new MissingArgException(
52                     String.format("Error: missing mandatory argument '%s'", arg));
53         }
54     }
55 
56     @Override
onCreate(Bundle arguments)57     public void onCreate(Bundle arguments) {
58         super.onCreate(arguments);
59         mArguments = arguments;
60         // elevate permission if on Qt or later
61         int apiLevel = Build.VERSION.SDK_INT;
62         if (!"REL".equals(Build.VERSION.CODENAME)) {
63             // add one extra if platform is under development
64             // i.e. trying to predict the next API level number
65             apiLevel++;
66         }
67         // on Qt or higher
68         // FIXEME: change to Build.VERSION_CODES.Q after API level is finalized
69         if (apiLevel >= API_LEVEL_Q_TBD) {
70             getUiAutomation().adoptShellPermissionIdentity();
71         }
72         start();
73     }
74 
75     /**
76      * Fails an instrumentation request.
77      *
78      * @param errMsg an error message
79      */
fail(String errMsg)80     private void fail(String errMsg) {
81         Log.e(TAG, errMsg);
82         Bundle result = new Bundle();
83         result.putString("error", errMsg);
84         finish(Activity.RESULT_CANCELED, result);
85     }
86 
87     /**
88      * Returns the string value of an argument for the specified name, or throws
89      * {@link MissingArgException} if the argument is not found or empty.
90      *
91      * @param arg the name of an argument
92      * @return the value of an argument
93      * @throws MissingArgException if the argument is not found
94      */
expectString(String arg)95     private String expectString(String arg) throws MissingArgException {
96         String val = mArguments.getString(arg);
97         if (TextUtils.isEmpty(val)) {
98             throw MissingArgException.fromArg(arg);
99         }
100 
101         return val;
102     }
103 
104     /**
105      * Returns the value of a string argument for the specified name, or defaultValue if the
106      * argument is not found or empty.
107      *
108      * @param arg the name of an argument
109      * @param defaultValue a value to return if the argument is not found
110      * @return the value of an argument
111      */
getString(String arg, String defaultValue)112     private String getString(String arg, String defaultValue) {
113         String val = mArguments.getString(arg);
114         if (TextUtils.isEmpty(val)) {
115             return defaultValue;
116         }
117 
118         return val;
119     }
120 
121     /**
122      * Returns the integer value of an argument for the specified name, or throws
123      * {@link MissingArgException} if the argument is not found or cannot be parsed to an integer.
124      *
125      * @param arg the name of an argument
126      * @return the value of an argument
127      * @throws MissingArgException if the argument is not found
128      */
expectInteger(String arg)129     private int expectInteger(String arg) throws MissingArgException {
130         String val = expectString(arg);
131         int intVal;
132         try {
133             intVal = Integer.parseInt(val);
134         } catch (NumberFormatException e) {
135             final String msg = String.format("Couldn't parse arg '%s': %s", arg,
136                     e.getMessage());
137             throw new MissingArgException(msg);
138         }
139 
140         return intVal;
141     }
142 
143     /**
144      * Returns the integer value of an argument for the specified name, or defaultValue if the
145      * argument is not found, empty, or cannot be parsed.
146      *
147      * @param arg the name of an argument
148      * @param defaultValue a value to return if the argument is not found
149      * @return the value of an argument
150      */
getInteger(String arg, int defaultValue)151     private int getInteger(String arg, int defaultValue) {
152         try {
153             return expectInteger(arg);
154         } catch (MissingArgException e) {
155             return defaultValue;
156         }
157     }
158 
getBoolean(String arg, boolean defaultValue)159     private boolean getBoolean(String arg, boolean defaultValue) {
160         try {
161             return Boolean.parseBoolean(expectString(arg));
162         } catch (MissingArgException e) {
163             return defaultValue;
164         }
165     }
166 
167     @Override
onStart()168     public void onStart() {
169         super.onStart();
170         final Bundle result = new Bundle();
171 
172         try {
173             final String method = expectString("method");
174 
175             WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
176             if (wifiManager == null) {
177                 fail("Couldn't get WifiManager reference; goodbye!");
178                 return;
179             }
180             WifiConnector connector = new WifiConnector(getContext());
181 
182             // As a pattern, method implementations below should gather arguments _first_, and then
183             // use those arguments so that the system is not left in an inconsistent state if an
184             // argument is missing in the middle of an implementation.
185             if ("addOpenNetwork".equals(method)) {
186                 final String ssid = expectString("ssid");
187                 final boolean scanSsid = getBoolean("scan_ssid", false);
188                 final boolean disableMacRandomization =
189                         getBoolean("disableMacRandomization", false);
190 
191                 result.putInt(
192                         "result",
193                         connector.addNetwork(ssid, null, scanSsid, disableMacRandomization));
194 
195             } else if ("addWpaPskNetwork".equals(method)) {
196                 final String ssid = expectString("ssid");
197                 final boolean scanSsid = getBoolean("scan_ssid", false);
198                 final String psk = expectString("psk");
199                 final boolean disableMacRandomization =
200                         getBoolean("disableMacRandomization", false);
201 
202                 result.putInt(
203                         "result",
204                         connector.addNetwork(ssid, psk, scanSsid, disableMacRandomization));
205 
206             } else if ("associateNetwork".equals(method)) {
207                 final int id = expectInteger("id");
208 
209                 result.putBoolean("result",
210                         wifiManager.enableNetwork(id, true /* disable other networks */));
211 
212             } else if ("disconnect".equals(method)) {
213                 result.putBoolean("result", wifiManager.disconnect());
214 
215             } else if ("disableNetwork".equals(method)) {
216                 final int id = expectInteger("id");
217 
218                 result.putBoolean("result", wifiManager.disableNetwork(id));
219 
220             } else if ("isWifiEnabled".equals(method)) {
221                 result.putBoolean("result", wifiManager.isWifiEnabled());
222 
223             } else if ("getIpAddress".equals(method)) {
224                 final WifiInfo info = wifiManager.getConnectionInfo();
225                 final int addr = info.getIpAddress();
226 
227                 // IP address is stored with the first octet in the lowest byte
228                 final int a = (addr >> 0) & 0xff;
229                 final int b = (addr >> 8) & 0xff;
230                 final int c = (addr >> 16) & 0xff;
231                 final int d = (addr >> 24) & 0xff;
232 
233                 result.putString("result", String.format("%s.%s.%s.%s", a, b, c, d));
234 
235             } else if ("getSSID".equals(method)) {
236                 final WifiInfo info = wifiManager.getConnectionInfo();
237 
238                 result.putString("result", info.getSSID());
239 
240             } else if ("getBSSID".equals(method)) {
241                 final WifiInfo info = wifiManager.getConnectionInfo();
242 
243                 result.putString("result", info.getBSSID());
244 
245             } else if ("removeAllNetworks".equals(method)) {
246                 connector.removeAllNetworks(true);
247 
248                 result.putBoolean("result", true);
249 
250             } else if ("removeNetwork".equals(method)) {
251                 final int id = expectInteger("id");
252 
253                 result.putBoolean("result", wifiManager.removeNetwork(id));
254 
255             } else if ("saveConfiguration".equals(method)) {
256                 result.putBoolean("result", wifiManager.saveConfiguration());
257 
258             } else if ("getSupplicantState".equals(method)) {
259                 String state = wifiManager.getConnectionInfo().getSupplicantState().name();
260                 result.putString("result", state);
261 
262             } else if ("checkConnectivity".equals(method)) {
263                 final String url = getString("urlToCheck", DEFAULT_URL_TO_CHECK);
264 
265                 result.putBoolean("result", connector.checkConnectivity(url));
266 
267             } else if ("connectToNetwork".equals(method)) {
268                 final String ssid = expectString("ssid");
269                 final boolean scanSsid = getBoolean("scan_ssid", false);
270                 final String psk = getString("psk", null);
271                 final String pingUrl = getString("urlToCheck", DEFAULT_URL_TO_CHECK);
272                 final long connectTimeout = getInteger("connectTimeout", -1);
273                 final boolean disableMacRandomization =
274                         getBoolean("disableMacRandomization", false);
275                 connector.connectToNetwork(
276                         ssid, psk, pingUrl, connectTimeout, scanSsid, disableMacRandomization);
277 
278                 result.putBoolean("result", true);
279 
280             } else if ("disconnectFromNetwork".equals(method)) {
281                 connector.disconnectFromNetwork();
282 
283                 result.putBoolean("result", true);
284 
285             } else if ("getWifiInfo".equals(method)) {
286                 result.putString("result", connector.getWifiInfo().toString());
287 
288             } else if ("startMonitor".equals(method)) {
289                 final int interval = expectInteger("interval");
290                 final String urlToCheck = getString("urlToCheck", DEFAULT_URL_TO_CHECK);
291 
292                 WifiMonitorService.enable(getContext(), interval, urlToCheck);
293 
294                 result.putBoolean("result", true);
295 
296             } else if ("stopMonitor".equals(method)) {
297                 final Context context = getContext();
298                 WifiMonitorService.disable(context);
299 
300                 result.putString("result", WifiMonitorService.getData(context));
301 
302             } else {
303                 fail(String.format("Didn't recognize method '%s'", method));
304                 return;
305             }
306         } catch (WifiException | MissingArgException e) {
307             fail(e.getMessage());
308             return;
309         }
310 
311         finish(Activity.RESULT_OK, result);
312     }
313 }
314