1 /* 2 * Copyright (C) 2016 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; 18 19 import com.android.cts.verifier.R; 20 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity; 21 22 import android.content.Context; 23 import android.hardware.Sensor; 24 import android.hardware.SensorEvent; 25 import android.hardware.SensorEventCallback; 26 import android.hardware.SensorManager; 27 import android.hardware.cts.helpers.SensorTestStateNotSupportedException; 28 import android.os.Build; 29 import android.util.Log; 30 31 import junit.framework.Assert; 32 33 import java.util.List; 34 import java.util.concurrent.CountDownLatch; 35 import java.util.concurrent.TimeUnit; 36 37 /** 38 * CTS Verifier case for verifying dynamic sensor discovery feature. 39 */ 40 public class DynamicSensorDiscoveryTestActivity extends SensorCtsVerifierTestActivity { 41 42 private final static String TAG = "DynamicSensorDiscoveryTestActivity"; 43 private final static int CONNECTION_TIMEOUT_SEC = 30; 44 private final static int DISCONNECTION_TIMEOUT_SEC = 30; 45 private final static int EVENT_TIMEOUT_SEC = 30; 46 private SensorManager mSensorManager; 47 private boolean mFeatureSupported = false; 48 private boolean mSensorConnected = false; 49 private boolean mSensorDisconnected = false; 50 private Integer mSensorId; 51 private Callback mCallback; 52 53 public DynamicSensorDiscoveryTestActivity() { 54 super(DynamicSensorDiscoveryTestActivity.class); 55 } 56 57 @Override 58 protected void activitySetUp() throws InterruptedException { 59 mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); 60 if (mSensorManager == null || !(Build.VERSION.SDK_INT > Build.VERSION_CODES.M || 61 Build.VERSION.CODENAME.startsWith("N") /* useful when N in dev */) ) { 62 return; 63 } 64 mFeatureSupported = mSensorManager.isDynamicSensorDiscoverySupported(); 65 66 try { 67 featureSupportedOrSkip(); 68 } catch (SensorTestStateNotSupportedException e) { 69 // This device doesn't support dynamic sensors. So we won't 70 // be running any of the tests, and really don't want to 71 // confuse the user by telling them they need to hoook one up. 72 // TODO(b/29606675): This is pretty hack, and should have 73 // a better overall approach. 74 return; 75 } 76 showUserMessage("This test will requires the user to connect an external sensor (" + 77 "physical or simulated) and then disconnect it."); 78 waitForUserToContinue(); 79 80 mCallback = new Callback(); 81 mSensorManager.registerDynamicSensorCallback(mCallback); 82 } 83 84 @SuppressWarnings("unused") 85 public String test0_OnConnect() { 86 featureSupportedOrSkip(); 87 88 showUserMessage(String.format("Please connect an external sensor to device in %d seconds.", 89 CONNECTION_TIMEOUT_SEC)); 90 91 Assert.assertTrue("Cannot detect sensor connection.", mCallback.waitForConnection(null)); 92 mSensorConnected = true; 93 mSensorId = mCallback.getSensorId(); 94 return "OnConnect: Success"; 95 } 96 97 @SuppressWarnings("unused") 98 public String test1_DynamicSensorList() { 99 featureSupportedOrSkip(); 100 sensorConnectedOrSkip(); 101 102 Assert.assertTrue("Dynamic sensor flag is not set correctly for at least one sensor", 103 isDynamicFlagSetCorrectly()); 104 105 Assert.assertTrue("Sensor connected, but is not in dynamic sensor list", 106 mCallback.isSensorInList()); 107 108 Assert.assertTrue("Sensor connected, but is not in dynamic sensor list of its type", 109 mCallback.isSensorInListOfSpecificType()); 110 111 return "DynamicSensorList: Success"; 112 } 113 114 @SuppressWarnings("unused") 115 public String test2_SensorOperation() { 116 featureSupportedOrSkip(); 117 sensorConnectedOrSkip(); 118 119 showUserMessage("Testing sensor operation ... Please make sure sensor generates sensor " + 120 "events if it does not automatically do so."); 121 122 Assert.assertTrue("Failed to receive sensor events", mCallback.waitForSensorEvent()); 123 return "SensorOperation: Success"; 124 } 125 126 @SuppressWarnings("unused") 127 public String test3_OnDisconnect() { 128 featureSupportedOrSkip(); 129 sensorConnectedOrSkip(); 130 131 showUserMessage(String.format("Please disconnect the external sensor that was previously " + 132 "connected in %d seconds", DISCONNECTION_TIMEOUT_SEC)); 133 Assert.assertTrue("Cannot detect sensor disconnection.", mCallback.waitForDisconnection()); 134 mSensorDisconnected = true; 135 return "OnDisconnect: Success"; 136 } 137 138 @SuppressWarnings("unused") 139 public String test4_OnReconnect() { 140 featureSupportedOrSkip(); 141 sensorConnectedOrSkip(); 142 sensorDisconnectedOrSkip(); 143 144 showUserMessage(String.format("Please connect the same sensor that was previously " + 145 "connected in %d seconds", CONNECTION_TIMEOUT_SEC)); 146 Assert.assertTrue("Cannot detect sensor reconnection.", 147 mCallback.waitForConnection(mSensorId)); 148 149 Integer sensorId = mCallback.getSensorId(); 150 boolean match = mSensorId != null && sensorId != null && 151 sensorId.intValue() == mSensorId.intValue(); 152 Assert.assertTrue("Id mismatch for the reconnected sensor", match); 153 return "OnReconnect: Success"; 154 } 155 156 private class Callback extends SensorManager.DynamicSensorCallback { 157 158 private Sensor mSensor = null; 159 private Integer mExpectSensorId = null; 160 private CountDownLatch mConnectLatch; 161 private CountDownLatch mDisconnectLatch; 162 163 @Override 164 public void onDynamicSensorConnected(Sensor sensor) { 165 Log.d(TAG, "Sensor Connected: " + sensor); 166 167 if (mExpectSensorId == null || mExpectSensorId == sensor.getId()) { 168 mSensor = sensor; 169 if (mConnectLatch != null) { 170 mConnectLatch.countDown(); 171 } 172 } 173 } 174 175 @Override 176 public void onDynamicSensorDisconnected(Sensor sensor) { 177 if (mSensor == sensor) { 178 mSensor = null; 179 if (mDisconnectLatch != null) { 180 mDisconnectLatch.countDown(); 181 } 182 } 183 } 184 185 public boolean waitForConnection(Integer sensorId) { 186 boolean ret; 187 mExpectSensorId = sensorId; 188 mConnectLatch = new CountDownLatch(1); 189 try { 190 ret = mConnectLatch.await(CONNECTION_TIMEOUT_SEC, TimeUnit.SECONDS); 191 } catch (InterruptedException e) { 192 ret = false; 193 Thread.currentThread().interrupt(); 194 } finally { 195 mConnectLatch = null; 196 } 197 return ret; 198 } 199 200 public boolean waitForDisconnection() { 201 boolean ret; 202 mDisconnectLatch = new CountDownLatch(1); 203 try { 204 ret = mDisconnectLatch.await(DISCONNECTION_TIMEOUT_SEC, TimeUnit.SECONDS); 205 } catch (InterruptedException e) { 206 ret = false; 207 Thread.currentThread().interrupt(); 208 } finally { 209 mDisconnectLatch = null; 210 } 211 return ret; 212 } 213 214 public boolean waitForSensorEvent() { 215 if (mSensor == null) { 216 Log.e(TAG, "Sensor is not set"); 217 return false; 218 } 219 220 final CountDownLatch eventLatch = new CountDownLatch(1); 221 222 SensorEventCallback eventCallback = 223 new SensorEventCallback() { 224 @Override 225 public void onSensorChanged(SensorEvent e) { 226 eventLatch.countDown(); 227 } 228 }; 229 230 if (!mSensorManager.registerListener( 231 eventCallback, mSensor, SensorManager.SENSOR_DELAY_FASTEST)) { 232 return false; 233 } 234 235 boolean ret; 236 try { 237 ret = eventLatch.await(EVENT_TIMEOUT_SEC, TimeUnit.SECONDS); 238 } catch (InterruptedException e) { 239 ret = false; 240 Thread.currentThread().interrupt(); 241 } finally { 242 mSensorManager.unregisterListener(eventCallback); 243 } 244 return ret; 245 } 246 247 public boolean isSensorInList() { 248 // This is intentional event if Sensor did not override equals(). 249 // Sensor objects representing the same sensor will be the same object. 250 return assumeSensorIsSet() && 251 mSensorManager.getDynamicSensorList(Sensor.TYPE_ALL).contains(mSensor); 252 } 253 254 public boolean isSensorInListOfSpecificType() { 255 // This is intentional event if Sensor did not override equals(). 256 // Sensor objects representing the same sensor will be the same object. 257 return assumeSensorIsSet() && 258 mSensorManager.getDynamicSensorList(mSensor.getType()).contains(mSensor); 259 } 260 261 public Integer getSensorId() { 262 return assumeSensorIsSet() ? mSensor.getId() : null; 263 } 264 265 // name assumeSensorIsSet instead of is... because the Log print is one of the main purpose. 266 private boolean assumeSensorIsSet() { 267 if (mSensor == null) { 268 Log.e(TAG, "Sensor is not set"); 269 return false; 270 } 271 return true; 272 } 273 } 274 275 private boolean isDynamicFlagSetCorrectly() { 276 boolean ret = true; 277 List<Sensor> dynamicSensors = mSensorManager.getDynamicSensorList(Sensor.TYPE_ALL); 278 for (Sensor s : dynamicSensors) { 279 if (!s.isDynamicSensor()) { 280 Log.e(TAG, String.format( 281 "Dynamic sensor \"%s\" isDynamicSensor() return false", s.getName())); 282 ret = false; 283 } 284 } 285 286 List<Sensor> staticSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); 287 for (Sensor s : staticSensors) { 288 if (s.isDynamicSensor()) { 289 Log.e(TAG, String.format( 290 "Static sensor \"%s\" isDynamicSensor() return true", s.getName())); 291 ret = false; 292 } 293 } 294 return ret; 295 } 296 297 private void featureSupportedOrSkip() { 298 if (!mFeatureSupported) { 299 throw new SensorTestStateNotSupportedException( 300 "Dynamic sensor discovery not supported, skip."); 301 } 302 } 303 304 private void sensorConnectedOrSkip() { 305 if (!mSensorConnected) { 306 throw new SensorTestStateNotSupportedException( 307 "Sensor not connected, skip."); 308 } 309 } 310 311 private void sensorDisconnectedOrSkip() { 312 if (!mSensorDisconnected) { 313 throw new SensorTestStateNotSupportedException( 314 "Sensor has not been disconnected, skip."); 315 } 316 } 317 318 /* 319 * This function serves as a proxy as appendText is marked to be deprecated. 320 * When appendText is removed, this function will have a different implementation. 321 * 322 */ 323 private void showUserMessage(String s) { 324 appendText(s); 325 } 326 } 327