1 /* 2 * Copyright (C) 2022 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.compatibility.common.deviceinfo; 18 19 import android.content.Context; 20 import android.location.GnssCapabilities; 21 import android.location.GnssMeasurement; 22 import android.location.GnssMeasurementsEvent; 23 import android.location.LocationManager; 24 import android.os.Build; 25 26 import com.android.compatibility.common.util.DeviceInfoStore; 27 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.concurrent.CountDownLatch; 32 import java.util.concurrent.TimeUnit; 33 34 /** 35 * Gnss device info collector. 36 */ 37 public final class GnssDeviceInfo extends DeviceInfo { 38 39 private static final String LOG_TAG = "GnssDeviceInfo"; 40 private static final String UNKNOWN_GNSS_NAME = "unknown"; 41 private static final String NO_GNSS_HARDWARE = "no_gnss_hardware"; 42 public static final int ADR_STATE_VALID = (1 << 0); 43 44 @Override collectDeviceInfo(DeviceInfoStore store)45 protected void collectDeviceInfo(DeviceInfoStore store) throws Exception { 46 LocationManager locationManager = 47 (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE); 48 49 if (locationManager == null) { 50 store.addResult("gnss_hardware_model_name", UNKNOWN_GNSS_NAME); 51 return; 52 } 53 collectGnssHardwareModelName(store, locationManager); 54 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { 55 collectGnssCapabilities(store, locationManager.getGnssCapabilities()); 56 } 57 collectAccumulatedDeltaRangeMeasurements(store, locationManager); 58 } 59 60 /** 61 * collect info for gnss hardware model name. If there is no GNSS hardware, the function stores 62 * "unknown". If there is no name available, the function stores "no_gnss_hardware". 63 */ collectGnssHardwareModelName( DeviceInfoStore store, LocationManager locationManager)64 private void collectGnssHardwareModelName( 65 DeviceInfoStore store, LocationManager locationManager) throws IOException { 66 if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { 67 store.addResult("gnss_hardware_model_name", NO_GNSS_HARDWARE); 68 return; 69 } 70 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { 71 return; 72 } 73 String gnssHardwareModelName = locationManager.getGnssHardwareModelName(); 74 if (gnssHardwareModelName == null) { 75 store.addResult("gnss_hardware_model_name", UNKNOWN_GNSS_NAME); 76 return; 77 } 78 store.addResult("gnss_hardware_model_name", gnssHardwareModelName); 79 } 80 81 /** collect info for gnss capabilities into a group */ collectGnssCapabilities(DeviceInfoStore store, GnssCapabilities gnssCapabilities)82 private void collectGnssCapabilities(DeviceInfoStore store, GnssCapabilities gnssCapabilities) 83 throws IOException { 84 store.startGroup("gnss_capabilities"); 85 86 store.addResult("has_low_power_mode", gnssCapabilities.hasLowPowerMode()); 87 store.addResult("has_geofencing", gnssCapabilities.hasGeofencing()); 88 store.addResult("has_measurements", gnssCapabilities.hasMeasurements()); 89 store.addResult( 90 "has_measurement_corrections", gnssCapabilities.hasMeasurementCorrections()); 91 store.addResult( 92 "has_measurement_corrections_los_sats", 93 gnssCapabilities.hasMeasurementCorrectionsLosSats()); 94 store.addResult( 95 "has_measurement_corrections_excess_path_length", 96 gnssCapabilities.hasMeasurementCorrectionsExcessPathLength()); 97 98 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 99 store.addResult("has_satellite_blocklist", gnssCapabilities.hasSatelliteBlacklist()); 100 store.addResult("has_navigation_messages", gnssCapabilities.hasNavMessages()); 101 store.addResult( 102 "has_measurement_corrections_reflecting_plane", 103 gnssCapabilities.hasMeasurementCorrectionsReflectingPane()); 104 } else { 105 store.addResult("has_satellite_blocklist", gnssCapabilities.hasSatelliteBlocklist()); 106 store.addResult("has_navigation_messages", gnssCapabilities.hasNavigationMessages()); 107 store.addResult( 108 "has_measurement_corrections_reflecting_plane", 109 gnssCapabilities.hasMeasurementCorrectionsReflectingPlane()); 110 } 111 112 if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { 113 store.addResult("has_antenna_info", gnssCapabilities.hasGnssAntennaInfo()); 114 } 115 116 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 117 store.addResult("has_antenna_info", gnssCapabilities.hasAntennaInfo()); 118 store.addResult("has_satellite_pvt", gnssCapabilities.hasSatellitePvt()); 119 store.addResult( 120 "has_measurement_correlation_vectors", 121 gnssCapabilities.hasMeasurementCorrelationVectors()); 122 store.addResult( 123 "has_measurement_corrections_for_driving", 124 gnssCapabilities.hasMeasurementCorrectionsForDriving()); 125 } 126 store.endGroup(); 127 } 128 129 /** 130 * Collect Accumulated Delta Range info: 131 * 1. Start measurement with 1s interval and wait for up to 10 measurement events. 132 * 2. Set as true if a measurement has a valid AccumulatedDeltaRange state, false otherwise 133 */ collectAccumulatedDeltaRangeMeasurements(DeviceInfoStore store, LocationManager locationManager)134 private void collectAccumulatedDeltaRangeMeasurements(DeviceInfoStore store, 135 LocationManager locationManager) throws InterruptedException, IOException { 136 final int gnssMeasurementsEventsToCollect = 10; 137 TestGnssMeasurementListener mMeasurementListener = new TestGnssMeasurementListener( 138 gnssMeasurementsEventsToCollect); 139 locationManager.registerGnssMeasurementsCallback(mMeasurementListener); 140 mMeasurementListener.waitFor(); 141 boolean hasAccumulatedDeltaRange = false; 142 for (GnssMeasurementsEvent event : mMeasurementListener.getEvents()) { 143 for (GnssMeasurement measurement : event.getMeasurements()) { 144 if ((measurement.getAccumulatedDeltaRangeState() & ADR_STATE_VALID) 145 == ADR_STATE_VALID) { 146 hasAccumulatedDeltaRange = true; 147 break; 148 } 149 if (hasAccumulatedDeltaRange) break; 150 } 151 } 152 locationManager.unregisterGnssMeasurementsCallback(mMeasurementListener); 153 store.addResult("has_valid_accumulated_delta_range", hasAccumulatedDeltaRange); 154 } 155 156 private class TestGnssMeasurementListener extends GnssMeasurementsEvent.Callback { 157 public static final int MEAS_TIMEOUT_IN_SEC = 5; 158 private final List<GnssMeasurementsEvent> mMeasurementsEvents; 159 private final CountDownLatch mCountDownLatch; 160 private static final long STANDARD_WAIT_TIME_MS = 50; 161 private static final long STANDARD_SLEEP_TIME_MS = 50; 162 163 /** 164 * Constructor for TestGnssMeasurementListener 165 * 166 * @param eventsToCollect wait until this number of events collected. 167 */ TestGnssMeasurementListener(int eventsToCollect)168 TestGnssMeasurementListener(int eventsToCollect) { 169 mCountDownLatch = new CountDownLatch(eventsToCollect); 170 mMeasurementsEvents = new ArrayList<>(eventsToCollect); 171 } 172 173 @Override onGnssMeasurementsReceived(GnssMeasurementsEvent event)174 public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) { 175 if (event.getMeasurements().size() > 0) { 176 synchronized (mMeasurementsEvents) { 177 mMeasurementsEvents.add(event); 178 } 179 mCountDownLatch.countDown(); 180 } 181 } 182 183 /** 184 * Get the current list of GPS Measurements Events. 185 * 186 * @return the current list of GPS Measurements Events 187 */ getEvents()188 public List<GnssMeasurementsEvent> getEvents() { 189 synchronized (mMeasurementsEvents) { 190 List<GnssMeasurementsEvent> clone = new ArrayList<>(); 191 clone.addAll(mMeasurementsEvents); 192 return clone; 193 } 194 } 195 waitFor()196 public boolean waitFor() throws InterruptedException { 197 long waitTimeRounds = (TimeUnit.SECONDS.toMillis(MEAS_TIMEOUT_IN_SEC)) 198 / (STANDARD_WAIT_TIME_MS + STANDARD_SLEEP_TIME_MS); 199 for (int i = 0; i < waitTimeRounds; ++i) { 200 Thread.sleep(STANDARD_SLEEP_TIME_MS); 201 if (mCountDownLatch.await(STANDARD_WAIT_TIME_MS, TimeUnit.MILLISECONDS)) { 202 return true; 203 } 204 } 205 return false; 206 } 207 } 208 } 209