Sl4a as a library support
Make Sl4A a library support managed by test writters.
Test: unit tests, run google/example/sl4a-BT-discovery
Bug: 31921775
Change-Id: Ib0a869dd4c8eff094e1791ccc72b53d67e07f69b
diff --git a/prod-tests/src/com/android/tradefed/Sl4aBluetoothDiscovery.java b/prod-tests/src/com/android/tradefed/Sl4aBluetoothDiscovery.java
index c45d1f7..86a79d4 100644
--- a/prod-tests/src/com/android/tradefed/Sl4aBluetoothDiscovery.java
+++ b/prod-tests/src/com/android/tradefed/Sl4aBluetoothDiscovery.java
@@ -90,8 +90,10 @@
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
- Sl4aClient clientDUT = mDut.startSL4A();
- Sl4aClient clientDiscoverer = mDiscoverer.startSL4A();
+ // We provide null path for the apk to assume it's already installed.
+ Sl4aClient dutClient = Sl4aClient.startSL4A(mDut, null);
+ Sl4aClient discovererClient = Sl4aClient.startSL4A(mDiscoverer, null);
+
TestIdentifier testId = new TestIdentifier(this.getClass().getCanonicalName(),
"bluetooth_discovery");
@@ -100,17 +102,17 @@
listener.testStarted(testId);
try {
- setup(clientDUT, clientDiscoverer);
- clientDUT.rpcCall("bluetoothMakeDiscoverable");
- Object rep = clientDUT.rpcCall("bluetoothGetScanMode");
+ setup(dutClient, discovererClient);
+ dutClient.rpcCall("bluetoothMakeDiscoverable");
+ Object rep = dutClient.rpcCall("bluetoothGetScanMode");
// 3 signifies CONNECTABLE and DISCOVERABLE
Assert.assertEquals(3, rep);
- clientDiscoverer.getEventDispatcher().clearAllEvents();
- clientDiscoverer.rpcCall("bluetoothStartDiscovery");
- clientDiscoverer.getEventDispatcher()
+ discovererClient.getEventDispatcher().clearAllEvents();
+ discovererClient.rpcCall("bluetoothStartDiscovery");
+ discovererClient.getEventDispatcher()
.popEvent("BluetoothDiscoveryFinished", 60000);
- Object listDiscovered = clientDiscoverer.rpcCall("bluetoothGetDiscoveredDevices");
+ Object listDiscovered = discovererClient.rpcCall("bluetoothGetDiscoveredDevices");
JSONArray response = (JSONArray) listDiscovered;
boolean found = false;
for (int i = 0; i < response.length(); i++) {
@@ -129,6 +131,8 @@
} finally {
listener.testEnded(testId, Collections.emptyMap());
listener.testRunEnded(System.currentTimeMillis() - startTime, Collections.emptyMap());
+ dutClient.close();
+ discovererClient.close();
}
}
}
diff --git a/src/com/android/tradefed/device/ITestDevice.java b/src/com/android/tradefed/device/ITestDevice.java
index fdfb90c..4007fc6 100644
--- a/src/com/android/tradefed/device/ITestDevice.java
+++ b/src/com/android/tradefed/device/ITestDevice.java
@@ -18,7 +18,6 @@
import com.android.ddmlib.IDevice;
import com.android.tradefed.result.InputStreamSource;
-import com.android.tradefed.util.sl4a.Sl4aClient;
import java.io.File;
import java.util.ArrayList;
@@ -559,12 +558,4 @@
* @throws DeviceNotAvailableException
*/
public Map<Integer, String> getAndroidIds() throws DeviceNotAvailableException;
-
- /**
- * Create and return the sl4a client associated with the ITestDevice.
- * Use option "--sl4a-apk-path <path>" to specify a particular apk to use.
- *
- * @return a {@link Sl4aClient} that is successfully connected and running on the device.
- */
- public Sl4aClient startSL4A() throws DeviceNotAvailableException;
}
diff --git a/src/com/android/tradefed/device/NativeDevice.java b/src/com/android/tradefed/device/NativeDevice.java
index 8f82fb8..eb3cd40 100644
--- a/src/com/android/tradefed/device/NativeDevice.java
+++ b/src/com/android/tradefed/device/NativeDevice.java
@@ -53,7 +53,6 @@
import com.android.tradefed.util.SizeLimitedOutputStream;
import com.android.tradefed.util.StreamUtil;
import com.android.tradefed.util.ZipUtil2;
-import com.android.tradefed.util.sl4a.Sl4aClient;
import org.apache.commons.compress.archivers.zip.ZipFile;
@@ -3679,12 +3678,4 @@
CLog.d("No valid MAC address queried from device %s", mIDevice.getSerialNumber());
return null;
}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Sl4aClient startSL4A() throws DeviceNotAvailableException {
- throw new UnsupportedOperationException("Sl4a is not supported by native device.");
- }
}
diff --git a/src/com/android/tradefed/device/TestDevice.java b/src/com/android/tradefed/device/TestDevice.java
index e82740f..a0970db 100644
--- a/src/com/android/tradefed/device/TestDevice.java
+++ b/src/com/android/tradefed/device/TestDevice.java
@@ -27,7 +27,6 @@
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.util.RunUtil;
import com.android.tradefed.util.StreamUtil;
-import com.android.tradefed.util.sl4a.Sl4aClient;
import com.google.common.annotations.VisibleForTesting;
@@ -36,7 +35,6 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
-import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -78,8 +76,6 @@
private static final int API_LEVEL_GET_CURRENT_USER = 24;
- private Sl4aClient mSl4aClient = null;
-
/**
* @param device
* @param stateMonitor
@@ -1080,49 +1076,4 @@
+ "Must be API %d.", feature, getSerialNumber(), strictMinLevel));
}
}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void postInvocationTearDown() {
- super.postInvocationTearDown();
- if (mSl4aClient != null) {
- mSl4aClient.close();
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Sl4aClient startSL4A() throws DeviceNotAvailableException {
- File sl4aApkFile = getOptions().getSl4aApkFile();
- if (sl4aApkFile != null) {
- if (!sl4aApkFile.exists()) {
- throw new RuntimeException(String.format("Sl4A apk '%s' was not found.",
- sl4aApkFile.getAbsoluteFile()));
- }
- String res = installPackage(sl4aApkFile, true);
- if (res != null) {
- throw new RuntimeException(String.format("Error when installing the Sl4A apk: %s",
- res));
- }
- }
- ServerSocket s = null;
- int port = -1;
- try {
- s = new ServerSocket(0);
- s.setReuseAddress(true);
- port = s.getLocalPort();
- s.close();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- // even after being closed, socket may remain in TIME_WAIT state
- // reuse address allows to connect to it even in this state.
- mSl4aClient = new Sl4aClient(this, port, 9998);
- mSl4aClient.startSl4A();
- return mSl4aClient;
- }
}
diff --git a/src/com/android/tradefed/device/TestDeviceOptions.java b/src/com/android/tradefed/device/TestDeviceOptions.java
index d6d81a6..5916cee 100644
--- a/src/com/android/tradefed/device/TestDeviceOptions.java
+++ b/src/com/android/tradefed/device/TestDeviceOptions.java
@@ -17,7 +17,6 @@
import com.android.tradefed.config.Option;
-import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -108,10 +107,6 @@
"the minimum battery level required to continue the invocation. Scale: 0-100")
private Integer mCutoffBattery = null;
- @Option(name = "sl4a-apk-path", description = "path to the sl4a.apk file to be used by the"
- + "device, if null it will assume the apk is already installed on the device.")
- private File mSl4aApkFile = null;
-
/**
* Check whether adb root should be enabled on boot for this device
*/
@@ -348,11 +343,4 @@
public boolean isWifiExpoRetryEnabled() {
return mWifiExpoRetryEnabled;
}
-
- /**
- * @return a {@link File} pointing to the sl4a.apk file. Can be null, if apk already installed.
- */
- public File getSl4aApkFile() {
- return mSl4aApkFile;
- }
}
diff --git a/src/com/android/tradefed/util/sl4a/Sl4aClient.java b/src/com/android/tradefed/util/sl4a/Sl4aClient.java
index 000e9ca..8d6aa11 100644
--- a/src/com/android/tradefed/util/sl4a/Sl4aClient.java
+++ b/src/com/android/tradefed/util/sl4a/Sl4aClient.java
@@ -26,9 +26,11 @@
import org.json.JSONObject;
import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
+import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
@@ -36,16 +38,16 @@
/**
* Sl4A client to interact via RPC with SL4A scripting layer.
*/
-public class Sl4aClient {
+public class Sl4aClient implements AutoCloseable {
- public static final String INIT = "initiate";
- public static final String CONTINUE = "continue";
+ private static final String INIT = "initiate";
public static final String IS_SL4A_RUNNING_CMD =
"ps -e | grep \"S com.googlecode.android_scripting\"";
public static final String SL4A_LAUNCH_CMD =
"am start -a com.googlecode.android_scripting.action.LAUNCH_SERVER " +
"--ei com.googlecode.android_scripting.extra.USE_SERVICE_PORT %s " +
"com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher";
+ public static final String STOP_SL4A_CMD = "am force-stop com.googlecode.android_scripting";
private static final int UNKNOWN_ID = -1;
@@ -72,6 +74,44 @@
}
/**
+ * Convenience method to create and start a client ready to use.
+ *
+ * @param device the {ITestDevice} that the client will be for.
+ * @param sl4aApkFile file path to hte sl4a apk to install, or null if already installed.
+ * @return an {@link Sl4aClient} instance that has been started.
+ * @throws DeviceNotAvailableException
+ */
+ public static Sl4aClient startSL4A(ITestDevice device, File sl4aApkFile)
+ throws DeviceNotAvailableException {
+ if (sl4aApkFile != null) {
+ if (!sl4aApkFile.exists()) {
+ throw new RuntimeException(String.format("Sl4A apk '%s' was not found.",
+ sl4aApkFile.getAbsoluteFile()));
+ }
+ String res = device.installPackage(sl4aApkFile, true);
+ if (res != null) {
+ throw new RuntimeException(String.format("Error when installing the Sl4A apk: %s",
+ res));
+ }
+ }
+ ServerSocket s = null;
+ int port = -1;
+ try {
+ s = new ServerSocket(0);
+ s.setReuseAddress(true);
+ port = s.getLocalPort();
+ s.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ // even after being closed, socket may remain in TIME_WAIT state
+ // reuse address allows to connect to it even in this state.
+ Sl4aClient sl4aClient = new Sl4aClient(device, port, 9998);
+ sl4aClient.startSl4A();
+ return sl4aClient;
+ }
+
+ /**
* Return the default runutil instance. Exposed for testing.
*/
protected IRunUtil getRunUtil() {
@@ -189,8 +229,10 @@
}
/**
- * Close the sl4a connection to device side.
+ * Close the sl4a connection to device side and Kills any running instance of sl4a.
+ * If no instance is running then nothing is done.
*/
+ @Override
public void close() {
try {
if (mEventDispatcher != null) {
@@ -199,6 +241,7 @@
if (mSocket != null) {
mSocket.close();
}
+ mDevice.executeShellCommand(STOP_SL4A_CMD);
mDevice.executeAdbCommand("forward", "--remove", "tcp:" + mHostPort);
} catch (IOException | DeviceNotAvailableException e) {
CLog.e(e);
diff --git a/src/com/android/tradefed/util/sl4a/Sl4aEventDispatcher.java b/src/com/android/tradefed/util/sl4a/Sl4aEventDispatcher.java
index 8814f63..3e3358f 100644
--- a/src/com/android/tradefed/util/sl4a/Sl4aEventDispatcher.java
+++ b/src/com/android/tradefed/util/sl4a/Sl4aEventDispatcher.java
@@ -68,7 +68,6 @@
try {
Object response = mClient.rpcCall("eventWait", mTimeout);
if (response == null) {
- CLog.e("response is null");
return true;
}
EventSl4aObject event = new EventSl4aObject(new JSONObject(response.toString()));
diff --git a/tests/src/com/android/tradefed/util/sl4a/Sl4aClientTest.java b/tests/src/com/android/tradefed/util/sl4a/Sl4aClientTest.java
index d87b26b..a9b63a4 100644
--- a/tests/src/com/android/tradefed/util/sl4a/Sl4aClientTest.java
+++ b/tests/src/com/android/tradefed/util/sl4a/Sl4aClientTest.java
@@ -15,6 +15,8 @@
*/
package com.android.tradefed.util.sl4a;
+import static org.junit.Assert.*;
+
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.util.IRunUtil;
@@ -26,6 +28,7 @@
import org.junit.Before;
import org.junit.Test;
+import java.io.File;
import java.io.IOException;
/**
@@ -121,6 +124,7 @@
+ "com.googlecode.android_scripting");
mMockRunUtil.sleep(EasyMock.anyLong());
EasyMock.expectLastCall();
+ EasyMock.expect(mMockDevice.executeShellCommand(Sl4aClient.STOP_SL4A_CMD)).andReturn("");
EasyMock.expect(mMockDevice.executeAdbCommand("forward", "tcp:" + mDeviceServer.getPort(),
"tcp:" + mDeviceServer.getPort())).andReturn("");
EasyMock.expect(mMockDevice.executeAdbCommand("forward", "--list")).andReturn("");
@@ -162,4 +166,22 @@
}
EasyMock.verify(mMockDevice, mMockRunUtil);
}
+
+ /**
+ * Test for {@link Sl4aClient#startSL4A(ITestDevice, File)} throws an exception if sl4a apk
+ * provided does not exist.
+ */
+ @Test
+ public void testCreateSl4aClient() throws Exception {
+ final String fakePath = "/fake/random/path";
+ EasyMock.replay(mMockDevice);
+ try {
+ Sl4aClient.startSL4A(mMockDevice, new File(fakePath));
+ fail("Should have thrown an exception");
+ } catch (RuntimeException expected) {
+ assertEquals(String.format("Sl4A apk '%s' was not found.", fakePath),
+ expected.getMessage());
+ }
+ EasyMock.verify(mMockDevice);
+ }
}