Home | History | Annotate | Download | only in helpers
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.sensors.helpers;
     18 
     19 import com.android.cts.verifier.sensors.reporting.SensorTestDetails;
     20 
     21 import android.content.Context;
     22 import android.hardware.Sensor;
     23 import android.hardware.SensorEvent;
     24 import android.hardware.SensorEventListener;
     25 import android.hardware.SensorManager;
     26 import android.hardware.cts.helpers.SensorCtsHelper;
     27 import android.net.LocalServerSocket;
     28 import android.net.LocalSocket;
     29 import android.util.Log;
     30 
     31 import java.io.IOException;
     32 import java.io.InputStream;
     33 import java.io.OutputStream;
     34 import java.util.List;
     35 import java.util.StringTokenizer;
     36 
     37 /**
     38  * This class handles communication with the host to respond to commands.
     39  * The command/response link is through a TCP socket on the host side, forwarded via adb to a local
     40  * socket on the device. The system uses a standard "accept-read_command-send_response-close" to
     41  * execute commands sent from the host.
     42  *
     43  * CAUTION: The local socket name (SOCKET_NAME below) must match that used by the host to set up
     44  * the adb-forwarding.
     45  */
     46 public class PowerTestHostLink {
     47     private static final String TAG = "PowerTestHostLink";
     48 
     49     /**
     50      * Host-to-device bridge will use a Listener instance to drive the test via the CtsVerifier
     51      * running on the device.
     52      */
     53     public interface HostToDeviceInterface {
     54         void logTestResult(SensorTestDetails testDetails);
     55         void raiseError(String testName, String message) throws Exception;
     56         void waitForUserAcknowledgement(String message) throws InterruptedException;
     57         void logText(String text);
     58         void turnScreenOff();
     59     }
     60 
     61     /** This is a data-only message to communicate result of a power test */
     62     public class PowerTestResult{
     63         public int passedCount = 0;
     64         public int skippedCount = 0;
     65         public int failedCount = 0;
     66     }
     67 
     68     /**
     69      * Standard response types back to host. Host-side code must match these definitions.
     70      */
     71     private final static String RESPONSE_OK = "OK";
     72     private final static String RESPONSE_ERR = "ERR";
     73     private final static String RESPONSE_UNAVAILABLE = "UNAVAILABLE";
     74 
     75     /**
     76      * Socket name for host adb forwarded communications. Must match naem in host-side code.
     77      */
     78     public final static String SOCKET_NAME = "/android/cts/powertest";
     79 
     80     private volatile boolean mStopThread;
     81     private final SensorManager mSensorManager;
     82     private final HostToDeviceInterface mHostToDeviceExecutor;
     83     private final PowerTestResult mTestResult = new PowerTestResult();
     84 
     85     public PowerTestHostLink(Context context, HostToDeviceInterface listener) {
     86         Log.d(TAG, " +++ Begin of localSocketServer() +++ ");
     87         mHostToDeviceExecutor = listener;
     88         mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
     89     }
     90 
     91     /**
     92      * Ensure connection to host is closed; stop accepting requests.
     93      **/
     94     public void close() {
     95         mStopThread = true;
     96     }
     97 
     98     /**
     99      * Run the suite of tests via the host, responding to host requests.
    100      *
    101      * @return number of failed test cases
    102      * @throws Exception
    103      */
    104     public PowerTestResult run() throws Exception {
    105         // define buffer to receive data from host
    106         final int BUFFER_SIZE = 4096;
    107         byte[] buffer = new byte[BUFFER_SIZE];
    108 
    109         LocalServerSocket serverSocket = createSocket();
    110         if (null == serverSocket) {
    111             mStopThread = true;
    112         }
    113 
    114         InputStream streamIn;
    115         OutputStream streamOut;
    116         LocalSocket receiverSocket;
    117         while (!mStopThread) {
    118 
    119             try {
    120                 Log.d(TAG, "localSocketServer accept...");
    121                 receiverSocket = serverSocket.accept();
    122                 Log.d(TAG, "Got new connection");
    123             } catch (IOException e) {
    124                 Log.d(TAG, "localSocketServer accept() failed !!!", e);
    125                 continue;
    126             }
    127 
    128             try {
    129                 streamIn = receiverSocket.getInputStream();
    130             } catch (IOException e) {
    131                 Log.d(TAG, "getInputStream() failed !!!", e);
    132                 continue;
    133             }
    134 
    135             try {
    136                 streamOut = receiverSocket.getOutputStream();
    137             } catch (IOException e) {
    138                 Log.e(TAG, "getOutputStream() failed", e);
    139                 continue;
    140             }
    141 
    142             Log.d(TAG, "The client connected to LocalServerSocket");
    143 
    144             try {
    145                 int total = 0;
    146                 // command and response handshake, so read all data
    147                 while (streamIn.available() > 0 || total == 0) {
    148                     if (total < BUFFER_SIZE) {
    149                         int bytesRead = streamIn.read(buffer, total,
    150                                 (BUFFER_SIZE - total));
    151                         if (bytesRead > 0) {
    152                             total += bytesRead;
    153                         }
    154                     } else {
    155                         Log.e(TAG, "Message too long: truncating");
    156                     }
    157                 }
    158                 String clientRequest = new String(buffer);
    159                 clientRequest = clientRequest.substring(0, total);
    160                 if (clientRequest.length() > 0) {
    161 
    162                     Log.d(TAG, "Client requested: " + clientRequest);
    163                     try {
    164                         String response = processClientRequest(clientRequest);
    165                         if (response != null) {
    166                             Log.d(TAG, "Sending response " + response);
    167                             streamOut.write(response.getBytes(), 0, response.length());
    168                         }
    169                         // null response means response is deferred awaiting user response
    170                     } catch (Exception e) {
    171                         Log.e(TAG, "Error executing " + clientRequest, e);
    172                         streamOut.write(RESPONSE_ERR.getBytes(), 0, RESPONSE_ERR.length());
    173                     }
    174                 }
    175                 receiverSocket.close();
    176             } catch (IOException e) {
    177                 Log.e(TAG, "There is an exception when reading from or writing to socket", e);
    178                 break;
    179             }
    180         }
    181         Log.d(TAG, "The LocalSocketServer thread is going to stop !!!");
    182 
    183         if (serverSocket != null) {
    184             try {
    185                 serverSocket.close();
    186             } catch (IOException e) {
    187                 Log.d(TAG, "Exception on close of server socket", e);
    188             }
    189         }
    190         mHostToDeviceExecutor.logText("Device disconnected.");
    191         Log.d(TAG, "Returning " + mTestResult.passedCount + "passed " + mTestResult.skippedCount +
    192                 "skipped " + mTestResult.failedCount + "failed.");
    193         return mTestResult;
    194     }
    195 
    196     private  String processClientRequest(String request) throws Exception {
    197         // the following constants need to match the definitions in execute_power_tests.py
    198         final String REQUEST_EXTERNAL_STORAGE = "EXTERNAL STORAGE?";
    199         final String REQUEST_EXIT = "EXIT";
    200         final String REQUEST_RAISE = "RAISE ";
    201         final String REQUEST_USER_RESPONSE = "USER RESPONSE ";
    202         final String REQUEST_SET_TEST_RESULT = "SET TEST RESULT ";
    203         final String REQUEST_SENSOR_ON = "SENSOR ON ";
    204         final String REQUEST_SENSOR_OFF = "SENSOR OFF";
    205         final String REQUEST_SENSOR_AVAILABILITY = "SENSOR? ";
    206         final String REQUEST_SCREEN_OFF = "SCREEN OFF";
    207         final String REQUEST_SHOW_MESSAGE = "MESSAGE ";
    208 
    209         String response = RESPONSE_ERR;
    210         // Queries must appear first and then commands to direct actions after in these statements
    211         if (request.startsWith(REQUEST_SENSOR_AVAILABILITY)) {
    212             final String sensor = request.substring(REQUEST_SENSOR_AVAILABILITY.length());
    213             final int sensorId = getSensorId(sensor);
    214             if (mSensorManager.getDefaultSensor(sensorId) == null) {
    215                 response = RESPONSE_UNAVAILABLE;
    216             } else {
    217                 response = RESPONSE_OK;
    218             }
    219         } else if (request.startsWith(REQUEST_EXTERNAL_STORAGE)){
    220             response = SensorCtsHelper.getSensorTestDataDirectory("power/").getAbsolutePath();
    221             Log.d(TAG,"External storage is " + response);
    222         } else if (request.startsWith(REQUEST_SCREEN_OFF)) {
    223             try {
    224                 mHostToDeviceExecutor.turnScreenOff();
    225                 response = RESPONSE_OK;
    226             } catch (SecurityException e) {
    227                 Log.e(TAG, "Error Turning screen off", e);
    228                 response = RESPONSE_ERR;
    229             }
    230         } else if (request.startsWith(REQUEST_SENSOR_ON)) {
    231             String sensorList = request.substring(REQUEST_SENSOR_ON.length()).trim();
    232             response = handleSensorSwitchCommand(sensorList, true);
    233         } else if (request.startsWith(REQUEST_SENSOR_OFF)) {
    234             String sensorList = request.substring(REQUEST_SENSOR_ON.length()).trim();
    235             response = handleSensorSwitchCommand(sensorList, false);
    236         } else if (request.startsWith(REQUEST_SHOW_MESSAGE)) {
    237             final String message = request.substring(REQUEST_SHOW_MESSAGE.length());
    238             mHostToDeviceExecutor.logText(message);
    239             response = RESPONSE_OK;
    240         } else if (request.startsWith(REQUEST_USER_RESPONSE)) {
    241             String message = request.substring(REQUEST_USER_RESPONSE.length());
    242             mHostToDeviceExecutor.waitForUserAcknowledgement(message);
    243             response = RESPONSE_OK;
    244         } else if (request.startsWith(REQUEST_SET_TEST_RESULT)) {
    245             String testResult = request.substring(REQUEST_SET_TEST_RESULT.length());
    246             response = handleSetTestResultCmd(testResult);
    247         } else if (request.startsWith(REQUEST_RAISE)) {
    248             String command = request.substring(REQUEST_RAISE.length());
    249             StringTokenizer tokenizer = new StringTokenizer(command);
    250             try {
    251                 final String testName = tokenizer.nextToken();
    252                 final String message = request.substring(7 + testName.length());
    253                 mHostToDeviceExecutor.raiseError(testName, message);
    254                 response = RESPONSE_OK;
    255             } catch (Exception e) {
    256                 Log.e(TAG, "Invalid RAISE command received (bad arguments): " + request);
    257                 response = RESPONSE_ERR;
    258             }
    259         } else if (request.startsWith(REQUEST_EXIT)) {
    260             mStopThread = true;
    261             response = RESPONSE_OK;
    262         } else {
    263             Log.e(TAG, "Unknown request: " + request);
    264         }
    265         return response;
    266     }
    267 
    268     private String handleSetTestResultCmd(final String request) {
    269         String response;
    270         StringTokenizer tokenizer = new StringTokenizer(request, " ");
    271         String testName = "";
    272         SensorTestDetails.ResultCode resultCode = SensorTestDetails.ResultCode.FAIL;
    273         String message = "";
    274 
    275         try {
    276             testName = tokenizer.nextToken();
    277             final String resultToken = tokenizer.nextToken();
    278 
    279             if (resultToken.equals("PASS")) {
    280                 resultCode = SensorTestDetails.ResultCode.PASS;
    281                 ++mTestResult.passedCount;
    282                 message = "PASSED: ";
    283                 response = RESPONSE_OK;
    284             } else if (resultToken.equals("FAIL")) {
    285                 resultCode = SensorTestDetails.ResultCode.FAIL;
    286                 ++mTestResult.failedCount;
    287                 message = "FAILED: ";
    288                 response = RESPONSE_OK;
    289             } else if (resultToken.equals("SKIPPED")) {
    290                 resultCode = SensorTestDetails.ResultCode.SKIPPED;
    291                 ++mTestResult.skippedCount;
    292                 message = "SKIPPED: ";
    293                 response = RESPONSE_OK;
    294             } else {
    295                 response = RESPONSE_ERR;
    296             }
    297             while (tokenizer.hasMoreTokens()) {
    298                 message += tokenizer.nextToken() + " ";
    299             }
    300             Log.i(TAG, message);
    301         } catch (Exception e) {
    302             Log.e(TAG, "Improperly formatted command received: " + request, e);
    303             response = RESPONSE_ERR;
    304         }
    305         String fullMessage = testName + " " + message;
    306         mHostToDeviceExecutor.logTestResult(
    307                 new SensorTestDetails(testName, resultCode, fullMessage));
    308         return response;
    309     }
    310 
    311     private String handleSensorSwitchCommand(String sensorList, boolean switchOn) {
    312         String response;
    313         try {
    314             StringTokenizer tokenizer = new StringTokenizer(sensorList, " ");
    315             int n = tokenizer.countTokens();
    316             if (n == 0) {
    317                 response = switchAllSensors(switchOn);
    318             } else {
    319                 String sensorName = tokenizer.nextToken();
    320                 String requestRate = "";
    321                 if (n > 1) {
    322                     requestRate = tokenizer.nextToken();
    323                 }
    324                 if (sensorName.equals("ALL")) {
    325                     response = switchAllSensors(switchOn);
    326                 } else {
    327                     int sensorId = getSensorId(sensorName);
    328                     if (sensorId >= 0) {
    329                         response = switchSensor(sensorId, switchOn, requestRate);
    330                     } else {
    331                         Log.e(TAG, "Unknown sensor in request: " + sensorName);
    332                         response = RESPONSE_UNAVAILABLE;
    333                     }
    334                 }
    335             }
    336         } catch (Exception e) {
    337             Log.e(TAG, "Improperly formatted command received on setting sensor state");
    338             response = RESPONSE_ERR;
    339         }
    340         return response;
    341     }
    342 
    343     private static int getSensorId(String sensorName) {
    344         int sensorId = -1;
    345 
    346         if (sensorName.compareToIgnoreCase("ACCELEROMETER") == 0) {
    347             sensorId = Sensor.TYPE_ACCELEROMETER;
    348         } else if (sensorName.compareToIgnoreCase("AMBIENT_TEMPERATURE") == 0) {
    349             sensorId = Sensor.TYPE_AMBIENT_TEMPERATURE;
    350         } else if (sensorName.compareToIgnoreCase("GAME_ROTATION_VECTOR") == 0) {
    351             sensorId = Sensor.TYPE_GAME_ROTATION_VECTOR;
    352         } else if (sensorName.compareToIgnoreCase("GEOMAGNETIC_ROTATION_VECTOR") == 0) {
    353             sensorId = Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR;
    354         } else if (sensorName.compareToIgnoreCase("GRAVITY") == 0) {
    355             sensorId = Sensor.TYPE_GRAVITY;
    356         } else if (sensorName.compareToIgnoreCase("GYROSCOPE") == 0) {
    357             sensorId = Sensor.TYPE_GYROSCOPE;
    358         } else if (sensorName.compareToIgnoreCase("LIGHT") == 0) {
    359             sensorId = Sensor.TYPE_LIGHT;
    360         } else if (sensorName.compareToIgnoreCase("MAGNETIC_FIELD") == 0) {
    361             sensorId = Sensor.TYPE_MAGNETIC_FIELD;
    362         } else if (sensorName.compareToIgnoreCase("PRESSURE") == 0) {
    363             sensorId = Sensor.TYPE_PRESSURE;
    364         } else if (sensorName.compareToIgnoreCase("PROXIMITY") == 0) {
    365             sensorId = Sensor.TYPE_PROXIMITY;
    366         } else if (sensorName.compareToIgnoreCase("RELATIVE_HUMIDITY") == 0) {
    367             sensorId = Sensor.TYPE_RELATIVE_HUMIDITY;
    368         } else if (sensorName.compareToIgnoreCase("ROTATION_VECTOR") == 0) {
    369             sensorId = Sensor.TYPE_ROTATION_VECTOR;
    370         } else if (sensorName.compareToIgnoreCase("SIGNIFICANT_MOTION") == 0) {
    371             sensorId = Sensor.TYPE_SIGNIFICANT_MOTION;
    372         } else if (sensorName.compareToIgnoreCase("STEP_COUNTER") == 0) {
    373             sensorId = Sensor.TYPE_STEP_COUNTER;
    374         } else if (sensorName.compareToIgnoreCase("STEP_DETECTOR") == 0) {
    375             sensorId = Sensor.TYPE_STEP_DETECTOR;
    376         }
    377 
    378         return sensorId;
    379     }
    380 
    381     private String switchSensor(int sensorId, boolean switchOn) {
    382         return switchSensor(sensorId, switchOn, "SENSOR_DELAY_NORMAL");
    383     }
    384 
    385     private String switchSensor(int sensorId, boolean switchOn, String requestFrequency) {
    386         String response;
    387         int rateUs = SensorManager.SENSOR_DELAY_NORMAL;
    388 
    389         if (requestFrequency.compareToIgnoreCase("SENSOR_DELAY_FASTEST") == 0) {
    390             rateUs = SensorManager.SENSOR_DELAY_FASTEST;
    391         } else if (requestFrequency.compareToIgnoreCase("SENSOR_DELAY_GAME") == 0) {
    392             rateUs = SensorManager.SENSOR_DELAY_GAME;
    393         } else if (requestFrequency.compareToIgnoreCase("SENSOR_DELAY_UI") == 0) {
    394             rateUs = SensorManager.SENSOR_DELAY_UI;
    395         }
    396 
    397         if (switchOn) {
    398             mSensorManager.registerListener(mSensorEventListener,
    399                     mSensorManager.getDefaultSensor(sensorId), rateUs);
    400             response = RESPONSE_OK;
    401             Log.v(TAG, "Switching ON " + String.valueOf(sensorId) + " | " + String.valueOf(rateUs));
    402         } else {
    403             mSensorManager.unregisterListener(mSensorEventListener,
    404                     mSensorManager.getDefaultSensor(sensorId));
    405             response = RESPONSE_OK;
    406             Log.v(TAG, "Switching  OFF " + String.valueOf(sensorId));
    407         }
    408 
    409         return response;
    410     }
    411 
    412     private String switchAllSensors(boolean on) {
    413         List<Sensor> allSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
    414         String response = RESPONSE_OK;
    415         for (Sensor sensor : allSensors) {
    416             if (sensor.getType() >= Sensor.TYPE_DEVICE_PRIVATE_BASE) {
    417                 continue;
    418             }
    419             response = switchSensor(sensor.getType(), on);
    420             if (response == null) {
    421                 response = RESPONSE_ERR;
    422             }
    423         }
    424         return response;
    425     }
    426 
    427     private LocalServerSocket createSocket() {
    428         try {
    429             return new LocalServerSocket(SOCKET_NAME);
    430         } catch (IOException e) {
    431             Log.e(TAG, "LocalSocketServer creation failure.", e);
    432             return null;
    433         }
    434     }
    435 
    436     private SensorEventListener mSensorEventListener = new SensorEventListener() {
    437         @Override
    438         public void onAccuracyChanged(Sensor sensor, int accuracy) {}
    439 
    440         @Override
    441         public void onSensorChanged(SensorEvent event) {}
    442     };
    443 }
    444