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