aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/AndroidManifest.xml2
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/AudioSnippet.java21
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/WifiManagerSnippet.java12
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/bluetooth/BluetoothAdapterSnippet.java53
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/utils/JsonSerializer.java41
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/utils/RpcEnum.java7
6 files changed, 113 insertions, 23 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 795c063..08341c3 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -11,7 +11,9 @@
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/AudioSnippet.java b/src/main/java/com/google/android/mobly/snippet/bundled/AudioSnippet.java
index 9b4874f..10a2c07 100644
--- a/src/main/java/com/google/android/mobly/snippet/bundled/AudioSnippet.java
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/AudioSnippet.java
@@ -104,6 +104,24 @@ public class AudioSnippet implements Snippet {
AudioManager.STREAM_VOICE_CALL, value, 0 /* flags, 0 = no flags */);
}
+ @Rpc(description = "Gets the alarm volume.")
+ public Integer getAlarmVolume() {
+ return mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
+ }
+
+ @Rpc(description = "Gets the maximum alarm volume value.")
+ public int getAlarmMaxVolume() {
+ return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM);
+ }
+
+ @Rpc(
+ description =
+ "Sets the alarm stream volume. The minimum value is 0. Use 'getAlarmMaxVolume'"
+ + " to determine the maximum.")
+ public void setAlarmVolume(Integer value) {
+ mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, value, 0 /* flags, 0 = no flags */);
+ }
+
@Rpc(description = "Silences all audio streams.")
public void muteAll() throws Exception {
/* Get numStreams from AudioSystem through reflection. If for some reason this fails,
@@ -129,6 +147,9 @@ public class AudioSnippet implements Snippet {
setMusicVolume(0);
}
+ @Rpc(description = "Mute alarm stream.")
+ public void muteAlarm() { setAlarmVolume(0); }
+
@Override
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
index cf577c3..7e1a416 100644
--- a/src/main/java/com/google/android/mobly/snippet/bundled/WifiManagerSnippet.java
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/WifiManagerSnippet.java
@@ -88,8 +88,18 @@ public class WifiManagerSnippet implements Snippet {
failedConfigs.add(config);
}
}
+
+ // If removeNetwork is called on a network with both an open and OWE config, it will remove
+ // both. The subsequent call on the same network will fail. The clear operation may succeed
+ // even if failures appear in the log below.
if (!failedConfigs.isEmpty()) {
- throw new WifiManagerSnippetException("Failed to remove networks: " + failedConfigs);
+ Log.e("Encountered error while removing networks: " + failedConfigs);
+ }
+
+ // Re-check configured configs list to ensure that it is cleared
+ unremovedConfigs = mWifiManager.getConfiguredNetworks();
+ if (!unremovedConfigs.isEmpty()) {
+ throw new WifiManagerSnippetException("Failed to remove networks: " + unremovedConfigs);
}
}
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/bluetooth/BluetoothAdapterSnippet.java b/src/main/java/com/google/android/mobly/snippet/bundled/bluetooth/BluetoothAdapterSnippet.java
index 6e66e43..c16a2b0 100644
--- a/src/main/java/com/google/android/mobly/snippet/bundled/bluetooth/BluetoothAdapterSnippet.java
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/bluetooth/BluetoothAdapterSnippet.java
@@ -25,6 +25,10 @@ import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
import com.google.android.mobly.snippet.Snippet;
import com.google.android.mobly.snippet.bundled.utils.JsonSerializer;
import com.google.android.mobly.snippet.bundled.utils.Utils;
@@ -34,6 +38,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
import org.json.JSONException;
/** Snippet class exposing Android APIs in BluetoothAdapter. */
@@ -46,6 +51,10 @@ public class BluetoothAdapterSnippet implements Snippet {
public BluetoothAdapterSnippetException(String msg) {
super(msg);
}
+
+ public BluetoothAdapterSnippetException(String msg, Throwable err) {
+ super(msg, err);
+ }
}
// Timeout to measure consistent BT state.
@@ -93,6 +102,16 @@ public class BluetoothAdapterSnippet implements Snippet {
return null;
}
+ /* Gets the UiDevice instance for UI operations. */
+ private static UiDevice getUiDevice() throws BluetoothAdapterSnippetException {
+ try {
+ return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ } catch (IllegalStateException e) {
+ throw new BluetoothAdapterSnippetException("Failed to get UiDevice. Please ensure that "
+ + "no other UiAutomation service is running.", e);
+ }
+ }
+
@Rpc(description = "Enable bluetooth with a 30s timeout.")
public void btEnable() throws BluetoothAdapterSnippetException, InterruptedException {
if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
@@ -100,7 +119,19 @@ public class BluetoothAdapterSnippet implements Snippet {
}
waitForStableBtState();
- if (!mBluetoothAdapter.enable()) {
+ if (Build.VERSION.SDK_INT >= 33) {
+ // BluetoothAdapter#enable is removed from public SDK for 33 and above, so uses an
+ // intent instead.
+ UiDevice uiDevice = getUiDevice();
+ Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ enableIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Triggers the system UI popup to ask for explicit permission.
+ mContext.startActivity(enableIntent);
+ // Clicks the "ALLOW" button.
+ BySelector allowButtonSelector = By.text(TEXT_PATTERN_ALLOW).clickable(true);
+ uiDevice.wait(Until.findObject(allowButtonSelector), 10);
+ uiDevice.findObject(allowButtonSelector).click();
+ } else if (!mBluetoothAdapter.enable()) {
throw new BluetoothAdapterSnippetException("Failed to start enabling bluetooth.");
}
if (!Utils.waitUntil(
@@ -200,7 +231,20 @@ public class BluetoothAdapterSnippet implements Snippet {
throw new BluetoothAdapterSnippetException(
"Bluetooth is not enabled, cannot become discoverable.");
}
- if (Build.VERSION.SDK_INT > 29) {
+ if (Build.VERSION.SDK_INT >= 31) {
+ // BluetoothAdapter#setScanMode is removed from public SDK for 31 and above, so uses an
+ // intent instead.
+ UiDevice uiDevice = getUiDevice();
+ Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
+ discoverableIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration);
+ // Triggers the system UI popup to ask for explicit permission.
+ mContext.startActivity(discoverableIntent);
+ // Clicks the "ALLOW" button.
+ BySelector allowButtonSelector = By.text(TEXT_PATTERN_ALLOW).clickable(true);
+ uiDevice.wait(Until.findObject(allowButtonSelector), 10);
+ uiDevice.findObject(allowButtonSelector).click();
+ } else if (Build.VERSION.SDK_INT >= 30) {
if (!(boolean)
Utils.invokeByReflection(
mBluetoothAdapter,
@@ -221,6 +265,9 @@ public class BluetoothAdapterSnippet implements Snippet {
}
}
+ private static final Pattern TEXT_PATTERN_ALLOW =
+ Pattern.compile("allow", Pattern.CASE_INSENSITIVE);
+
@Rpc(description = "Cancel ongoing bluetooth discovery.")
public void btCancelDiscovery() throws BluetoothAdapterSnippetException {
if (!mBluetoothAdapter.isDiscovering()) {
@@ -305,7 +352,7 @@ public class BluetoothAdapterSnippet implements Snippet {
return;
}
}
- throw new NoSuchElementException("No device wih address " + deviceAddress + " is paired.");
+ throw new NoSuchElementException("No device with address " + deviceAddress + " is paired.");
}
@Override
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
index 82e1e4f..82e6f7c 100644
--- 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
@@ -27,6 +27,7 @@ import android.net.wifi.WifiInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelUuid;
+import android.util.SparseArray;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.lang.reflect.Modifier;
@@ -41,17 +42,13 @@ 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();
- }
+ private static final Gson gson =
+ new GsonBuilder()
+ .serializeNulls()
+ .excludeFieldsWithModifiers(Modifier.STATIC)
+ .enableComplexMapKeySerialization()
+ .disableInnerClassSerialization()
+ .create();
/**
* Remove the extra quotation marks from the beginning and the end of a string.
@@ -89,11 +86,11 @@ public class JsonSerializer {
* @throws JSONException
*/
private JSONObject defaultSerialization(Object data) throws JSONException {
- return new JSONObject(mGson.toJson(data));
+ return new JSONObject(gson.toJson(data));
}
private JSONObject serializeDhcpInfo(DhcpInfo data) throws JSONException {
- JSONObject result = new JSONObject(mGson.toJson(data));
+ JSONObject result = new JSONObject(gson.toJson(data));
int ipAddress = data.ipAddress;
byte[] addressBytes = {
(byte) (0xff & ipAddress),
@@ -111,14 +108,14 @@ public class JsonSerializer {
}
private JSONObject serializeWifiConfiguration(WifiConfiguration data) throws JSONException {
- JSONObject result = new JSONObject(mGson.toJson(data));
+ JSONObject result = new JSONObject(gson.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));
+ JSONObject result = new JSONObject(gson.toJson(data));
result.put("SSID", trimQuotationMarks(data.getSSID()));
for (SupplicantState state : SupplicantState.values()) {
if (data.getSupplicantState().equals(state)) {
@@ -187,6 +184,20 @@ public class JsonSerializer {
Bundle result = new Bundle();
result.putString("DeviceName", record.getDeviceName());
result.putInt("TxPowerLevel", record.getTxPowerLevel());
+ result.putBundle(
+ "manufacturerSpecificData", serializeBleScanManufacturerSpecificData(record));
+ return result;
+ }
+
+ /** Serialize manufacturer specific data from ScanRecord for Bluetooth LE. */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private Bundle serializeBleScanManufacturerSpecificData(ScanRecord record) {
+ Bundle result = new Bundle();
+ SparseArray<byte[]> sparseArray = record.getManufacturerSpecificData();
+ for (int i = 0; i < sparseArray.size(); i++) {
+ int key = sparseArray.keyAt(i);
+ result.putByteArray(String.valueOf(key), sparseArray.get(key));
+ }
return result;
}
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/utils/RpcEnum.java b/src/main/java/com/google/android/mobly/snippet/bundled/utils/RpcEnum.java
index d3d95ae..d6442a8 100644
--- a/src/main/java/com/google/android/mobly/snippet/bundled/utils/RpcEnum.java
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/utils/RpcEnum.java
@@ -29,8 +29,8 @@ import com.google.common.collect.ImmutableBiMap;
public class RpcEnum {
private final ImmutableBiMap<String, Integer> mEnums;
- private RpcEnum(ImmutableBiMap.Builder<String, Integer> builder, int minSdk) {
- mEnums = builder.build();
+ private RpcEnum(ImmutableBiMap.Builder<String, Integer> builder) {
+ mEnums = builder.buildOrThrow();
}
/**
@@ -64,7 +64,6 @@ public class RpcEnum {
/** Builder for RpcEnum. */
public static class Builder {
private final ImmutableBiMap.Builder<String, Integer> builder;
- public int minSdk = 0;
public Builder() {
builder = new ImmutableBiMap.Builder<>();
@@ -83,7 +82,7 @@ public class RpcEnum {
}
public RpcEnum build() {
- return new RpcEnum(builder, minSdk);
+ return new RpcEnum(builder);
}
}
}