blob: cedab27bfe0e682697d6d78aa7dc559ca5679e5d [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.verifier.sensors;
import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
import android.Manifest;
import com.android.cts.verifier.R;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.provider.Settings;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Test cases to verify step sensor permissions
*/
public class StepSensorPermissionTestActivity extends SensorCtsVerifierTestActivity
implements SensorEventListener {
private static final int STEP_DETECT_DELAY_SECONDS = 30;
private static final int STEP_COUNT_DELAY_SECONDS = 30;
// Additional amount of time to give for receiving either a step detect or
// count event in case the user hasn't started walking at the time the test
// starts.
private static final int ADDITIONAL_EVENT_DELAY_SECONDS = 2;
private SensorManager mSensorManager;
private boolean mHasAccelFeature = false;
private boolean mHasStepCounterFeature = false;
private boolean mHasStepDetectorFeature = false;
private CountDownLatch mEventReceivedLatch = null;
private Sensor mSensorUnderTest = null;
private AccelRecorder mAccelRecorder = null;
public StepSensorPermissionTestActivity() {
super(StepSensorPermissionTestActivity.class);
}
@Override
protected void activitySetUp() {
mSensorManager = (SensorManager) getApplicationContext()
.getSystemService(Context.SENSOR_SERVICE);
mHasAccelFeature = getApplicationContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_ACCELEROMETER);
mHasStepCounterFeature = getApplicationContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_STEP_COUNTER);
mHasStepDetectorFeature = getApplicationContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
mAccelRecorder = new AccelRecorder(mSensorManager);
}
@Override
protected void activityCleanUp() {
mSensorManager.unregisterListener(this);
mAccelRecorder.stop();
// Notify the user that the test has concluded. This will play in both the pass
// and fail case
try {
playSound();
} catch (InterruptedException e) {
// Ignored
}
}
/**
* Test cases.
*/
@SuppressWarnings("unused")
public String testStepEvents() throws Throwable {
if (mHasStepCounterFeature || mHasStepDetectorFeature) {
// Verify that sensors cannot be registered when the permission is not granted
runTest(false /* permissionGranted */);
// Signal to the user that the first part of the test is over
try {
playSound();
} catch (InterruptedException e) {
// Ignored
}
// Verify that the sensors can be registered when the permission is not granted
runTest(true /* permissionGranted */);
}
return "Pass";
}
private void runTest(boolean permissionGranted) throws InterruptedException {
if (hasPermission(Manifest.permission.ACTIVITY_RECOGNITION) != permissionGranted) {
requestChangePermission(permissionGranted);
}
waitForUser(R.string.snsr_step_permission_walk);
checkPermission(Manifest.permission.ACTIVITY_RECOGNITION, permissionGranted);
if (mHasStepDetectorFeature) {
checkSensor(Sensor.TYPE_STEP_DETECTOR, permissionGranted, STEP_DETECT_DELAY_SECONDS);
}
if (mHasStepCounterFeature) {
checkSensor(Sensor.TYPE_STEP_COUNTER, permissionGranted, STEP_COUNT_DELAY_SECONDS);
}
}
private void requestChangePermission(boolean enable) throws InterruptedException {
SensorTestLogger logger = getTestLogger();
if (enable) {
logger.logInstructions(R.string.snsr_step_permission_enable);
} else {
logger.logInstructions(R.string.snsr_step_permission_disable);
}
waitForUserToContinue();
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
private boolean hasPermission(String permission) {
return getApplicationContext().checkSelfPermission(permission) ==
PackageManager.PERMISSION_GRANTED;
}
private void checkPermission(String permission, boolean expected) {
if (expected && !hasPermission(permission)) {
throw new AssertionError(String.format("Should not have '%s' permission", permission));
} else if (!expected && hasPermission(permission)) {
throw new AssertionError(String.format("Should have '%s' permission", permission));
}
}
private void checkSensor(int sensorType, boolean expected, int eventDelaySeconds)
throws InterruptedException {
if (mHasAccelFeature && !expected) {
// Record accel when no events are expected to ensure that the device is moving during
// the test.
mAccelRecorder.start();
}
mEventReceivedLatch = new CountDownLatch(1);
Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
mSensorUnderTest = sensor;
String msg = String.format("Should %sbe able to register for '%s' events",
expected ? "" : "not ", sensor.getName());
assertTrue(msg, mSensorManager.registerListener(this, sensor, 0, 0) == expected);
boolean eventReceived = mEventReceivedLatch.await(
eventDelaySeconds + ADDITIONAL_EVENT_DELAY_SECONDS, TimeUnit.SECONDS);
// Ensure that events are only received when the application has permission
if (expected) {
assertTrue("Failed to receive event for " + sensor.getName(), eventReceived);
} else {
assertFalse("Should not have received event for " + sensor.getName(), eventReceived);
if (mHasAccelFeature) {
// Verify that the device was moving during the test
mAccelRecorder.stop();
assertTrue("Walking not detected", mAccelRecorder.variance() > 1.0f);
}
}
}
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor == mSensorUnderTest) {
mEventReceivedLatch.countDown();
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
class AccelRecorder implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mAccel;
private ArrayList<Float> mMagnitudes = new ArrayList<>();
public AccelRecorder(SensorManager sensorManager) {
mSensorManager = sensorManager;
mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
public void start() {
mMagnitudes.clear();
mSensorManager.registerListener(
this, mAccel, (int)TimeUnit.MILLISECONDS.toMicros(20), 0);
}
public void stop() {
mSensorManager.unregisterListener(this);
}
public float variance() {
if (mMagnitudes.size() <= 1) {
return 0.0f;
}
float mean = 0;
for (float val : mMagnitudes) {
mean += val;
}
mean /= mMagnitudes.size();
float var = 0;
for (float val : mMagnitudes) {
var += (val - mean) * (val - mean);
}
return var / (mMagnitudes.size() - 1);
}
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor == mAccel) {
float magnitude = sensorEvent.values[0] * sensorEvent.values[0] +
sensorEvent.values[1] * sensorEvent.values[1] +
sensorEvent.values[2] * sensorEvent.values[2];
mMagnitudes.add((float)Math.sqrt(magnitude));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
}