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