1 /*
2  * Copyright (C) 2019 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.sensors.base.SensorCtsVerifierTestActivity;
20 
21 import android.Manifest;
22 import com.android.cts.verifier.R;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.hardware.Sensor;
27 import android.hardware.SensorEvent;
28 import android.hardware.SensorEventListener;
29 import android.hardware.SensorManager;
30 import android.net.Uri;
31 import android.provider.Settings;
32 
33 import java.util.ArrayList;
34 import java.util.concurrent.CountDownLatch;
35 import java.util.concurrent.TimeUnit;
36 
37 import static org.junit.Assert.assertFalse;
38 import static org.junit.Assert.assertTrue;
39 
40 /**
41  * Test cases to verify step sensor permissions
42  */
43 public class StepSensorPermissionTestActivity extends SensorCtsVerifierTestActivity
44         implements SensorEventListener {
45     private static final int STEP_DETECT_DELAY_SECONDS = 30;
46     private static final int STEP_COUNT_DELAY_SECONDS = 30;
47     // Additional amount of time to give for receiving either a step detect or
48     // count event in case the user hasn't started walking at the time the test
49     // starts.
50     private static final int ADDITIONAL_EVENT_DELAY_SECONDS = 2;
51 
52     private SensorManager mSensorManager;
53 
54     private boolean mHasAccelFeature = false;
55     private boolean mHasStepCounterFeature = false;
56     private boolean mHasStepDetectorFeature = false;
57     private CountDownLatch mEventReceivedLatch = null;
58     private Sensor mSensorUnderTest = null;
59     private AccelRecorder mAccelRecorder = null;
60 
StepSensorPermissionTestActivity()61     public StepSensorPermissionTestActivity() {
62         super(StepSensorPermissionTestActivity.class);
63     }
64 
65     @Override
activitySetUp()66     protected void activitySetUp() {
67         mSensorManager = (SensorManager) getApplicationContext()
68                 .getSystemService(Context.SENSOR_SERVICE);
69 
70         mHasAccelFeature = getApplicationContext().getPackageManager().hasSystemFeature(
71                 PackageManager.FEATURE_SENSOR_ACCELEROMETER);
72         mHasStepCounterFeature = getApplicationContext().getPackageManager().hasSystemFeature(
73                 PackageManager.FEATURE_SENSOR_STEP_COUNTER);
74         mHasStepDetectorFeature = getApplicationContext().getPackageManager().hasSystemFeature(
75                 PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
76 
77         mAccelRecorder = new AccelRecorder(mSensorManager);
78     }
79 
80     @Override
activityCleanUp()81     protected void activityCleanUp() {
82         mSensorManager.unregisterListener(this);
83         mAccelRecorder.stop();
84 
85         // Notify the user that the test has concluded. This will play in both the pass
86         // and fail case
87         try {
88             playSound();
89         } catch (InterruptedException e) {
90             // Ignored
91         }
92     }
93 
94     /**
95      * Test cases.
96      */
97     @SuppressWarnings("unused")
testStepEvents()98     public String testStepEvents() throws Throwable {
99         if (mHasStepCounterFeature || mHasStepDetectorFeature) {
100             // Verify that sensors cannot be registered when the permission is not granted
101             runTest(false /* permissionGranted */);
102 
103             // Signal to the user that the first part of the test is over
104             try {
105                 playSound();
106             } catch (InterruptedException e) {
107                 // Ignored
108             }
109 
110             // Verify that the sensors can be registered when the permission is not granted
111             runTest(true /* permissionGranted */);
112         }
113 
114         return "Pass";
115     }
116 
runTest(boolean permissionGranted)117     private void runTest(boolean permissionGranted) throws InterruptedException {
118         if (hasPermission(Manifest.permission.ACTIVITY_RECOGNITION) != permissionGranted) {
119             requestChangePermission(permissionGranted);
120         }
121 
122         waitForUser(R.string.snsr_step_permission_walk);
123         checkPermission(Manifest.permission.ACTIVITY_RECOGNITION, permissionGranted);
124 
125         if (mHasStepDetectorFeature) {
126             checkSensor(Sensor.TYPE_STEP_DETECTOR, permissionGranted, STEP_DETECT_DELAY_SECONDS);
127         }
128 
129         if (mHasStepCounterFeature) {
130             checkSensor(Sensor.TYPE_STEP_COUNTER, permissionGranted, STEP_COUNT_DELAY_SECONDS);
131         }
132     }
133 
requestChangePermission(boolean enable)134     private void requestChangePermission(boolean enable) throws InterruptedException {
135         SensorTestLogger logger = getTestLogger();
136         if (enable) {
137             logger.logInstructions(R.string.snsr_step_permission_enable);
138         } else {
139             logger.logInstructions(R.string.snsr_step_permission_disable);
140         }
141         waitForUserToContinue();
142         Intent intent = new Intent();
143         intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
144         Uri uri = Uri.fromParts("package", getPackageName(), null);
145         intent.setData(uri);
146         startActivity(intent);
147     }
148 
hasPermission(String permission)149     private boolean hasPermission(String permission) {
150         return getApplicationContext().checkSelfPermission(permission) ==
151                 PackageManager.PERMISSION_GRANTED;
152     }
153 
checkPermission(String permission, boolean expected)154     private void checkPermission(String permission, boolean expected) {
155         if (expected && !hasPermission(permission)) {
156             throw new AssertionError(String.format("Should not have '%s' permission", permission));
157         } else if (!expected && hasPermission(permission)) {
158             throw new AssertionError(String.format("Should have '%s' permission", permission));
159         }
160     }
161 
checkSensor(int sensorType, boolean expected, int eventDelaySeconds)162     private void checkSensor(int sensorType, boolean expected, int eventDelaySeconds)
163             throws InterruptedException {
164         if (mHasAccelFeature && !expected) {
165             // Record accel when no events are expected to ensure that the device is moving during
166             // the test.
167             mAccelRecorder.start();
168         }
169 
170         mEventReceivedLatch = new CountDownLatch(1);
171         Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
172         mSensorUnderTest = sensor;
173 
174         String msg = String.format("Should %sbe able to register for '%s' events",
175                 expected ? "" : "not ", sensor.getName());
176         assertTrue(msg, mSensorManager.registerListener(this, sensor, 0, 0) == expected);
177 
178         boolean eventReceived = mEventReceivedLatch.await(
179                 eventDelaySeconds + ADDITIONAL_EVENT_DELAY_SECONDS, TimeUnit.SECONDS);
180 
181         // Ensure that events are only received when the application has permission
182         if (expected) {
183             assertTrue("Failed to receive event for " + sensor.getName(), eventReceived);
184         } else {
185             assertFalse("Should not have received event for " + sensor.getName(), eventReceived);
186 
187             if (mHasAccelFeature) {
188                 // Verify that the device was moving during the test
189                 mAccelRecorder.stop();
190                 assertTrue("Walking not detected", mAccelRecorder.variance() > 1.0f);
191             }
192         }
193     }
194 
195     @Override
onSensorChanged(SensorEvent sensorEvent)196     public void onSensorChanged(SensorEvent sensorEvent) {
197         if (sensorEvent.sensor == mSensorUnderTest) {
198             mEventReceivedLatch.countDown();
199         }
200     }
201 
202     @Override
onAccuracyChanged(Sensor sensor, int i)203     public void onAccuracyChanged(Sensor sensor, int i) {
204     }
205 
206     class AccelRecorder implements SensorEventListener {
207 
208         private SensorManager mSensorManager;
209         private Sensor mAccel;
210         private ArrayList<Float> mMagnitudes = new ArrayList<>();
211 
AccelRecorder(SensorManager sensorManager)212         public AccelRecorder(SensorManager sensorManager) {
213             mSensorManager = sensorManager;
214             mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
215         }
216 
start()217         public void start() {
218             mMagnitudes.clear();
219             mSensorManager.registerListener(
220                     this, mAccel, (int)TimeUnit.MILLISECONDS.toMicros(20), 0);
221         }
222 
stop()223         public void stop() {
224             mSensorManager.unregisterListener(this);
225         }
226 
variance()227         public float variance() {
228             if (mMagnitudes.size() <= 1) {
229                 return 0.0f;
230             }
231 
232             float mean = 0;
233             for (float val : mMagnitudes) {
234                 mean += val;
235             }
236             mean /= mMagnitudes.size();
237 
238             float var = 0;
239             for (float val : mMagnitudes) {
240                 var += (val - mean) * (val - mean);
241             }
242             return var / (mMagnitudes.size() - 1);
243         }
244 
245         @Override
onSensorChanged(SensorEvent sensorEvent)246         public void onSensorChanged(SensorEvent sensorEvent) {
247             if (sensorEvent.sensor == mAccel) {
248                 float magnitude = sensorEvent.values[0] * sensorEvent.values[0] +
249                         sensorEvent.values[1] * sensorEvent.values[1] +
250                         sensorEvent.values[2] * sensorEvent.values[2];
251                 mMagnitudes.add((float)Math.sqrt(magnitude));
252             }
253         }
254 
255         @Override
onAccuracyChanged(Sensor sensor, int i)256         public void onAccuracyChanged(Sensor sensor, int i) {
257 
258         }
259     }
260 }
261