/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tradefed.device; import com.android.ddmlib.MultiLineReceiver; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.targetprep.TargetSetupError; import com.android.tradefed.util.FileUtil; import com.android.tradefed.util.IRunUtil; import com.android.tradefed.util.RunUtil; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Helper class for manipulating wifi services on device. */ public class WifiHelper implements IWifiHelper { private static final String NULL_IP_ADDR = "0.0.0.0"; private static final String INSTRUMENTATION_CLASS = ".WifiUtil"; public static final String INSTRUMENTATION_PKG = "com.android.tradefed.utils.wifi"; static final String FULL_INSTRUMENTATION_NAME = String.format("%s/%s", INSTRUMENTATION_PKG, INSTRUMENTATION_CLASS); static final String CHECK_INSTRUMENTATION_CMD = String.format( "pm list instrumentation %s", INSTRUMENTATION_PKG); private static final String WIFIUTIL_APK_NAME = "WifiUtil.apk"; /** the default time in ms to wait for a wifi state */ private static final long DEFAULT_WIFI_STATE_TIMEOUT = 30 * 1000; private final ITestDevice mDevice; public WifiHelper(ITestDevice device) throws TargetSetupError, DeviceNotAvailableException { mDevice = device; ensureDeviceSetup(); } /** * Get the {@link RunUtil} instance to use. * <p/> * Exposed for unit testing. */ IRunUtil getRunUtil() { return RunUtil.getDefault(); } void ensureDeviceSetup() throws TargetSetupError, DeviceNotAvailableException { final String inst = mDevice .executeShellCommand(CHECK_INSTRUMENTATION_CMD); if ((inst != null) && inst.contains(FULL_INSTRUMENTATION_NAME)) { // Good to go return; } else { // Attempt to install utility File apkTempFile = null; try { apkTempFile = extractWifiUtilApk(); final String result = mDevice .installPackage(apkTempFile, false); if (result == null) { // Installed successfully; good to go. return; } else { throw new TargetSetupError(String.format( "Unable to install WifiUtil utility: %s", result)); } } catch (IOException e) { throw new TargetSetupError( String.format("Failed to unpack WifiUtil utility: %s", e.getMessage())); } finally { FileUtil.deleteFile(apkTempFile); } } } /** * Helper method to extract the wifi util apk from the classpath */ public static File extractWifiUtilApk() throws IOException { File apkTempFile; apkTempFile = FileUtil.createTempFile(WIFIUTIL_APK_NAME, ".apk"); InputStream apkStream = WifiHelper.class.getResourceAsStream(String .format("/apks/wifiutil/%s", WIFIUTIL_APK_NAME)); FileUtil.writeToFile(apkStream, apkTempFile); return apkTempFile; } /** * {@inheritDoc} */ @Override public boolean enableWifi() throws DeviceNotAvailableException { return asBool(runWifiUtil("enableWifi")); } /** * {@inheritDoc} */ @Override public boolean disableWifi() throws DeviceNotAvailableException { return asBool(runWifiUtil("disableWifi")); } /** * {@inheritDoc} */ @Override public boolean waitForWifiState(WifiState... expectedStates) throws DeviceNotAvailableException { return waitForWifiState(DEFAULT_WIFI_STATE_TIMEOUT, expectedStates); } /** * Waits the given time until one of the expected wifi states occurs. * * @param expectedStates * one or more wifi states to expect * @param timeout * max time in ms to wait * @return <code>true</code> if the one of the expected states occurred. * <code>false</code> if none of the states occurred before timeout * is reached * @throws DeviceNotAvailableException */ boolean waitForWifiState(long timeout, WifiState... expectedStates) throws DeviceNotAvailableException { long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() < (startTime + timeout)) { String state = runWifiUtil("getSupplicantState"); for (WifiState expectedState : expectedStates) { if (expectedState.name().equals(state)) { return true; } } getRunUtil().sleep(getPollTime()); } return false; } /** * Gets the time to sleep between poll attempts */ long getPollTime() { return 1 * 1000; } /** * Remove the network identified by an integer network id. * * @param networkId * the network id identifying its profile in wpa_supplicant * configuration * @throws DeviceNotAvailableException */ void removeNetwork(int networkId) throws DeviceNotAvailableException { runWifiUtil("removeNetwork", "id", Integer.toString(networkId)); } /** * {@inheritDoc} */ @Override public boolean addOpenNetwork(String ssid) throws DeviceNotAvailableException { int id = asInt(runWifiUtil("addOpenNetwork", "ssid", ssid)); if (id < 0) { return false; } if (!asBool(runWifiUtil("associateNetwork", "id", Integer.toString(id)))) { return false; } if (!asBool(runWifiUtil("saveConfiguration"))) { return false; } return true; } /** * {@inheritDoc} */ @Override public boolean addWpaPskNetwork(String ssid, String psk) throws DeviceNotAvailableException { int id = asInt(runWifiUtil("addWpaPskNetwork", "ssid", ssid, "psk", psk)); if (id < 0) { return false; } if (!asBool(runWifiUtil("associateNetwork", "id", Integer.toString(id)))) { return false; } if (!asBool(runWifiUtil("saveConfiguration"))) { return false; } return true; } /** * {@inheritDoc} */ @Override public boolean waitForIp(long timeout) throws DeviceNotAvailableException { long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() < (startTime + timeout)) { if (hasValidIp()) { return true; } getRunUtil().sleep(getPollTime()); } return false; } /** * {@inheritDoc} */ @Override public boolean hasValidIp() throws DeviceNotAvailableException { final String ip = getIpAddress(); return ip != null && !ip.isEmpty() && !NULL_IP_ADDR.equals(ip); } /** * {@inheritDoc} */ @Override public String getIpAddress() throws DeviceNotAvailableException { return runWifiUtil("getIpAddress"); } /** * {@inheritDoc} */ @Override public String getSSID() throws DeviceNotAvailableException { return runWifiUtil("getSSID"); } /** * {@inheritDoc} */ @Override public void removeAllNetworks() throws DeviceNotAvailableException { runWifiUtil("removeAllNetworks"); } /** * {@inheritDoc} */ @Override public boolean isWifiEnabled() throws DeviceNotAvailableException { return asBool(runWifiUtil("isWifiEnabled")); } /** * {@inheritDoc} */ @Override public boolean waitForWifiEnabled() throws DeviceNotAvailableException { return waitForWifiEnabled(DEFAULT_WIFI_STATE_TIMEOUT); } @Override public boolean waitForWifiEnabled(long timeout) throws DeviceNotAvailableException { long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() < (startTime + timeout)) { if (isWifiEnabled()) { return true; } getRunUtil().sleep(getPollTime()); } return false; } /** * Run a WifiUtil command and return the result * * @param method * the WifiUtil method to call * @param args * a flat list of [arg-name, value] pairs to pass * @return The value of the result field in the output, or <code>null</code> * if result could not be parsed */ private String runWifiUtil(String method, String... args) throws DeviceNotAvailableException { final String cmd = buildWifiUtilCmd(method, args); CLog.d(String.format("向wifi app发送命令 %s", cmd)); WifiUtilOutput parser = new WifiUtilOutput(); mDevice.executeShellCommand(cmd, parser); return parser.getResult(); } /** * Build and return a WifiUtil command for the specified method and args * * @param method * the WifiUtil method to call * @param args * a flat list of [arg-name, value] pairs to pass * @return the command to be executed on the device shell */ static String buildWifiUtilCmd(String method, String... args) { Map<String, String> argMap = new HashMap<String, String>(); argMap.put("method", method); if ((args.length & 0x1) == 0x1) { throw new IllegalArgumentException( "args should have even length, consisting of key and value pairs"); } for (int i = 0; i < args.length; i += 2) { argMap.put(args[i], args[i + 1]); } return buildWifiUtilCmdFromMap(argMap); } /** * Build and return a WifiUtil command for the specified args * * @param args * A Map of (arg-name, value) pairs to pass as "-e" arguments to * the `am` command * @return the commadn to be executed on the device shell */ static String buildWifiUtilCmdFromMap(Map<String, String> args) { StringBuilder sb = new StringBuilder("am instrument"); for (Map.Entry<String, String> arg : args.entrySet()) { sb.append(" -e "); sb.append(arg.getKey()); sb.append(" "); sb.append(quote(arg.getValue())); } sb.append(" -w "); sb.append(INSTRUMENTATION_PKG); sb.append("/"); sb.append(INSTRUMENTATION_CLASS); return sb.toString(); } /** * Helper function to convert a String to an Integer */ private static int asInt(String str) { if (str == null) { return -1; } try { return Integer.parseInt(str); } catch (NumberFormatException e) { return -1; } } /** * Helper function to convert a String to a boolean. Maps "true" to true, * and everything else to false. */ private static boolean asBool(String str) { return "true".equals(str); } /** * Helper function to wrap the specified String in double-quotes to prevent * shell interpretation */ private static String quote(String str) { return String.format("\"%s\"", str); } /** * Processes the output of a WifiUtil invocation */ private static class WifiUtilOutput extends MultiLineReceiver { private static final Pattern RESULT_PAT = Pattern .compile("INSTRUMENTATION_RESULT: result=(.*)"); private String mResult = null; /** * {@inheritDoc} */ @Override public void processNewLines(String[] lines) { for (String line : lines) { Matcher resultMatcher = RESULT_PAT.matcher(line); if (resultMatcher.matches()) { mResult = resultMatcher.group(1); } } } /** * Return the result flag parsed from instrmentation output. * <code>null</code> is returned if result output was not present. */ String getResult() { return mResult; } /** * {@inheritDoc} */ @Override public boolean isCancelled() { return false; } } }