blob: fc0d67e02db56609aba8e101e7661097f21f729c [file] [log] [blame]
Ye Wenbdb6fe12014-10-30 10:49:05 -07001/*
2 * Copyright (C) 2014 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
17package com.android.mms.service;
18
Ye Wenbdb6fe12014-10-30 10:49:05 -070019import android.content.Context;
Ye Weneeb364a2016-05-20 15:58:50 -070020import android.net.ConnectivityManager;
21import android.net.LinkProperties;
Ye Wen7a284e52015-01-26 15:17:24 -080022import android.net.Network;
Remi NGUYEN VAN2a83a312018-04-25 17:53:28 +090023import android.net.dns.ResolvUtil;
Jonathan Basseri44670072015-06-09 11:46:26 -070024import android.os.Bundle;
Ye Wen44108ec2016-07-18 14:37:34 -070025import android.telephony.CarrierConfigManager;
Jonathan Basseri44670072015-06-09 11:46:26 -070026import android.telephony.SmsManager;
27import android.telephony.SubscriptionManager;
28import android.telephony.TelephonyManager;
Ye Wenbdb6fe12014-10-30 10:49:05 -070029import android.text.TextUtils;
Jonathan Basseri44670072015-06-09 11:46:26 -070030import android.util.Base64;
Ye Wenbdb6fe12014-10-30 10:49:05 -070031import android.util.Log;
Ye Wenbdb6fe12014-10-30 10:49:05 -070032import com.android.mms.service.exception.MmsHttpException;
Ye Wenbdb6fe12014-10-30 10:49:05 -070033
34import java.io.BufferedInputStream;
35import java.io.BufferedOutputStream;
36import java.io.ByteArrayOutputStream;
37import java.io.IOException;
38import java.io.InputStream;
39import java.io.OutputStream;
Jonathan Basseri44670072015-06-09 11:46:26 -070040import java.io.UnsupportedEncodingException;
Ye Wenbdb6fe12014-10-30 10:49:05 -070041import java.net.HttpURLConnection;
Ye Weneeb364a2016-05-20 15:58:50 -070042import java.net.Inet4Address;
43import java.net.InetAddress;
Ye Wenbdb6fe12014-10-30 10:49:05 -070044import java.net.InetSocketAddress;
45import java.net.MalformedURLException;
46import java.net.ProtocolException;
47import java.net.Proxy;
48import java.net.URL;
49import java.util.List;
50import java.util.Locale;
51import java.util.Map;
52import java.util.regex.Matcher;
53import java.util.regex.Pattern;
54
55/**
56 * MMS HTTP client for sending and downloading MMS messages
57 */
58public class MmsHttpClient {
59 public static final String METHOD_POST = "POST";
60 public static final String METHOD_GET = "GET";
61
62 private static final String HEADER_CONTENT_TYPE = "Content-Type";
63 private static final String HEADER_ACCEPT = "Accept";
64 private static final String HEADER_ACCEPT_LANGUAGE = "Accept-Language";
65 private static final String HEADER_USER_AGENT = "User-Agent";
Ye Wen44108ec2016-07-18 14:37:34 -070066 private static final String HEADER_CONNECTION = "Connection";
Ye Wenbdb6fe12014-10-30 10:49:05 -070067
68 // The "Accept" header value
69 private static final String HEADER_VALUE_ACCEPT =
70 "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic";
71 // The "Content-Type" header value
Ye Wen857fb762014-12-15 14:44:22 -080072 private static final String HEADER_VALUE_CONTENT_TYPE_WITH_CHARSET =
Ye Wenbdb6fe12014-10-30 10:49:05 -070073 "application/vnd.wap.mms-message; charset=utf-8";
Ye Wen857fb762014-12-15 14:44:22 -080074 private static final String HEADER_VALUE_CONTENT_TYPE_WITHOUT_CHARSET =
75 "application/vnd.wap.mms-message";
Ye Wen44108ec2016-07-18 14:37:34 -070076 private static final String HEADER_CONNECTION_CLOSE = "close";
Ye Wenbdb6fe12014-10-30 10:49:05 -070077
Ye Weneeb364a2016-05-20 15:58:50 -070078 private static final int IPV4_WAIT_ATTEMPTS = 15;
79 private static final long IPV4_WAIT_DELAY_MS = 1000; // 1 seconds
80
Ye Wenbdb6fe12014-10-30 10:49:05 -070081 private final Context mContext;
Neil Fuller6b2ef3b2015-01-20 15:39:29 +000082 private final Network mNetwork;
Ye Weneeb364a2016-05-20 15:58:50 -070083 private final ConnectivityManager mConnectivityManager;
Ye Wenbdb6fe12014-10-30 10:49:05 -070084
85 /**
86 * Constructor
Ye Weneeb364a2016-05-20 15:58:50 -070087 * @param context The Context object
Neil Fuller6b2ef3b2015-01-20 15:39:29 +000088 * @param network The Network for creating an OKHttp client
Ye Weneeb364a2016-05-20 15:58:50 -070089 * @param connectivityManager
Ye Wenbdb6fe12014-10-30 10:49:05 -070090 */
Ye Weneeb364a2016-05-20 15:58:50 -070091 public MmsHttpClient(Context context, Network network,
92 ConnectivityManager connectivityManager) {
Ye Wenbdb6fe12014-10-30 10:49:05 -070093 mContext = context;
Remi NGUYEN VAN2a83a312018-04-25 17:53:28 +090094 // Mms server is on a carrier private network so it may not be resolvable using 3rd party
95 // private dns
96 mNetwork = ResolvUtil.makeNetworkWithPrivateDnsBypass(network);
Ye Weneeb364a2016-05-20 15:58:50 -070097 mConnectivityManager = connectivityManager;
Ye Wenbdb6fe12014-10-30 10:49:05 -070098 }
99
100 /**
101 * Execute an MMS HTTP request, either a POST (sending) or a GET (downloading)
102 *
103 * @param urlString The request URL, for sending it is usually the MMSC, and for downloading
104 * it is the message URL
105 * @param pdu For POST (sending) only, the PDU to send
106 * @param method HTTP method, POST for sending and GET for downloading
107 * @param isProxySet Is there a proxy for the MMSC
108 * @param proxyHost The proxy host
109 * @param proxyPort The proxy port
110 * @param mmsConfig The MMS config to use
Jonathan Basseri44670072015-06-09 11:46:26 -0700111 * @param subId The subscription ID used to get line number, etc.
Ye Wen8153aed2015-06-16 13:36:31 -0700112 * @param requestId The request ID for logging
Ye Wenbdb6fe12014-10-30 10:49:05 -0700113 * @return The HTTP response body
114 * @throws MmsHttpException For any failures
115 */
116 public byte[] execute(String urlString, byte[] pdu, String method, boolean isProxySet,
Ye Wen8153aed2015-06-16 13:36:31 -0700117 String proxyHost, int proxyPort, Bundle mmsConfig, int subId, String requestId)
Ye Wenbdb6fe12014-10-30 10:49:05 -0700118 throws MmsHttpException {
Ye Wen8153aed2015-06-16 13:36:31 -0700119 LogUtil.d(requestId, "HTTP: " + method + " " + redactUrlForNonVerbose(urlString)
Ye Wenbdb6fe12014-10-30 10:49:05 -0700120 + (isProxySet ? (", proxy=" + proxyHost + ":" + proxyPort) : "")
121 + ", PDU size=" + (pdu != null ? pdu.length : 0));
122 checkMethod(method);
123 HttpURLConnection connection = null;
124 try {
Ye Wen960d2d42015-02-27 13:10:31 -0800125 Proxy proxy = Proxy.NO_PROXY;
Ye Wenbdb6fe12014-10-30 10:49:05 -0700126 if (isProxySet) {
Ye Wenc2348f82015-09-17 11:28:31 -0700127 proxy = new Proxy(Proxy.Type.HTTP,
128 new InetSocketAddress(mNetwork.getByName(proxyHost), proxyPort));
Ye Wenbdb6fe12014-10-30 10:49:05 -0700129 }
130 final URL url = new URL(urlString);
Ye Weneeb364a2016-05-20 15:58:50 -0700131 maybeWaitForIpv4(requestId, url);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700132 // Now get the connection
Ye Wen7a284e52015-01-26 15:17:24 -0800133 connection = (HttpURLConnection) mNetwork.openConnection(url, proxy);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700134 connection.setDoInput(true);
Jonathan Basseri44670072015-06-09 11:46:26 -0700135 connection.setConnectTimeout(
136 mmsConfig.getInt(SmsManager.MMS_CONFIG_HTTP_SOCKET_TIMEOUT));
Ye Wenbdb6fe12014-10-30 10:49:05 -0700137 // ------- COMMON HEADERS ---------
138 // Header: Accept
139 connection.setRequestProperty(HEADER_ACCEPT, HEADER_VALUE_ACCEPT);
140 // Header: Accept-Language
141 connection.setRequestProperty(
142 HEADER_ACCEPT_LANGUAGE, getCurrentAcceptLanguage(Locale.getDefault()));
143 // Header: User-Agent
Jonathan Basseri44670072015-06-09 11:46:26 -0700144 final String userAgent = mmsConfig.getString(SmsManager.MMS_CONFIG_USER_AGENT);
Ye Wen8153aed2015-06-16 13:36:31 -0700145 LogUtil.i(requestId, "HTTP: User-Agent=" + userAgent);
Ye Wenf3debb12014-11-26 11:12:13 -0800146 connection.setRequestProperty(HEADER_USER_AGENT, userAgent);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700147 // Header: x-wap-profile
Jonathan Basseri44670072015-06-09 11:46:26 -0700148 final String uaProfUrlTagName =
149 mmsConfig.getString(SmsManager.MMS_CONFIG_UA_PROF_TAG_NAME);
150 final String uaProfUrl = mmsConfig.getString(SmsManager.MMS_CONFIG_UA_PROF_URL);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700151 if (uaProfUrl != null) {
Ye Wen8153aed2015-06-16 13:36:31 -0700152 LogUtil.i(requestId, "HTTP: UaProfUrl=" + uaProfUrl);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700153 connection.setRequestProperty(uaProfUrlTagName, uaProfUrl);
154 }
Ye Wen44108ec2016-07-18 14:37:34 -0700155 // Header: Connection: close (if needed)
156 // Some carriers require that the HTTP connection's socket is closed
157 // after an MMS request/response is complete. In these cases keep alive
158 // is disabled. See https://tools.ietf.org/html/rfc7230#section-6.6
159 if (mmsConfig.getBoolean(SmsManager.MMS_CONFIG_CLOSE_CONNECTION, false)) {
160 LogUtil.i(requestId, "HTTP: Connection close after request");
161 connection.setRequestProperty(HEADER_CONNECTION, HEADER_CONNECTION_CLOSE);
162 }
Ye Wenbdb6fe12014-10-30 10:49:05 -0700163 // Add extra headers specified by mms_config.xml's httpparams
Jonathan Basseri44670072015-06-09 11:46:26 -0700164 addExtraHeaders(connection, mmsConfig, subId);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700165 // Different stuff for GET and POST
166 if (METHOD_POST.equals(method)) {
167 if (pdu == null || pdu.length < 1) {
Ye Wen8153aed2015-06-16 13:36:31 -0700168 LogUtil.e(requestId, "HTTP: empty pdu");
Ye Wenbdb6fe12014-10-30 10:49:05 -0700169 throw new MmsHttpException(0/*statusCode*/, "Sending empty PDU");
170 }
171 connection.setDoOutput(true);
172 connection.setRequestMethod(METHOD_POST);
Jonathan Basseri44670072015-06-09 11:46:26 -0700173 if (mmsConfig.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER)) {
Ye Wen857fb762014-12-15 14:44:22 -0800174 connection.setRequestProperty(HEADER_CONTENT_TYPE,
175 HEADER_VALUE_CONTENT_TYPE_WITH_CHARSET);
176 } else {
177 connection.setRequestProperty(HEADER_CONTENT_TYPE,
178 HEADER_VALUE_CONTENT_TYPE_WITHOUT_CHARSET);
179 }
Ye Wen8153aed2015-06-16 13:36:31 -0700180 if (LogUtil.isLoggable(Log.VERBOSE)) {
181 logHttpHeaders(connection.getRequestProperties(), requestId);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700182 }
183 connection.setFixedLengthStreamingMode(pdu.length);
184 // Sending request body
185 final OutputStream out =
186 new BufferedOutputStream(connection.getOutputStream());
187 out.write(pdu);
188 out.flush();
189 out.close();
190 } else if (METHOD_GET.equals(method)) {
Ye Wen8153aed2015-06-16 13:36:31 -0700191 if (LogUtil.isLoggable(Log.VERBOSE)) {
192 logHttpHeaders(connection.getRequestProperties(), requestId);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700193 }
194 connection.setRequestMethod(METHOD_GET);
195 }
196 // Get response
Ye Wenf3debb12014-11-26 11:12:13 -0800197 final int responseCode = connection.getResponseCode();
198 final String responseMessage = connection.getResponseMessage();
Ye Wen8153aed2015-06-16 13:36:31 -0700199 LogUtil.d(requestId, "HTTP: " + responseCode + " " + responseMessage);
200 if (LogUtil.isLoggable(Log.VERBOSE)) {
201 logHttpHeaders(connection.getHeaderFields(), requestId);
Ye Wenf3debb12014-11-26 11:12:13 -0800202 }
203 if (responseCode / 100 != 2) {
204 throw new MmsHttpException(responseCode, responseMessage);
205 }
Ye Wenbdb6fe12014-10-30 10:49:05 -0700206 final InputStream in = new BufferedInputStream(connection.getInputStream());
207 final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
208 final byte[] buf = new byte[4096];
209 int count = 0;
210 while ((count = in.read(buf)) > 0) {
211 byteOut.write(buf, 0, count);
212 }
213 in.close();
214 final byte[] responseBody = byteOut.toByteArray();
Ye Wen8153aed2015-06-16 13:36:31 -0700215 LogUtil.d(requestId, "HTTP: response size="
Ye Wenbdb6fe12014-10-30 10:49:05 -0700216 + (responseBody != null ? responseBody.length : 0));
217 return responseBody;
218 } catch (MalformedURLException e) {
Ye Wen189322a2015-02-19 16:08:16 -0800219 final String redactedUrl = redactUrlForNonVerbose(urlString);
Ye Wen8153aed2015-06-16 13:36:31 -0700220 LogUtil.e(requestId, "HTTP: invalid URL " + redactedUrl, e);
Ye Wen189322a2015-02-19 16:08:16 -0800221 throw new MmsHttpException(0/*statusCode*/, "Invalid URL " + redactedUrl, e);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700222 } catch (ProtocolException e) {
Ye Wen189322a2015-02-19 16:08:16 -0800223 final String redactedUrl = redactUrlForNonVerbose(urlString);
Ye Wen8153aed2015-06-16 13:36:31 -0700224 LogUtil.e(requestId, "HTTP: invalid URL protocol " + redactedUrl, e);
Ye Wen189322a2015-02-19 16:08:16 -0800225 throw new MmsHttpException(0/*statusCode*/, "Invalid URL protocol " + redactedUrl, e);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700226 } catch (IOException e) {
Ye Wen8153aed2015-06-16 13:36:31 -0700227 LogUtil.e(requestId, "HTTP: IO failure", e);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700228 throw new MmsHttpException(0/*statusCode*/, e);
229 } finally {
230 if (connection != null) {
231 connection.disconnect();
232 }
233 }
234 }
235
Ye Weneeb364a2016-05-20 15:58:50 -0700236 private void maybeWaitForIpv4(final String requestId, final URL url) {
237 // If it's a literal IPv4 address and we're on an IPv6-only network,
238 // wait until IPv4 is available.
239 Inet4Address ipv4Literal = null;
240 try {
241 ipv4Literal = (Inet4Address) InetAddress.parseNumericAddress(url.getHost());
242 } catch (IllegalArgumentException | ClassCastException e) {
243 // Ignore
244 }
245 if (ipv4Literal == null) {
246 // Not an IPv4 address.
247 return;
248 }
249 for (int i = 0; i < IPV4_WAIT_ATTEMPTS; i++) {
250 final LinkProperties lp = mConnectivityManager.getLinkProperties(mNetwork);
251 if (lp != null) {
252 if (!lp.isReachable(ipv4Literal)) {
253 LogUtil.w(requestId, "HTTP: IPv4 not yet provisioned");
254 try {
255 Thread.sleep(IPV4_WAIT_DELAY_MS);
256 } catch (InterruptedException e) {
257 // Ignore
258 }
259 } else {
260 LogUtil.i(requestId, "HTTP: IPv4 provisioned");
261 break;
262 }
263 } else {
264 LogUtil.w(requestId, "HTTP: network disconnected, skip ipv4 check");
265 break;
266 }
267 }
268 }
269
Ye Wen8153aed2015-06-16 13:36:31 -0700270 private static void logHttpHeaders(Map<String, List<String>> headers, String requestId) {
Ye Wenbdb6fe12014-10-30 10:49:05 -0700271 final StringBuilder sb = new StringBuilder();
272 if (headers != null) {
273 for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
274 final String key = entry.getKey();
275 final List<String> values = entry.getValue();
276 if (values != null) {
277 for (String value : values) {
278 sb.append(key).append('=').append(value).append('\n');
279 }
280 }
281 }
Ye Wen8153aed2015-06-16 13:36:31 -0700282 LogUtil.v(requestId, "HTTP: headers\n" + sb.toString());
Ye Wenbdb6fe12014-10-30 10:49:05 -0700283 }
284 }
285
286 private static void checkMethod(String method) throws MmsHttpException {
287 if (!METHOD_GET.equals(method) && !METHOD_POST.equals(method)) {
288 throw new MmsHttpException(0/*statusCode*/, "Invalid method " + method);
289 }
290 }
291
292 private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US";
293
294 /**
295 * Return the Accept-Language header. Use the current locale plus
296 * US if we are in a different locale than US.
297 * This code copied from the browser's WebSettings.java
298 *
299 * @return Current AcceptLanguage String.
300 */
301 public static String getCurrentAcceptLanguage(Locale locale) {
302 final StringBuilder buffer = new StringBuilder();
303 addLocaleToHttpAcceptLanguage(buffer, locale);
304
305 if (!Locale.US.equals(locale)) {
306 if (buffer.length() > 0) {
307 buffer.append(", ");
308 }
309 buffer.append(ACCEPT_LANG_FOR_US_LOCALE);
310 }
311
312 return buffer.toString();
313 }
314
315 /**
316 * Convert obsolete language codes, including Hebrew/Indonesian/Yiddish,
317 * to new standard.
318 */
319 private static String convertObsoleteLanguageCodeToNew(String langCode) {
320 if (langCode == null) {
321 return null;
322 }
323 if ("iw".equals(langCode)) {
324 // Hebrew
325 return "he";
326 } else if ("in".equals(langCode)) {
327 // Indonesian
328 return "id";
329 } else if ("ji".equals(langCode)) {
330 // Yiddish
331 return "yi";
332 }
333 return langCode;
334 }
335
336 private static void addLocaleToHttpAcceptLanguage(StringBuilder builder, Locale locale) {
337 final String language = convertObsoleteLanguageCodeToNew(locale.getLanguage());
338 if (language != null) {
339 builder.append(language);
340 final String country = locale.getCountry();
341 if (country != null) {
342 builder.append("-");
343 builder.append(country);
344 }
345 }
346 }
347
Jonathan Basseri44670072015-06-09 11:46:26 -0700348 /**
349 * Add extra HTTP headers from mms_config.xml's httpParams, which is a list of key/value
350 * pairs separated by "|". Each key/value pair is separated by ":". Value may contain
351 * macros like "##LINE1##" or "##NAI##" which is resolved with methods in this class
352 *
353 * @param connection The HttpURLConnection that we add headers to
354 * @param mmsConfig The MmsConfig object
355 * @param subId The subscription ID used to get line number, etc.
356 */
357 private void addExtraHeaders(HttpURLConnection connection, Bundle mmsConfig, int subId) {
358 final String extraHttpParams = mmsConfig.getString(SmsManager.MMS_CONFIG_HTTP_PARAMS);
359 if (!TextUtils.isEmpty(extraHttpParams)) {
360 // Parse the parameter list
361 String paramList[] = extraHttpParams.split("\\|");
362 for (String paramPair : paramList) {
363 String splitPair[] = paramPair.split(":", 2);
364 if (splitPair.length == 2) {
365 final String name = splitPair[0].trim();
366 final String value =
367 resolveMacro(mContext, splitPair[1].trim(), mmsConfig, subId);
368 if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {
369 // Add the header if the param is valid
370 connection.setRequestProperty(name, value);
371 }
372 }
373 }
374 }
375 }
376
Ye Wenbdb6fe12014-10-30 10:49:05 -0700377 private static final Pattern MACRO_P = Pattern.compile("##(\\S+)##");
378 /**
379 * Resolve the macro in HTTP param value text
380 * For example, "something##LINE1##something" is resolved to "something9139531419something"
381 *
382 * @param value The HTTP param value possibly containing macros
Jonathan Basseri44670072015-06-09 11:46:26 -0700383 * @param subId The subscription ID used to get line number, etc.
384 * @return The HTTP param with macros resolved to real value
Ye Wenbdb6fe12014-10-30 10:49:05 -0700385 */
Jonathan Basseri44670072015-06-09 11:46:26 -0700386 private static String resolveMacro(Context context, String value, Bundle mmsConfig, int subId) {
Ye Wenbdb6fe12014-10-30 10:49:05 -0700387 if (TextUtils.isEmpty(value)) {
388 return value;
389 }
390 final Matcher matcher = MACRO_P.matcher(value);
391 int nextStart = 0;
392 StringBuilder replaced = null;
393 while (matcher.find()) {
394 if (replaced == null) {
395 replaced = new StringBuilder();
396 }
397 final int matchedStart = matcher.start();
398 if (matchedStart > nextStart) {
399 replaced.append(value.substring(nextStart, matchedStart));
400 }
401 final String macro = matcher.group(1);
Jonathan Basseri44670072015-06-09 11:46:26 -0700402 final String macroValue = getMacroValue(context, macro, mmsConfig, subId);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700403 if (macroValue != null) {
404 replaced.append(macroValue);
Ye Wenbdb6fe12014-10-30 10:49:05 -0700405 }
406 nextStart = matcher.end();
407 }
408 if (replaced != null && nextStart < value.length()) {
409 replaced.append(value.substring(nextStart));
410 }
411 return replaced == null ? value : replaced.toString();
412 }
413
414 /**
Ye Wen189322a2015-02-19 16:08:16 -0800415 * Redact the URL for non-VERBOSE logging. Replace url with only the host part and the length
416 * of the input URL string.
417 *
418 * @param urlString
419 * @return
420 */
421 public static String redactUrlForNonVerbose(String urlString) {
Ye Wen8153aed2015-06-16 13:36:31 -0700422 if (LogUtil.isLoggable(Log.VERBOSE)) {
Ye Wen189322a2015-02-19 16:08:16 -0800423 // Don't redact for VERBOSE level logging
424 return urlString;
425 }
426 if (TextUtils.isEmpty(urlString)) {
427 return urlString;
428 }
429 String protocol = "http";
430 String host = "";
431 try {
432 final URL url = new URL(urlString);
433 protocol = url.getProtocol();
434 host = url.getHost();
435 } catch (MalformedURLException e) {
436 // Ignore
437 }
438 // Print "http://host[length]"
439 final StringBuilder sb = new StringBuilder();
440 sb.append(protocol).append("://").append(host)
441 .append("[").append(urlString.length()).append("]");
442 return sb.toString();
443 }
Jonathan Basseri44670072015-06-09 11:46:26 -0700444
445 /*
446 * Macro names
447 */
448 // The raw phone number from TelephonyManager.getLine1Number
449 private static final String MACRO_LINE1 = "LINE1";
450 // The phone number without country code
451 private static final String MACRO_LINE1NOCOUNTRYCODE = "LINE1NOCOUNTRYCODE";
452 // NAI (Network Access Identifier), used by Sprint for authentication
453 private static final String MACRO_NAI = "NAI";
454 /**
455 * Return the HTTP param macro value.
456 * Example: "LINE1" returns the phone number, etc.
457 *
458 * @param macro The macro name
Hotice Hsu1f4477d2019-01-07 13:31:30 +0800459 * @param mmsConfig The MMS config which contains NAI suffix and SIM country ISO to override.
Jonathan Basseri44670072015-06-09 11:46:26 -0700460 * @param subId The subscription ID used to get line number, etc.
461 * @return The value of the defined macro
462 */
463 private static String getMacroValue(Context context, String macro, Bundle mmsConfig,
464 int subId) {
465 if (MACRO_LINE1.equals(macro)) {
466 return getLine1(context, subId);
467 } else if (MACRO_LINE1NOCOUNTRYCODE.equals(macro)) {
Hotice Hsu1f4477d2019-01-07 13:31:30 +0800468 return getLine1NoCountryCode(context, mmsConfig, subId);
Jonathan Basseri44670072015-06-09 11:46:26 -0700469 } else if (MACRO_NAI.equals(macro)) {
470 return getNai(context, mmsConfig, subId);
471 }
Ye Wen8153aed2015-06-16 13:36:31 -0700472 LogUtil.e("Invalid macro " + macro);
Jonathan Basseri44670072015-06-09 11:46:26 -0700473 return null;
474 }
475
476 /**
477 * Returns the phone number for the given subscription ID.
478 */
479 private static String getLine1(Context context, int subId) {
480 final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
481 Context.TELEPHONY_SERVICE);
Shishir Agrawal033ab5c2016-01-25 14:07:09 -0800482 return telephonyManager.getLine1Number(subId);
Jonathan Basseri44670072015-06-09 11:46:26 -0700483 }
484
485 /**
486 * Returns the phone number (without country code) for the given subscription ID.
487 */
Hotice Hsu1f4477d2019-01-07 13:31:30 +0800488 private static String getLine1NoCountryCode(Context context, Bundle mmsConfig, int subId) {
Jonathan Basseri44670072015-06-09 11:46:26 -0700489 final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
490 Context.TELEPHONY_SERVICE);
Hotice Hsu1f4477d2019-01-07 13:31:30 +0800491 String countryIsoOverride =
492 mmsConfig.getString(SmsManager.MMS_CONFIG_SIM_COUNTRY_ISO_OVERRIDE);
Jonathan Basseri44670072015-06-09 11:46:26 -0700493 return PhoneUtils.getNationalNumber(
Hotice Hsu1f4477d2019-01-07 13:31:30 +0800494 telephonyManager,
495 subId,
496 telephonyManager.getLine1Number(subId),
497 countryIsoOverride);
Jonathan Basseri44670072015-06-09 11:46:26 -0700498 }
499
500 /**
501 * Returns the NAI (Network Access Identifier) from SystemProperties for the given subscription
502 * ID.
503 */
504 private static String getNai(Context context, Bundle mmsConfig, int subId) {
505 final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
506 Context.TELEPHONY_SERVICE);
Sanket Padawe5ce5c2e2017-03-20 15:07:06 -0700507 String nai = telephonyManager.getNai(SubscriptionManager.getSlotIndex(subId));
Ye Wen8153aed2015-06-16 13:36:31 -0700508 if (LogUtil.isLoggable(Log.VERBOSE)) {
509 LogUtil.v("getNai: nai=" + nai);
Jonathan Basseri44670072015-06-09 11:46:26 -0700510 }
511
512 if (!TextUtils.isEmpty(nai)) {
513 String naiSuffix = mmsConfig.getString(SmsManager.MMS_CONFIG_NAI_SUFFIX);
514 if (!TextUtils.isEmpty(naiSuffix)) {
515 nai = nai + naiSuffix;
516 }
517 byte[] encoded = null;
518 try {
519 encoded = Base64.encode(nai.getBytes("UTF-8"), Base64.NO_WRAP);
520 } catch (UnsupportedEncodingException e) {
521 encoded = Base64.encode(nai.getBytes(), Base64.NO_WRAP);
522 }
523 try {
524 nai = new String(encoded, "UTF-8");
525 } catch (UnsupportedEncodingException e) {
526 nai = new String(encoded);
527 }
528 }
529 return nai;
530 }
Ye Wenbdb6fe12014-10-30 10:49:05 -0700531}