aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/google/android
diff options
context:
space:
mode:
authorAng Li <angli@google.com>2017-01-23 15:46:10 -0800
committerAlexander Dorokhine <adorokhine@google.com>2017-01-30 15:13:56 -0800
commit0eb024bbab6f81d26a48e6df2acb729bca38c1a6 (patch)
tree21815cf8358f5f34580d7406757cbc4dae6e669a /src/main/java/com/google/android
parentae805aaa07788a96b807e0ce064724bf0e850799 (diff)
downloadmobly-bundled-snippets-0eb024bbab6f81d26a48e6df2acb729bca38c1a6.tar.gz
Utilities, conventions, serialize/deserialize mechanism, and basic Wi-Fi APIs
* Add basic Wi-Fi API support. * Make bluetooth snippet style consistent with Wi-Fi. * Add the mechanism to serialize and deserialize data types. * Create a util for waiting on asynch operations. * Enable Java8 for the project. * Add formatter for the project. Fixes #1.
Diffstat (limited to 'src/main/java/com/google/android')
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/BluetoothAdapterSnippet.java40
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/WifiManagerSnippet.java230
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonDeserializer.java43
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonSerializer.java120
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/utils/Utils.java54
5 files changed, 462 insertions, 25 deletions
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/BluetoothAdapterSnippet.java b/src/main/java/com/google/android/mobly/snippet/bundled/BluetoothAdapterSnippet.java
index a830ad9..79268da 100644
--- a/src/main/java/com/google/android/mobly/snippet/bundled/BluetoothAdapterSnippet.java
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/BluetoothAdapterSnippet.java
@@ -19,13 +19,14 @@ package com.google.android.mobly.snippet.bundled;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
-
import com.google.android.mobly.snippet.Snippet;
+import com.google.android.mobly.snippet.bundled.utils.Utils;
import com.google.android.mobly.snippet.rpc.Rpc;
+/** Snippet class exposing Android APIs in BluetoothAdapter. */
public class BluetoothAdapterSnippet implements Snippet {
- private static class BluetoothException extends Exception {
- public BluetoothException(String msg) {
+ private static class BluetoothAdapterSnippetException extends Exception {
+ public BluetoothAdapterSnippetException(String msg) {
super(msg);
}
}
@@ -38,37 +39,26 @@ public class BluetoothAdapterSnippet implements Snippet {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
- @Rpc(description = "Enable bluetooth")
- public void bluetoothEnable() throws BluetoothException, InterruptedException {
+ @Rpc(description = "Enable bluetooth with a 30s timeout.")
+ public void bluetoothEnable() throws BluetoothAdapterSnippetException, InterruptedException {
if (!mBluetoothAdapter.enable()) {
- throw new BluetoothException("Failed to start enabling bluetooth");
- }
- int timeout = 30;
- while (!mBluetoothAdapter.isEnabled() && timeout >= 0) {
- Thread.sleep(1000);
- timeout -= 1;
+ throw new BluetoothAdapterSnippetException("Failed to start enabling bluetooth");
}
- if (!mBluetoothAdapter.isEnabled()) {
- throw new BluetoothException("Bluetooth did not turn on before timeout");
+ if (!Utils.waitUntil(() -> mBluetoothAdapter.isEnabled(), 30)) {
+ throw new BluetoothAdapterSnippetException("Bluetooth did not turn on within 30s.");
}
}
- @Rpc(description = "Disable bluetooth")
- public void bluetoothDisable() throws BluetoothException, InterruptedException {
+ @Rpc(description = "Disable bluetooth with a 30s timeout.")
+ public void bluetoothDisable() throws BluetoothAdapterSnippetException, InterruptedException {
if (!mBluetoothAdapter.disable()) {
- throw new BluetoothException("Failed to start disabling bluetooth");
+ throw new BluetoothAdapterSnippetException("Failed to start disabling bluetooth");
}
- int timeout = 30;
- while (mBluetoothAdapter.isEnabled() && timeout >= 0) {
- Thread.sleep(1000);
- timeout -= 1;
- }
- if (mBluetoothAdapter.isEnabled()) {
- throw new BluetoothException("Bluetooth did not turn off before timeout");
+ if (!Utils.waitUntil(() -> !mBluetoothAdapter.isEnabled(), 30)) {
+ throw new BluetoothAdapterSnippetException("Bluetooth did not turn off within 30s.");
}
}
@Override
- public void shutdown() {
- }
+ public void shutdown() {}
}
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/WifiManagerSnippet.java b/src/main/java/com/google/android/mobly/snippet/bundled/WifiManagerSnippet.java
new file mode 100644
index 0000000..1dca048
--- /dev/null
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/WifiManagerSnippet.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * 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.google.android.mobly.snippet.bundled;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import com.google.android.mobly.snippet.Snippet;
+import com.google.android.mobly.snippet.bundled.utils.JsonDeserializer;
+import com.google.android.mobly.snippet.bundled.utils.JsonSerializer;
+import com.google.android.mobly.snippet.bundled.utils.Utils;
+import com.google.android.mobly.snippet.rpc.Rpc;
+import com.google.android.mobly.snippet.util.Log;
+import java.util.ArrayList;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/** Snippet class exposing Android APIs in WifiManager. */
+public class WifiManagerSnippet implements Snippet {
+ private static class WifiManagerSnippetException extends Exception {
+ public WifiManagerSnippetException(String msg) {
+ super(msg);
+ }
+ }
+
+ private final WifiManager mWifiManager;
+ private final Context mContext;
+ private static final String TAG = "WifiManagerSnippet";
+ private final JsonSerializer mJsonSerializer = new JsonSerializer();
+ private volatile boolean mIsScanning = false;
+ private volatile boolean mIsScanResultAvailable = false;
+
+ public WifiManagerSnippet() {
+ mContext = InstrumentationRegistry.getContext();
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ @Rpc(description = "Turns on Wi-Fi with a 30s timeout.")
+ public void wifiEnable() throws InterruptedException, WifiManagerSnippetException {
+ if (!mWifiManager.setWifiEnabled(true)) {
+ throw new WifiManagerSnippetException("Failed to initiate enabling Wi-Fi.");
+ }
+ if (!Utils.waitUntil(
+ () -> mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED, 30)) {
+ throw new WifiManagerSnippetException("Failed to enable Wi-Fi after 30s, timeout!");
+ }
+ }
+
+ @Rpc(description = "Turns off Wi-Fi with a 30s timeout.")
+ public void wifiDisable() throws InterruptedException, WifiManagerSnippetException {
+ if (!mWifiManager.setWifiEnabled(false)) {
+ throw new WifiManagerSnippetException("Failed to initiate disabling Wi-Fi.");
+ }
+ if (!Utils.waitUntil(
+ () -> mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED, 30)) {
+ throw new WifiManagerSnippetException("Failed to disable Wi-Fi after 30s, timeout!");
+ }
+ }
+
+ @Rpc(description = "Trigger Wi-Fi scan.")
+ public void wifiStartScan() throws WifiManagerSnippetException {
+ if (!mWifiManager.startScan()) {
+ throw new WifiManagerSnippetException("Failed to initiate Wi-Fi scan.");
+ }
+ }
+
+ @Rpc(
+ description =
+ "Get Wi-Fi scan results, which is a list of serialized WifiScanResult objects."
+ )
+ public JSONArray wifiGetCachedScanResults() throws JSONException {
+ JSONArray results = new JSONArray();
+ for (ScanResult result : mWifiManager.getScanResults()) {
+ results.put(mJsonSerializer.toJson(result));
+ }
+ return results;
+ }
+
+ @Rpc(
+ description =
+ "Start scan, wait for scan to complete, and return results, which is a list of "
+ + "serialized WifiScanResult objects."
+ )
+ public JSONArray wifiScanAndGetResults()
+ throws InterruptedException, JSONException, WifiManagerSnippetException {
+ mContext.registerReceiver(
+ new WifiScanReceiver(),
+ new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ wifiStartScan();
+ mIsScanResultAvailable = false;
+ mIsScanning = true;
+ if (!Utils.waitUntil(() -> mIsScanResultAvailable, 2 * 60)) {
+ throw new WifiManagerSnippetException(
+ "Failed to get scan results after 2min, timeout!");
+ }
+ return wifiGetCachedScanResults();
+ }
+
+ @Rpc(
+ description =
+ "Connects to a Wi-Fi network. This covers the common network types like open and "
+ + "WPA2."
+ )
+ public void wifiConnectSimple(String ssid, @Nullable String password)
+ throws InterruptedException, JSONException, WifiManagerSnippetException {
+ JSONObject config = new JSONObject();
+ config.put("SSID", ssid);
+ if (password != null) {
+ config.put("password", password);
+ }
+ wifiConnect(config);
+ }
+
+ /**
+ * Connect to a Wi-Fi network.
+ *
+ * @param wifiNetworkConfig A JSON object that contains the info required to connect to a Wi-Fi
+ * network. It follows the fields of WifiConfiguration type, e.g. {"SSID": "myWifi",
+ * "password": "12345678"}.
+ * @throws InterruptedException
+ * @throws JSONException
+ * @throws WifiManagerSnippetException
+ */
+ @Rpc(description = "Connects to a Wi-Fi network.")
+ public void wifiConnect(JSONObject wifiNetworkConfig)
+ throws InterruptedException, JSONException, WifiManagerSnippetException {
+ Log.d("Got network config: " + wifiNetworkConfig);
+ WifiConfiguration wifiConfig = JsonDeserializer.jsonToWifiConfig(wifiNetworkConfig);
+ int networkId = mWifiManager.addNetwork(wifiConfig);
+ mWifiManager.disconnect();
+ if (!mWifiManager.enableNetwork(networkId, true)) {
+ throw new WifiManagerSnippetException(
+ "Failed to enable Wi-Fi network of ID: " + networkId);
+ }
+ if (!mWifiManager.reconnect()) {
+ throw new WifiManagerSnippetException(
+ "Failed to reconnect to Wi-Fi network of ID: " + networkId);
+ }
+ if (!Utils.waitUntil(
+ () -> mWifiManager.getConnectionInfo().getSSID().equals(wifiConfig.SSID), 90)) {
+ throw new WifiManagerSnippetException(
+ "Failed to connect to Wi-Fi network "
+ + wifiNetworkConfig.toString()
+ + ", timeout!");
+ }
+ Log.d(
+ "Connected to network '"
+ + mWifiManager.getConnectionInfo().getSSID()
+ + "' with ID "
+ + mWifiManager.getConnectionInfo().getNetworkId());
+ }
+
+ @Rpc(
+ description =
+ "Forget a configured Wi-Fi network by its network ID, which is part of the"
+ + " WifiConfiguration."
+ )
+ public void wifiRemoveNetwork(Integer networkId) throws WifiManagerSnippetException {
+ if (!mWifiManager.removeNetwork(networkId)) {
+ throw new WifiManagerSnippetException("Failed to remove network of ID: " + networkId);
+ }
+ }
+
+ @Rpc(
+ description =
+ "Get the list of configured Wi-Fi networks, each is a serialized "
+ + "WifiConfiguration object."
+ )
+ public ArrayList<JSONObject> wifiGetConfiguredNetworks() throws JSONException {
+ ArrayList<JSONObject> networks = new ArrayList<>();
+ for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
+ networks.add(mJsonSerializer.toJson(config));
+ }
+ return networks;
+ }
+
+ @Rpc(
+ description =
+ "Get the information about the active Wi-Fi connection, which is a serialized "
+ + "WifiInfo object."
+ )
+ public JSONObject wifiGetConnectionInfo() throws JSONException {
+ return mJsonSerializer.toJson(mWifiManager.getConnectionInfo());
+ }
+
+ @Rpc(
+ description =
+ "Get the info from last successful DHCP request, which is a serialized DhcpInfo "
+ + "object."
+ )
+ public JSONObject wifiGetDhcpInfo() throws JSONException {
+ return mJsonSerializer.toJson(mWifiManager.getDhcpInfo());
+ }
+
+ @Override
+ public void shutdown() {}
+
+ private class WifiScanReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context c, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ mIsScanning = false;
+ mIsScanResultAvailable = true;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonDeserializer.java b/src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonDeserializer.java
new file mode 100644
index 0000000..4fc6b82
--- /dev/null
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonDeserializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * 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.google.android.mobly.snippet.bundled.utils;
+
+import android.net.wifi.WifiConfiguration;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * A collection of methods used to deserialize JSON strings into data objects defined in Android
+ * API.
+ */
+public class JsonDeserializer {
+
+ private JsonDeserializer() {}
+
+ public static WifiConfiguration jsonToWifiConfig(JSONObject jsonObject) throws JSONException {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "\"" + jsonObject.getString("SSID") + "\"";
+ config.hiddenSSID = jsonObject.optBoolean("hiddenSSID", false);
+ if (jsonObject.has("password")) {
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ config.preSharedKey = "\"" + jsonObject.getString("password") + "\"";
+ } else {
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ }
+ return config;
+ }
+}
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonSerializer.java b/src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonSerializer.java
new file mode 100644
index 0000000..58f8bac
--- /dev/null
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonSerializer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * 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.google.android.mobly.snippet.bundled.utils;
+
+import android.net.DhcpInfo;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.lang.reflect.Modifier;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * A collection of methods used to serialize data types defined in Android API into JSON strings.
+ */
+public class JsonSerializer {
+ private static Gson mGson;
+
+ public JsonSerializer() {
+ GsonBuilder builder = new GsonBuilder();
+ mGson =
+ builder.serializeNulls()
+ .excludeFieldsWithModifiers(Modifier.STATIC)
+ .enableComplexMapKeySerialization()
+ .disableInnerClassSerialization()
+ .create();
+ }
+
+ /**
+ * Remove the extra quotation marks from the beginning and the end of a string.
+ *
+ * <p>This is useful for strings like the SSID field of Android's Wi-Fi configuration.
+ *
+ * @param originalString
+ */
+ private String trimQuotationMarks(String originalString) {
+ String result = originalString;
+ if (originalString.charAt(0) == '"'
+ && originalString.charAt(originalString.length() - 1) == '"') {
+ result = originalString.substring(1, originalString.length() - 1);
+ }
+ return result;
+ }
+
+ public JSONObject toJson(Object object) throws JSONException {
+ if (object instanceof DhcpInfo) {
+ return serializeDhcpInfo((DhcpInfo) object);
+ } else if (object instanceof WifiConfiguration) {
+ return serializeWifiConfiguration((WifiConfiguration) object);
+ } else if (object instanceof WifiInfo) {
+ return serializeWifiInfo((WifiInfo) object);
+ }
+ return defaultSerialization(object);
+ }
+
+ /**
+ * By default, we rely on Gson to do the right job.
+ *
+ * @param data An object to serialize
+ * @return A JSONObject that has the info of the serialized data object.
+ * @throws JSONException
+ */
+ private JSONObject defaultSerialization(Object data) throws JSONException {
+ return new JSONObject(mGson.toJson(data));
+ }
+
+ private JSONObject serializeDhcpInfo(DhcpInfo data) throws JSONException {
+ JSONObject result = new JSONObject(mGson.toJson(data));
+ int ipAddress = data.ipAddress;
+ byte[] addressBytes = {
+ (byte) (0xff & ipAddress),
+ (byte) (0xff & (ipAddress >> 8)),
+ (byte) (0xff & (ipAddress >> 16)),
+ (byte) (0xff & (ipAddress >> 24))
+ };
+ try {
+ String addressString = InetAddress.getByAddress(addressBytes).toString();
+ result.put("IpAddress", addressString);
+ } catch (UnknownHostException e) {
+ result.put("IpAddress", ipAddress);
+ }
+ return result;
+ }
+
+ private JSONObject serializeWifiConfiguration(WifiConfiguration data) throws JSONException {
+ JSONObject result = new JSONObject(mGson.toJson(data));
+ result.put("Status", WifiConfiguration.Status.strings[data.status]);
+ result.put("SSID", trimQuotationMarks(data.SSID));
+ return result;
+ }
+
+ private JSONObject serializeWifiInfo(WifiInfo data) throws JSONException {
+ JSONObject result = new JSONObject(mGson.toJson(data));
+ result.put("SSID", trimQuotationMarks(data.getSSID()));
+ for (SupplicantState state : SupplicantState.values()) {
+ if (data.getSupplicantState().equals(state)) {
+ result.put("SupplicantState", state.name());
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/utils/Utils.java b/src/main/java/com/google/android/mobly/snippet/bundled/utils/Utils.java
new file mode 100644
index 0000000..00bfbd2
--- /dev/null
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/utils/Utils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * 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.google.android.mobly.snippet.bundled.utils;
+
+public final class Utils {
+ private Utils() {}
+
+ /**
+ * Waits util a condition is met.
+ *
+ * <p>This is often used to wait for asynchronous operations to finish and the system to reach a
+ * desired state.
+ *
+ * @param predicate A lambda function that specifies the condition to wait for. This function
+ * should return true when the desired state has been reached.
+ * @param timeout The number of seconds to wait for before giving up.
+ * @return true if the operation finished before timeout, false otherwise.
+ * @throws InterruptedException
+ */
+ public static boolean waitUntil(Utils.Predicate predicate, int timeout)
+ throws InterruptedException {
+ timeout *= 10;
+ while (!predicate.waitCondition() && timeout >= 0) {
+ Thread.sleep(100);
+ timeout -= 1;
+ }
+ if (predicate.waitCondition()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * A function interface that is used by lambda functions signaling an async operation is still
+ * going on.
+ */
+ public interface Predicate {
+ boolean waitCondition();
+ }
+}