88
99import android .content .Context ;
1010import android .content .Intent ;
11+ import android .os .Build ;
1112
1213import androidx .test .ext .junit .runners .AndroidJUnit4 ;
1314import androidx .test .platform .app .InstrumentationRegistry ;
3132//drift; this keeps them in one file and still exercises the real scan UI, which is part of what
3233//the suite protects.
3334//
34- //So this test ends where the host takes over: the experiment the device offers is loaded, not
35- //started, and serving the remote API . It asserts nothing about the data.
35+ //So this test ends where the host takes over: the experiment the device offers is loaded and
36+ //NOT started . It asserts nothing about the data.
3637//
37- //The device name comes from the driver, which flashes the library examples UNMODIFIED and knows
38- //what they advertise as:
38+ //The class name is the driver's, not a choice - tools/lab/ble.py runs exactly
3939//
40- // adb shell am instrument -e class de.rwth_aachen.phyphox.BleCompatTest \
41- // -e bleDevice phyphox-arduino ...
40+ // am instrument -e class de.rwth_aachen.phyphox.BleCompatConnectTest \
41+ // -e bleDevice <name> ...
4242//
43- //Without that parameter there is no board to talk to and that test skips itself, which is what
44- //happens in CI - the row needs hardware and runs in the lab. The seam check below needs none and
45- //runs everywhere, because a suite that cannot reach the phone is worth catching before the board
46- //is even involved.
43+ //and the driver flashes the library examples UNMODIFIED, so the name is whatever they advertise
44+ //as. Without that parameter there is no board to talk to and this skips itself, which is what
45+ //happens in CI - the row needs hardware and runs in the lab.
46+ //
47+ //Nothing here touches debug.phyphox.remote. The driver owns it (AndroidDevice.prepare sets it,
48+ //cleanup clears it) and goes on to talk to the phone after this test returns, so clearing it
49+ //here would pull the API out from under the host's assertions.
4750@ RunWith (AndroidJUnit4 .class )
48- public class BleCompatTest {
51+ public class BleCompatConnectTest {
4952
5053 private static final String PACKAGE = "de.rwth_aachen.phyphox" ;
51- private static final int PORT = 8080 ;
5254
5355 private UiDevice device () {
5456 return UiDevice .getInstance (getInstrumentation ());
@@ -61,56 +63,23 @@ private UiObject2 waitForId(String id, long timeout) {
6163 @ Before
6264 public void quietFirstRunDialogs () {
6365 FixtureExperiment .suppressHints ();
66+ //The scan asks for these at runtime and autoConfirm deliberately does not answer system
67+ //permission dialogs, so an ungranted phone stops on one with no board in sight. The
68+ //driver grants the sensor permissions it knows about but not these, and granting them
69+ //here keeps the test standalone either way.
70+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S )
71+ grant ("android.permission.BLUETOOTH_CONNECT" , "android.permission.BLUETOOTH_SCAN" );
72+ grant ("android.permission.ACCESS_FINE_LOCATION" , "android.permission.ACCESS_COARSE_LOCATION" );
6473 }
6574
66- //The precondition the whole suite rests on, and the one half of it that needs no board: an
67- //experiment with a Bluetooth block serves the remote API when the switch is set, so the host
68- //can reach it. The switch is applied where every experiment finishes loading
69- //(Experiment.onExperimentLoaded), which is the same place a transferred one arrives at, so
70- //this covers the delivered case too as far as it can be covered without hardware.
71- @ Test
72- public void aBluetoothExperimentServesTheRemoteApiWhenTheSwitchIsSet () throws Exception {
73- shell ("setprop debug.phyphox.remote 1" );
74- shell ("setprop debug.phyphox.remotePort " + PORT );
75- try {
76- FixtureExperiment .launchAssetWithoutWaiting ("bluetooth/Heart Rate.phyphox" );
77- //It stops at "please pick a device" without one, which is exactly the state the host
78- //finds a transferred experiment in before it starts it - and the API has to answer
79- //there, not only once something is connected.
80- long deadline = System .currentTimeMillis () + 30000 ;
81- boolean answered = false ;
82- while (!answered && System .currentTimeMillis () < deadline ) {
83- answered = remoteApiAnswers ();
84- if (!answered )
85- Thread .sleep (500 );
86- }
87- assertTrue ("the remote API did not come up for a Bluetooth experiment although "
88- + "debug.phyphox.remote is set - the host cannot reach a device-delivered "
89- + "experiment either" , answered );
90- } finally {
91- shell ("setprop debug.phyphox.remote '\" \" '" );
92- shell ("setprop debug.phyphox.remotePort '\" \" '" );
93- FixtureExperiment .close (FixtureExperiment .activity ());
94- }
95- }
96-
97- private void shell (String command ) throws Exception {
98- device ().executeShellCommand (command );
99- }
100-
101- private boolean remoteApiAnswers () {
102- try {
103- java .net .HttpURLConnection connection = (java .net .HttpURLConnection )
104- new java .net .URL ("http://127.0.0.1:" + PORT + "/config" ).openConnection ();
105- connection .setConnectTimeout (2000 );
106- connection .setReadTimeout (2000 );
107- try (java .io .InputStream in = connection .getInputStream ()) {
108- return in .read () > 0 ;
109- } finally {
110- connection .disconnect ();
75+ private void grant (String ... permissions ) {
76+ for (String permission : permissions ) {
77+ try {
78+ getInstrumentation ().getUiAutomation ().grantRuntimePermission (PACKAGE , permission );
79+ } catch (Exception e ) {
80+ //Not declared on this API level, or already granted - either way not this test's
81+ //problem; the scan below fails with a message if it actually mattered.
11182 }
112- } catch (Exception e ) {
113- return false ;
11483 }
11584 }
11685
0 commit comments