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.ArrayList; 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 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 Properties mProperties; 97 98 private int mEsExtensionSec = 0; 99 100 private final Context mContext; 101 GnssConfiguration(Context context)102 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 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 defaulSuplEs)166 int getSuplEs(int defaulSuplEs) { 167 return getIntConfig(CONFIG_SUPL_ES, defaulSuplEs); 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 or 180 * {@Collections.EMPTY_LIST} if no value is provided. 181 */ getProxyApps()182 List<String> getProxyApps() { 183 // Space separated list of Android proxy app package names. 184 String proxyAppsStr = mProperties.getProperty(CONFIG_NFW_PROXY_APPS); 185 if (TextUtils.isEmpty(proxyAppsStr)) { 186 return Collections.EMPTY_LIST; 187 } 188 189 String[] proxyAppsArray = proxyAppsStr.trim().split("\\s+"); 190 if (proxyAppsArray.length == 0) { 191 return Collections.EMPTY_LIST; 192 } 193 194 ArrayList proxyApps = new ArrayList(proxyAppsArray.length); 195 for (String proxyApp : proxyAppsArray) { 196 proxyApps.add(proxyApp); 197 } 198 199 return proxyApps; 200 } 201 202 /** 203 * Updates the GNSS HAL satellite blacklist. 204 */ setSatelliteBlacklist(int[] constellations, int[] svids)205 void setSatelliteBlacklist(int[] constellations, int[] svids) { 206 native_set_satellite_blacklist(constellations, svids); 207 } 208 getHalInterfaceVersion()209 HalInterfaceVersion getHalInterfaceVersion() { 210 return native_get_gnss_configuration_version(); 211 } 212 213 interface SetCarrierProperty { set(int value)214 boolean set(int value); 215 } 216 217 /** 218 * Loads the GNSS properties from carrier config file followed by the properties from 219 * gps debug config file. 220 */ reloadGpsProperties()221 void reloadGpsProperties() { 222 if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + mProperties.size()); 223 loadPropertiesFromCarrierConfig(); 224 225 String lpp_prof = SystemProperties.get(LPP_PROFILE); 226 if (!TextUtils.isEmpty(lpp_prof)) { 227 // override default value of this if lpp_prof is not empty 228 mProperties.setProperty(CONFIG_LPP_PROFILE, lpp_prof); 229 } 230 /* 231 * Overlay carrier properties from a debug configuration file. 232 */ 233 loadPropertiesFromGpsDebugConfig(mProperties); 234 235 mEsExtensionSec = getRangeCheckedConfigEsExtensionSec(); 236 237 logConfigurations(); 238 239 final HalInterfaceVersion gnssConfigurationIfaceVersion = getHalInterfaceVersion(); 240 if (gnssConfigurationIfaceVersion != null) { 241 // Set to a range checked value. 242 if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion) 243 && !native_set_es_extension_sec(mEsExtensionSec)) { 244 Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec); 245 } 246 247 Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() { 248 { 249 put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version); 250 put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode); 251 252 if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) { 253 put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es); 254 } 255 256 put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile); 257 put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 258 GnssConfiguration::native_set_gnss_pos_protocol_select); 259 put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 260 GnssConfiguration::native_set_emergency_supl_pdn); 261 262 if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) { 263 put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock); 264 } 265 } 266 }; 267 268 for (Entry<String, SetCarrierProperty> entry : map.entrySet()) { 269 String propertyName = entry.getKey(); 270 String propertyValueString = mProperties.getProperty(propertyName); 271 if (propertyValueString != null) { 272 try { 273 int propertyValueInt = Integer.decode(propertyValueString); 274 boolean result = entry.getValue().set(propertyValueInt); 275 if (!result) { 276 Log.e(TAG, "Unable to set " + propertyName); 277 } 278 } catch (NumberFormatException e) { 279 Log.e(TAG, "Unable to parse propertyName: " + propertyValueString); 280 } 281 } 282 } 283 } else if (DEBUG) { 284 Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not" 285 + " supported"); 286 } 287 } 288 logConfigurations()289 private void logConfigurations() { 290 FrameworkStatsLog.write(FrameworkStatsLog.GNSS_CONFIGURATION_REPORTED, 291 getSuplHost(), 292 getSuplPort(0), 293 getC2KHost(), 294 getC2KPort(0), 295 getIntConfig(CONFIG_SUPL_VER, 0), 296 getSuplMode(0), 297 getSuplEs(0) == 1, 298 getIntConfig(CONFIG_LPP_PROFILE, 0), 299 getIntConfig(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 0), 300 getIntConfig(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 0) == 1, 301 getIntConfig(CONFIG_GPS_LOCK, 0), 302 getEsExtensionSec(), 303 mProperties.getProperty(CONFIG_NFW_PROXY_APPS)); 304 } 305 306 /** 307 * Loads GNSS properties from carrier config file. 308 */ loadPropertiesFromCarrierConfig()309 void loadPropertiesFromCarrierConfig() { 310 CarrierConfigManager configManager = (CarrierConfigManager) 311 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 312 if (configManager == null) { 313 return; 314 } 315 316 int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId(); 317 PersistableBundle configs = SubscriptionManager.isValidSubscriptionId(ddSubId) 318 ? configManager.getConfigForSubId(ddSubId) : null; 319 if (configs == null) { 320 if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config."); 321 configs = CarrierConfigManager.getDefaultConfig(); 322 } 323 for (String configKey : configs.keySet()) { 324 if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) { 325 String key = configKey 326 .substring(CarrierConfigManager.Gps.KEY_PREFIX.length()) 327 .toUpperCase(); 328 Object value = configs.get(configKey); 329 if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value); 330 if (value instanceof String) { 331 // Most GPS properties are of String type; convert so. 332 mProperties.setProperty(key, (String) value); 333 } else if (value != null) { 334 mProperties.setProperty(key, value.toString()); 335 } 336 } 337 } 338 } 339 loadPropertiesFromGpsDebugConfig(Properties properties)340 private void loadPropertiesFromGpsDebugConfig(Properties properties) { 341 try { 342 File file = new File(DEBUG_PROPERTIES_FILE); 343 FileInputStream stream = null; 344 try { 345 stream = new FileInputStream(file); 346 properties.load(stream); 347 } finally { 348 IoUtils.closeQuietly(stream); 349 } 350 } catch (IOException e) { 351 if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE); 352 } 353 } 354 getRangeCheckedConfigEsExtensionSec()355 private int getRangeCheckedConfigEsExtensionSec() { 356 int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0); 357 if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) { 358 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 359 + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS); 360 emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS; 361 } else if (emergencyExtensionSeconds < 0) { 362 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 363 + " is negative, reset to zero."); 364 emergencyExtensionSeconds = 0; 365 } 366 return emergencyExtensionSeconds; 367 } 368 getIntConfig(String configParameter, int defaultValue)369 private int getIntConfig(String configParameter, int defaultValue) { 370 String valueString = mProperties.getProperty(configParameter); 371 if (TextUtils.isEmpty(valueString)) { 372 return defaultValue; 373 } 374 try { 375 return Integer.decode(valueString); 376 } catch (NumberFormatException e) { 377 Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: " 378 + valueString + ". Using default value: " + defaultValue); 379 return defaultValue; 380 } 381 } 382 isConfigEsExtensionSecSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)383 private static boolean isConfigEsExtensionSecSupported( 384 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 385 // ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal 386 return gnssConfiguartionIfaceVersion.mMajor >= 2; 387 } 388 isConfigSuplEsSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)389 private static boolean isConfigSuplEsSupported( 390 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 391 // SUPL_ES is deprecated in @2.0::IGnssConfiguration.hal 392 return gnssConfiguartionIfaceVersion.mMajor < 2; 393 } 394 isConfigGpsLockSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)395 private static boolean isConfigGpsLockSupported( 396 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 397 // GPS_LOCK is deprecated in @2.0::IGnssConfiguration.hal 398 return gnssConfiguartionIfaceVersion.mMajor < 2; 399 } 400 native_get_gnss_configuration_version()401 private static native HalInterfaceVersion native_get_gnss_configuration_version(); 402 native_set_supl_version(int version)403 private static native boolean native_set_supl_version(int version); 404 native_set_supl_mode(int mode)405 private static native boolean native_set_supl_mode(int mode); 406 native_set_supl_es(int es)407 private static native boolean native_set_supl_es(int es); 408 native_set_lpp_profile(int lppProfile)409 private static native boolean native_set_lpp_profile(int lppProfile); 410 native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect)411 private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect); 412 native_set_gps_lock(int gpsLock)413 private static native boolean native_set_gps_lock(int gpsLock); 414 native_set_emergency_supl_pdn(int emergencySuplPdn)415 private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn); 416 native_set_satellite_blacklist(int[] constellations, int[] svIds)417 private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds); 418 native_set_es_extension_sec(int emergencyExtensionSeconds)419 private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds); 420 } 421