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 { logTestResult(SensorTestDetails testDetails)54 void logTestResult(SensorTestDetails testDetails); raiseError(String testName, String message)55 void raiseError(String testName, String message) throws Exception; waitForUserAcknowledgement(String message)56 void waitForUserAcknowledgement(String message) throws InterruptedException; logText(String text)57 void logText(String text); turnScreenOff()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 PowerTestHostLink(Context context, HostToDeviceInterface listener)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 **/ close()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 */ run()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 processClientRequest(String request)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 handleSetTestResultCmd(final String request)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 handleSensorSwitchCommand(String sensorList, boolean switchOn)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 getSensorId(String sensorName)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 switchSensor(int sensorId, boolean switchOn)381 private String switchSensor(int sensorId, boolean switchOn) { 382 return switchSensor(sensorId, switchOn, "SENSOR_DELAY_NORMAL"); 383 } 384 switchSensor(int sensorId, boolean switchOn, String requestFrequency)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 switchAllSensors(boolean on)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 createSocket()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