1 /* 2 * Copyright (C) 2020 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 android.hardware.cts.helpers; 18 19 import android.content.Context; 20 import android.hardware.HardwareBuffer; 21 import android.hardware.Sensor; 22 import android.hardware.SensorDirectChannel; 23 import android.hardware.SensorManager; 24 import android.hardware.SensorPrivacyManager; 25 import android.hardware.cts.SensorDirectReportTest; 26 27 import com.android.compatibility.common.util.ShellUtils; 28 import com.android.compatibility.common.util.SystemUtil; 29 30 import com.google.common.collect.ImmutableSet; 31 32 import org.junit.Assert; 33 import org.junit.Assume; 34 35 import java.nio.ByteBuffer; 36 import java.nio.ByteOrder; 37 import java.util.List; 38 import java.util.concurrent.TimeUnit; 39 import java.util.stream.Collectors; 40 41 /** 42 * A helper class to test sampling rates of direct sensor channels. 43 */ 44 public class SensorRatePermissionDirectReportTestHelper { 45 public static final int CAPPED_SAMPLE_RATE_HZ = 200; 46 public static final int CAPPED_DIRECT_REPORT_RATE_LEVEL = SensorDirectChannel.RATE_NORMAL; 47 // Set of sensors that are throttled 48 public static final ImmutableSet<Integer> CAPPED_SENSOR_TYPE_SET = ImmutableSet.of( 49 Sensor.TYPE_ACCELEROMETER, 50 Sensor.TYPE_ACCELEROMETER_UNCALIBRATED, 51 Sensor.TYPE_GYROSCOPE, 52 Sensor.TYPE_GYROSCOPE_UNCALIBRATED, 53 Sensor.TYPE_MAGNETIC_FIELD, 54 Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED 55 ); 56 public static final int TEST_RUN_TIME_PERIOD_MILLISEC = 1000; 57 public static final int SENSORS_EVENT_SIZE = 104; 58 59 static { 60 System.loadLibrary("cts-sensors-ndk-jni"); 61 } 62 63 private final SensorManager mSensorManager; 64 65 private Sensor mSensor; 66 SensorRatePermissionDirectReportTestHelper(Context context, int sensorType)67 public SensorRatePermissionDirectReportTestHelper(Context context, int sensorType) { 68 mSensorManager = context.getSystemService(SensorManager.class); 69 mSensor = null; 70 for (Sensor sensor : mSensorManager.getSensorList(sensorType)) { 71 if (!CAPPED_SENSOR_TYPE_SET.contains(sensor.getType())) { 72 continue; 73 } 74 if (sensor.isDirectChannelTypeSupported(SensorDirectChannel.TYPE_HARDWARE_BUFFER)) { 75 mSensor = sensor; 76 break; 77 } 78 } 79 Assume.assumeTrue("Failed to find a sensor!", mSensor != null); 80 } 81 nativeReadHardwareBuffer(HardwareBuffer hardwareBuffer, byte[] buffer, int srcOffset, int destOffset, int count)82 private static native boolean nativeReadHardwareBuffer(HardwareBuffer hardwareBuffer, 83 byte[] buffer, int srcOffset, int destOffset, int count); 84 computeAvgRate(List<SensorDirectReportTest.DirectReportSensorEvent> events, long startTimestamp, long endTimestamp)85 public static double computeAvgRate(List<SensorDirectReportTest.DirectReportSensorEvent> events, 86 long startTimestamp, long endTimestamp) { 87 88 List<SensorDirectReportTest.DirectReportSensorEvent> filteredEvents = events.stream() 89 .filter(event -> event.ts > startTimestamp && event.ts < endTimestamp) 90 .collect(Collectors.toList()); 91 92 double rate = Double.MIN_VALUE; 93 int numOfEvents = filteredEvents.size(); 94 if (numOfEvents >= 2) { 95 long lastTimestamp = filteredEvents.get(numOfEvents - 1).ts; 96 long firstTimestamp = filteredEvents.get(0).ts; 97 rate = SensorCtsHelper.getFrequency( 98 (lastTimestamp - firstTimestamp) / (numOfEvents - 1), 99 TimeUnit.NANOSECONDS); 100 } 101 return rate; 102 } 103 getSensor()104 public Sensor getSensor() { 105 return mSensor; 106 } 107 108 /** 109 * Error message being shown in Assert statements of unit tests when the sampling rate exceeds 110 * the allowed capped rate. 111 */ errorWhenExceedCappedRate()112 public String errorWhenExceedCappedRate() { 113 return String.format( 114 "%s: Sampling rate is expected to be less than or equal to %d (Hz)", 115 mSensor.getName(), 116 CAPPED_SAMPLE_RATE_HZ); 117 } 118 119 /** 120 * Error message being shown in Assert statements of unit tests when the sampling rate is below 121 * its expected rate. 122 */ errorWhenBelowExpectedRate()123 public String errorWhenBelowExpectedRate() { 124 return String.format( 125 "%s: Sampling rate is expected to larger than to %d (Hz)", 126 mSensor.getName(), 127 CAPPED_SAMPLE_RATE_HZ); 128 } 129 130 /** 131 * Flip the microphone toggle to off and assert that it is indeed off. 132 */ flipAndAssertMicToggleOff(int userID, SensorPrivacyManager spm)133 public void flipAndAssertMicToggleOff(int userID, SensorPrivacyManager spm) { 134 ShellUtils.runShellCommand("cmd sensor_privacy disable " + userID + " microphone"); 135 SystemUtil.runWithShellPermissionIdentity(() -> { 136 Assert.assertTrue("Failed to switch the mic toggle off!", 137 !spm.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.MICROPHONE)); 138 }); 139 } 140 141 /** 142 * Flip the microphone toggle to off and assert that it is indeed on. 143 */ flipAndAssertMicToggleOn(int userID, SensorPrivacyManager spm)144 public void flipAndAssertMicToggleOn(int userID, SensorPrivacyManager spm) { 145 ShellUtils.runShellCommand("cmd sensor_privacy enable " + userID + " microphone"); 146 SystemUtil.runWithShellPermissionIdentity(() -> { 147 Assert.assertTrue("Failed to switch the mic toggle on!", 148 spm.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.MICROPHONE)); 149 }); 150 } 151 152 /** 153 * Configure a direct channel and return the sensor data in a DirectReportSensorEvent list. 154 */ getSensorEvents(int rateLevel)155 public List<SensorDirectReportTest.DirectReportSensorEvent> getSensorEvents(int rateLevel) 156 throws InterruptedException { 157 int sensorEventCount = 2000; // 800 Hz * 2.2 * 1s + extra 158 int sharedMemorySize = sensorEventCount * SENSORS_EVENT_SIZE; 159 HardwareBuffer hardwareBuffer = HardwareBuffer.create( 160 sharedMemorySize, 1, HardwareBuffer.BLOB, 1, 161 HardwareBuffer.USAGE_CPU_READ_OFTEN | HardwareBuffer.USAGE_GPU_DATA_BUFFER 162 | HardwareBuffer.USAGE_SENSOR_DIRECT_DATA); 163 164 SensorDirectChannel channel = mSensorManager.createDirectChannel(hardwareBuffer); 165 int token = channel.configure(mSensor, rateLevel); 166 SensorCtsHelper.sleep(TEST_RUN_TIME_PERIOD_MILLISEC, TimeUnit.MILLISECONDS); 167 channel.configure(mSensor, SensorDirectChannel.RATE_STOP); 168 List<SensorDirectReportTest.DirectReportSensorEvent> events = 169 readEventsFromHardwareBuffer(token, hardwareBuffer, sensorEventCount); 170 channel.close(); 171 hardwareBuffer.close(); 172 return events; 173 } 174 175 /** 176 * Parse HardwareBuffer to return a list of DirectReportSensorEvents 177 */ readEventsFromHardwareBuffer( int token, HardwareBuffer hardwareBuffer, int sensorEventCount)178 public List<SensorDirectReportTest.DirectReportSensorEvent> readEventsFromHardwareBuffer( 179 int token, HardwareBuffer hardwareBuffer, int sensorEventCount) { 180 int sharedMemorySize = sensorEventCount * SENSORS_EVENT_SIZE; 181 SensorDirectReportTest.EventPool eventPool = new SensorDirectReportTest.EventPool( 182 10 * sensorEventCount); 183 ByteBuffer byteBuffer = ByteBuffer.allocate(sharedMemorySize); 184 byte[] buffer = byteBuffer.array(); 185 byteBuffer.order(ByteOrder.nativeOrder()); 186 nativeReadHardwareBuffer(hardwareBuffer, buffer, 0, 0, sharedMemorySize); 187 List<SensorDirectReportTest.DirectReportSensorEvent> events = 188 SensorDirectReportTest.parseEntireBuffer(token, eventPool, byteBuffer, 189 sharedMemorySize); 190 eventPool.reset(); 191 byteBuffer.clear(); 192 return events; 193 } 194 } 195