blob: c8720703a17d6b7cb6ae75dcd2acc94e068cfb21 [file] [log] [blame]
package android.location.cts.gnss;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import android.app.UiAutomation;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.location.GnssStatus;
import android.location.cts.common.GnssTestCase;
import android.location.cts.common.SoftAssert;
import android.location.cts.common.TestLocationListener;
import android.location.cts.common.TestLocationManager;
import android.location.cts.common.TestMeasurementUtil;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import java.util.ArrayList;
import java.util.List;
public class GnssStatusTest extends GnssTestCase {
private static final String TAG = "GnssStatusTest";
private static final int LOCATION_TO_COLLECT_COUNT = 1;
private static final int STATUS_TO_COLLECT_COUNT = 3;
private UiAutomation mUiAutomation;
@Override
protected void setUp() throws Exception {
super.setUp();
mTestLocationManager = new TestLocationManager(getContext());
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
}
/**
* Tests that one can listen for {@link GnssStatus}.
*/
@AppModeFull(reason = "Instant apps cannot access package manager to scan for permissions")
public void testGnssStatusChanges() throws Exception {
// Checks if GPS hardware feature is present, skips test (pass) if not
if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, TAG)) {
return;
}
// Revoke location permissions from packages before running GnssStatusTest stops
// active location requests, allowing this test to receive all necessary Gnss callbacks.
List<String> courseLocationPackages = revokePermissions(ACCESS_COARSE_LOCATION);
List<String> fineLocationPackages = revokePermissions(ACCESS_FINE_LOCATION);
try {
// Register Gps Status Listener.
TestGnssStatusCallback testGnssStatusCallback =
new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
checkGnssChange(testGnssStatusCallback);
} finally {
// For each location package, re-grant the permission
grantLocationPermissions(ACCESS_COARSE_LOCATION, courseLocationPackages);
grantLocationPermissions(ACCESS_FINE_LOCATION, fineLocationPackages);
}
}
private void checkGnssChange(TestGnssStatusCallback testGnssStatusCallback)
throws InterruptedException {
mTestLocationManager.registerGnssStatusCallback(testGnssStatusCallback);
TestLocationListener locationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
mTestLocationManager.requestLocationUpdates(locationListener);
boolean success = testGnssStatusCallback.awaitStart();
success = success ? testGnssStatusCallback.awaitStatus() : false;
success = success ? testGnssStatusCallback.awaitTtff() : false;
mTestLocationManager.removeLocationUpdates(locationListener);
success = success ? testGnssStatusCallback.awaitStop() : false;
mTestLocationManager.unregisterGnssStatusCallback(testGnssStatusCallback);
SoftAssert softAssert = new SoftAssert(TAG);
softAssert.assertTrue(
"Time elapsed without getting the right status changes."
+ " Possibly, the test has been run deep indoors."
+ " Consider retrying test outdoors.",
success);
softAssert.assertAll();
}
/**
* Tests values of {@link GnssStatus}.
*/
public void testGnssStatusValues() throws InterruptedException {
// Checks if GPS hardware feature is present, skips test (pass) if not
if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, TAG)) {
return;
}
SoftAssert softAssert = new SoftAssert(TAG);
// Register Gps Status Listener.
TestGnssStatusCallback testGnssStatusCallback =
new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
checkGnssChange(testGnssStatusCallback);
validateGnssStatus(testGnssStatusCallback.getGnssStatus(), softAssert);
softAssert.assertAll();
}
/**
* To validate the fields in GnssStatus class, the value is got from device
* @param status, GnssStatus
* @param softAssert, customized assert class.
*/
private void validateGnssStatus(GnssStatus status, SoftAssert softAssert) {
int sCount = status.getSatelliteCount();
Log.i(TAG, "Total satellite:" + sCount);
// total number of satellites for all constellation is less than 200
softAssert.assertTrue("Satellite count test sCount : " + sCount , sCount < 200);
for (int i = 0; i < sCount; ++i) {
softAssert.assertTrue("azimuth_degrees: Azimuth in degrees: ",
"0.0 <= X <= 360.0",
String.valueOf(status.getAzimuthDegrees(i)),
status.getAzimuthDegrees(i) >= 0.0 && status.getAzimuthDegrees(i) <= 360.0);
TestMeasurementUtil.verifyGnssCarrierFrequency(softAssert, mTestLocationManager,
status.hasCarrierFrequencyHz(i),
status.hasCarrierFrequencyHz(i) ? status.getCarrierFrequencyHz(i) : 0F);
softAssert.assertTrue("c_n0_dbhz: Carrier-to-noise density",
"0.0 <= X <= 63",
String.valueOf(status.getCn0DbHz(i)),
status.getCn0DbHz(i) >= 0.0 &&
status.getCn0DbHz(i) <= 63.0);
Log.i(TAG, "hasBasebandCn0DbHz: " + status.hasBasebandCn0DbHz(i));
if (status.hasBasebandCn0DbHz(i)) {
softAssert.assertTrue("baseband_cn0_dbhz: Baseband carrier-to-noise density",
"0.0 <= X <= 63",
String.valueOf(status.getBasebandCn0DbHz(i)),
status.getBasebandCn0DbHz(i) >= 0.0 &&
status.getBasebandCn0DbHz(i) <= 63.0);
}
softAssert.assertTrue("elevation_degrees: Elevation in Degrees :",
"0.0 <= X <= 90.0",
String.valueOf(status.getElevationDegrees(i)),
status.getElevationDegrees(i) >= 0.0 && status.getElevationDegrees(i) <= 90.0);
// in validateSvidSub, it will validate ConstellationType, svid
// however, we don't have the event time in the current scope, pass in "-1" instead
TestMeasurementUtil.validateSvidSub(softAssert, null,
status.getConstellationType(i),status.getSvid(i));
// For those function with boolean type return, just simply call the function
// to make sure those function won't crash, also increase the test coverage.
Log.i(TAG, "hasAlmanacData: " + status.hasAlmanacData(i));
Log.i(TAG, "hasEphemerisData: " + status.hasEphemerisData(i));
Log.i(TAG, "usedInFix: " + status.usedInFix(i));
}
}
private List<String> getPackagesWithPermissions(String permission) {
Context context = InstrumentationRegistry.getTargetContext();
PackageManager pm = context.getPackageManager();
ArrayList<String> packagesWithPermission = new ArrayList<>();
List<ApplicationInfo> packages = pm.getInstalledApplications(/*flags=*/ 0);
for (ApplicationInfo applicationInfo : packages) {
String packageName = applicationInfo.packageName;
if (packageName.equals(context.getPackageName())) {
// Don't include this test package.
continue;
}
if (pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED) {
final int flags;
mUiAutomation.adoptShellPermissionIdentity("android.permission.GET_RUNTIME_PERMISSIONS");
try {
flags = pm.getPermissionFlags(permission, packageName,
android.os.Process.myUserHandle());
} finally {
mUiAutomation.dropShellPermissionIdentity();
}
final boolean fixed = (flags & (PackageManager.FLAG_PERMISSION_USER_FIXED
| PackageManager.FLAG_PERMISSION_POLICY_FIXED
| PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) != 0;
if (!fixed) {
packagesWithPermission.add(packageName);
}
}
}
return packagesWithPermission;
}
private List<String> revokePermissions(String permission) {
List<String> packages = getPackagesWithPermissions(permission);
for (String packageWithPermission : packages) {
Log.i(TAG, "Revoking permissions from: " + packageWithPermission);
mUiAutomation.revokeRuntimePermission(packageWithPermission, permission);
}
return packages;
}
private void grantLocationPermissions(String permission, List<String> packages) {
for (String packageToGivePermission : packages) {
Log.i(TAG, "Granting permissions (back) to: " + packageToGivePermission);
mUiAutomation.grantRuntimePermission(packageToGivePermission, permission);
}
}
}