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