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 com.android.server.location.gnss; 18 19 import android.content.Context; 20 import android.os.PersistableBundle; 21 import android.os.SystemProperties; 22 import android.telephony.CarrierConfigManager; 23 import android.telephony.SubscriptionManager; 24 import android.text.TextUtils; 25 import android.util.Log; 26 27 import com.android.internal.util.FrameworkStatsLog; 28 29 import libcore.io.IoUtils; 30 31 import java.io.File; 32 import java.io.FileInputStream; 33 import java.io.IOException; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Map.Entry; 40 import java.util.Properties; 41 42 /** 43 * A utility class to hold GNSS configuration properties. 44 * 45 * The trigger to load/reload the configuration parameters should be managed by the class 46 * that owns an instance of this class. 47 * 48 * Instances of this class are not thread-safe and should either be used from a single thread 49 * or with external synchronization when used by multiple threads. 50 */ 51 public class GnssConfiguration { 52 private static final String TAG = "GnssConfiguration"; 53 54 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 55 56 private static final String DEBUG_PROPERTIES_FILE = "/etc/gps_debug.conf"; 57 58 // config.xml properties 59 private static final String CONFIG_SUPL_HOST = "SUPL_HOST"; 60 private static final String CONFIG_SUPL_PORT = "SUPL_PORT"; 61 private static final String CONFIG_C2K_HOST = "C2K_HOST"; 62 private static final String CONFIG_C2K_PORT = "C2K_PORT"; 63 private static final String CONFIG_SUPL_VER = "SUPL_VER"; 64 private static final String CONFIG_SUPL_MODE = "SUPL_MODE"; 65 private static final String CONFIG_SUPL_ES = "SUPL_ES"; 66 private static final String CONFIG_LPP_PROFILE = "LPP_PROFILE"; 67 private static final String CONFIG_A_GLONASS_POS_PROTOCOL_SELECT = 68 "A_GLONASS_POS_PROTOCOL_SELECT"; 69 private static final String CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL = 70 "USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL"; 71 private static final String CONFIG_GPS_LOCK = "GPS_LOCK"; 72 private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC"; 73 public static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS"; 74 75 // Limit on NI emergency mode time extension after emergency sessions ends 76 private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum 77 78 // Persist property for LPP_PROFILE 79 static final String LPP_PROFILE = "persist.sys.gps.lpp"; 80 81 // Represents an HAL interface version. Instances of this class are created in the JNI layer 82 // and returned through native methods. 83 static class HalInterfaceVersion { 84 final int mMajor; 85 final int mMinor; 86 HalInterfaceVersion(int major, int minor)87 HalInterfaceVersion(int major, int minor) { 88 mMajor = major; 89 mMinor = minor; 90 } 91 } 92 93 /** 94 * Properties loaded from PROPERTIES_FILE. 95 */ 96 private final Properties mProperties; 97 98 private int mEsExtensionSec = 0; 99 100 private final Context mContext; 101 GnssConfiguration(Context context)102 public GnssConfiguration(Context context) { 103 mContext = context; 104 mProperties = new Properties(); 105 } 106 107 /** 108 * Returns the full set of properties loaded. 109 */ getProperties()110 Properties getProperties() { 111 return mProperties; 112 } 113 114 /** 115 * Returns the value of config parameter ES_EXTENSION_SEC. The value is range checked 116 * and constrained to min/max limits. 117 */ getEsExtensionSec()118 public int getEsExtensionSec() { 119 return mEsExtensionSec; 120 } 121 122 /** 123 * Returns the value of config parameter SUPL_HOST or {@code null} if no value is 124 * provided. 125 */ getSuplHost()126 String getSuplHost() { 127 return mProperties.getProperty(CONFIG_SUPL_HOST); 128 } 129 130 /** 131 * Returns the value of config parameter SUPL_PORT or {@code defaultPort} if no value is 132 * provided or if there is an error parsing the configured value. 133 */ getSuplPort(int defaultPort)134 int getSuplPort(int defaultPort) { 135 return getIntConfig(CONFIG_SUPL_PORT, defaultPort); 136 } 137 138 /** 139 * Returns the value of config parameter C2K_HOST or {@code null} if no value is 140 * provided. 141 */ getC2KHost()142 String getC2KHost() { 143 return mProperties.getProperty(CONFIG_C2K_HOST); 144 } 145 146 /** 147 * Returns the value of config parameter C2K_PORT or {@code defaultPort} if no value is 148 * provided or if there is an error parsing the configured value. 149 */ getC2KPort(int defaultPort)150 int getC2KPort(int defaultPort) { 151 return getIntConfig(CONFIG_C2K_PORT, defaultPort); 152 } 153 154 /** 155 * Returns the value of config parameter SUPL_MODE or {@code defaultMode} if no value is 156 * provided or if there is an error parsing the configured value. 157 */ getSuplMode(int defaultMode)158 int getSuplMode(int defaultMode) { 159 return getIntConfig(CONFIG_SUPL_MODE, defaultMode); 160 } 161 162 /** 163 * Returns the value of config parameter SUPL_ES or {@code defaultSuplEs} if no value is 164 * provided or if there is an error parsing the configured value. 165 */ getSuplEs(int defaultSuplEs)166 public int getSuplEs(int defaultSuplEs) { 167 return getIntConfig(CONFIG_SUPL_ES, defaultSuplEs); 168 } 169 170 /** 171 * Returns the value of config parameter LPP_PROFILE or {@code null} if no value is 172 * provided. 173 */ getLppProfile()174 String getLppProfile() { 175 return mProperties.getProperty(CONFIG_LPP_PROFILE); 176 } 177 178 /** 179 * Returns the list of proxy apps from the value of config parameter NFW_PROXY_APPS. 180 */ getProxyApps()181 List<String> getProxyApps() { 182 // Space separated list of Android proxy app package names. 183 String proxyAppsStr = mProperties.getProperty(CONFIG_NFW_PROXY_APPS); 184 if (TextUtils.isEmpty(proxyAppsStr)) { 185 return Collections.emptyList(); 186 } 187 188 String[] proxyAppsArray = proxyAppsStr.trim().split("\\s+"); 189 if (proxyAppsArray.length == 0) { 190 return Collections.emptyList(); 191 } 192 193 return Arrays.asList(proxyAppsArray); 194 } 195 196 /** 197 * Updates the GNSS HAL satellite denylist. 198 */ setSatelliteBlocklist(int[] constellations, int[] svids)199 void setSatelliteBlocklist(int[] constellations, int[] svids) { 200 native_set_satellite_blocklist(constellations, svids); 201 } 202 getHalInterfaceVersion()203 HalInterfaceVersion getHalInterfaceVersion() { 204 return native_get_gnss_configuration_version(); 205 } 206 207 interface SetCarrierProperty { set(int value)208 boolean set(int value); 209 } 210 211 /** 212 * Loads the GNSS properties from carrier config file followed by the properties from 213 * gps debug config file. 214 */ reloadGpsProperties()215 void reloadGpsProperties() { 216 if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + mProperties.size()); 217 loadPropertiesFromCarrierConfig(); 218 219 String lpp_prof = SystemProperties.get(LPP_PROFILE); 220 if (!TextUtils.isEmpty(lpp_prof)) { 221 // override default value of this if lpp_prof is not empty 222 mProperties.setProperty(CONFIG_LPP_PROFILE, lpp_prof); 223 } 224 225 /* 226 * Overlay carrier properties from a debug configuration file. 227 */ 228 loadPropertiesFromGpsDebugConfig(mProperties); 229 mEsExtensionSec = getRangeCheckedConfigEsExtensionSec(); 230 231 logConfigurations(); 232 233 final HalInterfaceVersion gnssConfigurationIfaceVersion = getHalInterfaceVersion(); 234 if (gnssConfigurationIfaceVersion != null) { 235 // Set to a range checked value. 236 if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion) 237 && !native_set_es_extension_sec(mEsExtensionSec)) { 238 Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec); 239 } 240 241 Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() { 242 { 243 put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version); 244 put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode); 245 246 if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) { 247 put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es); 248 } 249 250 put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile); 251 put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 252 GnssConfiguration::native_set_gnss_pos_protocol_select); 253 put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 254 GnssConfiguration::native_set_emergency_supl_pdn); 255 256 if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) { 257 put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock); 258 } 259 } 260 }; 261 262 for (Entry<String, SetCarrierProperty> entry : map.entrySet()) { 263 String propertyName = entry.getKey(); 264 String propertyValueString = mProperties.getProperty(propertyName); 265 if (propertyValueString != null) { 266 try { 267 int propertyValueInt = Integer.decode(propertyValueString); 268 boolean result = entry.getValue().set(propertyValueInt); 269 if (!result) { 270 Log.e(TAG, "Unable to set " + propertyName); 271 } 272 } catch (NumberFormatException e) { 273 Log.e(TAG, "Unable to parse propertyName: " + propertyValueString); 274 } 275 } 276 } 277 } else if (DEBUG) { 278 Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not" 279 + " supported"); 280 } 281 } 282 logConfigurations()283 private void logConfigurations() { 284 FrameworkStatsLog.write(FrameworkStatsLog.GNSS_CONFIGURATION_REPORTED, 285 getSuplHost(), 286 getSuplPort(0), 287 getC2KHost(), 288 getC2KPort(0), 289 getIntConfig(CONFIG_SUPL_VER, 0), 290 getSuplMode(0), 291 getSuplEs(0) == 1, 292 getIntConfig(CONFIG_LPP_PROFILE, 0), 293 getIntConfig(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 0), 294 getIntConfig(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 0) == 1, 295 getIntConfig(CONFIG_GPS_LOCK, 0), 296 getEsExtensionSec(), 297 mProperties.getProperty(CONFIG_NFW_PROXY_APPS)); 298 } 299 300 /** 301 * Loads GNSS properties from carrier config file. 302 */ loadPropertiesFromCarrierConfig()303 void loadPropertiesFromCarrierConfig() { 304 CarrierConfigManager configManager = (CarrierConfigManager) 305 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 306 if (configManager == null) { 307 return; 308 } 309 310 int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId(); 311 PersistableBundle configs = SubscriptionManager.isValidSubscriptionId(ddSubId) 312 ? configManager.getConfigForSubId(ddSubId) : configManager.getConfig(); 313 if (configs == null) { 314 if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config."); 315 configs = CarrierConfigManager.getDefaultConfig(); 316 } 317 for (String configKey : configs.keySet()) { 318 if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) { 319 String key = configKey 320 .substring(CarrierConfigManager.Gps.KEY_PREFIX.length()) 321 .toUpperCase(); 322 Object value = configs.get(configKey); 323 if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value); 324 if (value instanceof String) { 325 // Most GPS properties are of String type; convert so. 326 mProperties.setProperty(key, (String) value); 327 } else if (value != null) { 328 mProperties.setProperty(key, value.toString()); 329 } 330 } 331 } 332 } 333 loadPropertiesFromGpsDebugConfig(Properties properties)334 private void loadPropertiesFromGpsDebugConfig(Properties properties) { 335 try { 336 File file = new File(DEBUG_PROPERTIES_FILE); 337 FileInputStream stream = null; 338 try { 339 stream = new FileInputStream(file); 340 properties.load(stream); 341 } finally { 342 IoUtils.closeQuietly(stream); 343 } 344 } catch (IOException e) { 345 if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE); 346 } 347 } 348 getRangeCheckedConfigEsExtensionSec()349 private int getRangeCheckedConfigEsExtensionSec() { 350 int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0); 351 if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) { 352 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 353 + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS); 354 emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS; 355 } else if (emergencyExtensionSeconds < 0) { 356 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 357 + " is negative, reset to zero."); 358 emergencyExtensionSeconds = 0; 359 } 360 return emergencyExtensionSeconds; 361 } 362 getIntConfig(String configParameter, int defaultValue)363 private int getIntConfig(String configParameter, int defaultValue) { 364 String valueString = mProperties.getProperty(configParameter); 365 if (TextUtils.isEmpty(valueString)) { 366 return defaultValue; 367 } 368 try { 369 return Integer.decode(valueString); 370 } catch (NumberFormatException e) { 371 Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: " 372 + valueString + ". Using default value: " + defaultValue); 373 return defaultValue; 374 } 375 } 376 isConfigEsExtensionSecSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)377 private static boolean isConfigEsExtensionSecSupported( 378 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 379 // ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal 380 return gnssConfiguartionIfaceVersion.mMajor >= 2; 381 } 382 isConfigSuplEsSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)383 private static boolean isConfigSuplEsSupported( 384 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 385 // SUPL_ES is deprecated in @2.0::IGnssConfiguration.hal 386 return gnssConfiguartionIfaceVersion.mMajor < 2; 387 } 388 isConfigGpsLockSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)389 private static boolean isConfigGpsLockSupported( 390 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 391 // GPS_LOCK is deprecated in @2.0::IGnssConfiguration.hal 392 return gnssConfiguartionIfaceVersion.mMajor < 2; 393 } 394 native_get_gnss_configuration_version()395 private static native HalInterfaceVersion native_get_gnss_configuration_version(); 396 native_set_supl_version(int version)397 private static native boolean native_set_supl_version(int version); 398 native_set_supl_mode(int mode)399 private static native boolean native_set_supl_mode(int mode); 400 native_set_supl_es(int es)401 private static native boolean native_set_supl_es(int es); 402 native_set_lpp_profile(int lppProfile)403 private static native boolean native_set_lpp_profile(int lppProfile); 404 native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect)405 private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect); 406 native_set_gps_lock(int gpsLock)407 private static native boolean native_set_gps_lock(int gpsLock); 408 native_set_emergency_supl_pdn(int emergencySuplPdn)409 private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn); 410 native_set_satellite_blocklist(int[] constellations, int[] svIds)411 private static native boolean native_set_satellite_blocklist(int[] constellations, int[] svIds); 412 native_set_es_extension_sec(int emergencyExtensionSeconds)413 private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds); 414 } 415