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