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.annotation.Nullable;
20 import android.net.TrafficStats;
21 import android.util.Log;
22 
23 import com.android.internal.util.TrafficStatsConstants;
24 
25 import java.io.ByteArrayOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.net.HttpURLConnection;
29 import java.net.URL;
30 import java.util.Properties;
31 import java.util.Random;
32 import java.util.concurrent.TimeUnit;
33 
34 /**
35  * A class for downloading GNSS PSDS data.
36  *
37  * {@hide}
38  */
39 class GnssPsdsDownloader {
40 
41     // how often to request PSDS download, in milliseconds
42     // current setting 24 hours
43     static final long PSDS_INTERVAL = 24 * 60 * 60 * 1000;
44 
45     private static final String TAG = "GnssPsdsDownloader";
46     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
47     private static final long MAXIMUM_CONTENT_LENGTH_BYTES = 1000000;  // 1MB.
48     private static final int CONNECTION_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(30);
49     private static final int READ_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(60);
50 
51     static final int LONG_TERM_PSDS_SERVER_INDEX = 1;
52     private static final int NORMAL_PSDS_SERVER_INDEX = 2;
53     private static final int REALTIME_PSDS_SERVER_INDEX = 3;
54     private static final int MAX_PSDS_TYPE_INDEX = 3;
55 
56     private final String[] mLongTermPsdsServers;
57     private final String[] mPsdsServers;
58     // to load balance our server requests
59     private int mNextServerIndex;
60 
GnssPsdsDownloader(Properties properties)61     GnssPsdsDownloader(Properties properties) {
62         // read PSDS servers from the Properties object
63         int count = 0;
64         String longTermPsdsServer1 = properties.getProperty(
65                 GnssConfiguration.CONFIG_LONGTERM_PSDS_SERVER_1);
66         String longTermPsdsServer2 = properties.getProperty(
67                 GnssConfiguration.CONFIG_LONGTERM_PSDS_SERVER_2);
68         String longTermPsdsServer3 = properties.getProperty(
69                 GnssConfiguration.CONFIG_LONGTERM_PSDS_SERVER_3);
70         if (longTermPsdsServer1 != null) count++;
71         if (longTermPsdsServer2 != null) count++;
72         if (longTermPsdsServer3 != null) count++;
73 
74         if (count == 0) {
75             Log.e(TAG, "No Long-Term PSDS servers were specified in the GnssConfiguration");
76             mLongTermPsdsServers = null;
77         } else {
78             mLongTermPsdsServers = new String[count];
79             count = 0;
80             if (longTermPsdsServer1 != null) mLongTermPsdsServers[count++] = longTermPsdsServer1;
81             if (longTermPsdsServer2 != null) mLongTermPsdsServers[count++] = longTermPsdsServer2;
82             if (longTermPsdsServer3 != null) mLongTermPsdsServers[count++] = longTermPsdsServer3;
83 
84             // randomize first server
85             Random random = new Random();
86             mNextServerIndex = random.nextInt(count);
87         }
88 
89         String normalPsdsServer = properties.getProperty(
90                 GnssConfiguration.CONFIG_NORMAL_PSDS_SERVER);
91         String realtimePsdsServer = properties.getProperty(
92                 GnssConfiguration.CONFIG_REALTIME_PSDS_SERVER);
93         mPsdsServers = new String[MAX_PSDS_TYPE_INDEX + 1];
94         mPsdsServers[NORMAL_PSDS_SERVER_INDEX] = normalPsdsServer;
95         mPsdsServers[REALTIME_PSDS_SERVER_INDEX] = realtimePsdsServer;
96     }
97 
98     @Nullable
downloadPsdsData(int psdsType)99     byte[] downloadPsdsData(int psdsType) {
100         byte[] result = null;
101         int startIndex = mNextServerIndex;
102 
103         if (psdsType == LONG_TERM_PSDS_SERVER_INDEX && mLongTermPsdsServers == null) {
104             return null;
105         } else if (psdsType > LONG_TERM_PSDS_SERVER_INDEX && psdsType <= MAX_PSDS_TYPE_INDEX
106                 && mPsdsServers[psdsType] == null) {
107             return null;
108         }
109 
110         if (psdsType == LONG_TERM_PSDS_SERVER_INDEX) {
111             // load balance our requests among the available servers
112             while (result == null) {
113                 result = doDownloadWithTrafficAccounted(mLongTermPsdsServers[mNextServerIndex]);
114 
115                 // increment mNextServerIndex and wrap around if necessary
116                 mNextServerIndex++;
117                 if (mNextServerIndex == mLongTermPsdsServers.length) {
118                     mNextServerIndex = 0;
119                 }
120                 // break if we have tried all the servers
121                 if (mNextServerIndex == startIndex) break;
122             }
123         } else if (psdsType > LONG_TERM_PSDS_SERVER_INDEX && psdsType <= MAX_PSDS_TYPE_INDEX) {
124             result = doDownloadWithTrafficAccounted(mPsdsServers[psdsType]);
125         }
126 
127         return result;
128     }
129 
130     @Nullable
doDownloadWithTrafficAccounted(String url)131     private byte[] doDownloadWithTrafficAccounted(String url) {
132         byte[] result;
133         final int oldTag = TrafficStats.getAndSetThreadStatsTag(
134                 TrafficStatsConstants.TAG_SYSTEM_GPS);
135         try {
136             result = doDownload(url);
137         } finally {
138             TrafficStats.setThreadStatsTag(oldTag);
139         }
140         return result;
141     }
142 
143     @Nullable
doDownload(String url)144     private byte[] doDownload(String url) {
145         if (DEBUG) Log.d(TAG, "Downloading PSDS data from " + url);
146 
147         HttpURLConnection connection = null;
148         try {
149             connection = (HttpURLConnection) (new URL(url)).openConnection();
150             connection.setRequestProperty(
151                     "Accept",
152                     "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
153             connection.setRequestProperty(
154                     "x-wap-profile",
155                     "http://www.openmobilealliance.org/tech/profiles/UAPROF/ccppschema-20021212#");
156             connection.setConnectTimeout(CONNECTION_TIMEOUT_MS);
157             connection.setReadTimeout(READ_TIMEOUT_MS);
158 
159             connection.connect();
160             int statusCode = connection.getResponseCode();
161             if (statusCode != HttpURLConnection.HTTP_OK) {
162                 if (DEBUG) Log.d(TAG, "HTTP error downloading gnss PSDS: " + statusCode);
163                 return null;
164             }
165 
166             try (InputStream in = connection.getInputStream()) {
167                 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
168                 byte[] buffer = new byte[1024];
169                 int count;
170                 while ((count = in.read(buffer)) != -1) {
171                     bytes.write(buffer, 0, count);
172                     if (bytes.size() > MAXIMUM_CONTENT_LENGTH_BYTES) {
173                         if (DEBUG) Log.d(TAG, "PSDS file too large");
174                         return null;
175                     }
176                 }
177                 return bytes.toByteArray();
178             }
179         } catch (IOException ioe) {
180             if (DEBUG) Log.d(TAG, "Error downloading gnss PSDS: ", ioe);
181         } finally {
182             if (connection != null) {
183                 connection.disconnect();
184             }
185         }
186         return null;
187     }
188 }
189