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