1d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian/* 2d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Copyright (C) 2016 The Android Open Source Project 3d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 4d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License"); 5d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * you may not use this file except in compliance with the License. 6d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * You may obtain a copy of the License at 7d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 8d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * http://www.apache.org/licenses/LICENSE-2.0 9d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 10d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Unless required by applicable law or agreed to in writing, software 11d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS, 12d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * See the License for the specific language governing permissions and 14d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * limitations under the License 15d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 16d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 17d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianpackage com.android.voicemail.impl.protocol; 18d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 19d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.annotation.TargetApi; 2010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.content.Context; 21d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.net.Network; 22d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.os.Build; 23d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.os.Build.VERSION_CODES; 24d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.os.Bundle; 2510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.support.annotation.NonNull; 2610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.support.annotation.VisibleForTesting; 27d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.support.annotation.WorkerThread; 28d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.telecom.PhoneAccountHandle; 29d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.telephony.TelephonyManager; 30d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.text.Html; 31d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.text.Spanned; 32d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.text.style.URLSpan; 33d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.util.ArrayMap; 342f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanianimport com.android.dialer.configprovider.ConfigProviderBindings; 35d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.ActivationTask; 36d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.Assert; 37d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.OmtpEvents; 38d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.OmtpVvmCarrierConfigHelper; 39d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.VoicemailStatus; 40d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.VvmLog; 41d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.sync.VvmNetworkRequest; 42d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.sync.VvmNetworkRequest.NetworkWrapper; 43d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.voicemail.impl.sync.VvmNetworkRequest.RequestFailedException; 44d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.volley.AuthFailureError; 45d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.volley.Request; 46d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.volley.RequestQueue; 47d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.volley.toolbox.HurlStack; 48d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.volley.toolbox.RequestFuture; 49d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.volley.toolbox.StringRequest; 50d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.volley.toolbox.Volley; 51d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.IOException; 52d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.net.CookieHandler; 53d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.net.CookieManager; 54d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.net.HttpURLConnection; 55d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.net.URL; 5610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport java.util.ArrayList; 5710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport java.util.List; 58d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.Locale; 59d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.Map; 60d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.Random; 61d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.concurrent.ExecutionException; 62d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.concurrent.TimeUnit; 63d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.concurrent.TimeoutException; 64d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.regex.Matcher; 65d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.regex.Pattern; 6610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport org.json.JSONArray; 6710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport org.json.JSONException; 68d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 69d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian/** 70d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Class to subscribe to basic VVM3 visual voicemail, for example, Verizon. Subscription is required 71d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * when the user is unprovisioned. This could happen when the user is on a legacy service, or 72d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * switched over from devices that used other type of visual voicemail. 73d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 74d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * <p>The STATUS SMS will come with a URL to the voicemail management gateway. From it we can find 75d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * the self provisioning gateway URL that we can modify voicemail services. 76d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 77d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * <p>A request to the self provisioning gateway to activate basic visual voicemail will return us 78d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * with a web page. If the user hasn't subscribe to it yet it will contain a link to confirm the 79d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * subscription. This link should be clicked through cellular network, and have cookies enabled. 80d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 81d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * <p>After the process is completed, the carrier should send us another STATUS SMS with a new or 82d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * ready user. 83d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 84d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian@TargetApi(VERSION_CODES.O) 85d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianpublic class Vvm3Subscriber { 86d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 87d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String TAG = "Vvm3Subscriber"; 88d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 89d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String OPERATION_GET_SPG_URL = "retrieveSPGURL"; 90d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_URL_TAG = "spgurl"; 91d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String TRANSACTION_ID_TAG = "transactionid"; 92d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian //language=XML 93d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String VMG_XML_REQUEST_FORMAT = 94d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian "" 95d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 96d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + "<VMGVVMRequest>" 97d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " <MessageHeader>" 98d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " <transactionid>%1$s</transactionid>" 99d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " </MessageHeader>" 100d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " <MessageBody>" 101d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " <mdn>%2$s</mdn>" 102d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " <operation>%3$s</operation>" 103d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " <source>Device</source>" 104d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " <devicemodel>%4$s</devicemodel>" 105d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + " </MessageBody>" 106d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian + "</VMGVVMRequest>"; 107d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 108d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian static final String VMG_URL_KEY = "vmg_url"; 109d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 110d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Self provisioning POST key/values. VVM3 API 2.1.0 12.3 111d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_VZW_MDN_PARAM = "VZW_MDN"; 112d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_VZW_SERVICE_PARAM = "VZW_SERVICE"; 113d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_VZW_SERVICE_BASIC = "BVVM"; 114d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_DEVICE_MODEL_PARAM = "DEVICE_MODEL"; 115d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Value for all android device 116d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_DEVICE_MODEL_ANDROID = "DROID_4G"; 117d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_APP_TOKEN_PARAM = "APP_TOKEN"; 118d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_APP_TOKEN = "q8e3t5u2o1"; 119d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_LANGUAGE_PARAM = "SPG_LANGUAGE_PARAM"; 120d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String SPG_LANGUAGE_EN = "ENGLISH"; 121d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 12210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @VisibleForTesting 12310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian static final String VVM3_SUBSCRIBE_LINK_PATTERNS_JSON_ARRAY = 12410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian "vvm3_subscribe_link_pattern_json_array"; 12510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 12610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private static final String VVM3_SUBSCRIBE_LINK_DEFAULT_PATTERNS = 12710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian "[" 12810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian + "\"(?i)Subscribe to Basic Visual Voice Mail\"," 12910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian + "\"(?i)Subscribe to Basic Visual Voicemail\"" 13010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian + "]"; 131d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 132d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final int REQUEST_TIMEOUT_SECONDS = 30; 133d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 134d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private final ActivationTask mTask; 135d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private final PhoneAccountHandle mHandle; 136d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private final OmtpVvmCarrierConfigHelper mHelper; 137d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private final VoicemailStatus.Editor mStatus; 138d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private final Bundle mData; 139d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 140d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private final String mNumber; 141d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 142d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private RequestQueue mRequestQueue; 143d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 14410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @VisibleForTesting 14510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian static class ProvisioningException extends Exception { 146d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 147d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public ProvisioningException(String message) { 148d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian super(message); 149d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 150d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 151d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 152d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian static { 153d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Set the default cookie handler to retain session data for the self provisioning gateway. 154d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Note; this is not ideal as it is application-wide, and can easily get clobbered. 155d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // But it seems to be the preferred way to manage cookie for HttpURLConnection, and manually 156d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // managing cookies will greatly increase complexity. 157d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian CookieManager cookieManager = new CookieManager(); 158d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian CookieHandler.setDefault(cookieManager); 159d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 160d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 161d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian @WorkerThread 162d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public Vvm3Subscriber( 163d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian ActivationTask task, 164d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian PhoneAccountHandle handle, 165d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian OmtpVvmCarrierConfigHelper helper, 166d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VoicemailStatus.Editor status, 167d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Bundle data) { 168d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Assert.isNotMainThread(); 169d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mTask = task; 170d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHandle = handle; 171d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHelper = helper; 172d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mStatus = status; 173d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mData = data; 174d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 175d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Assuming getLine1Number() will work with VVM3. For unprovisioned users the IMAP username 176d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // is not included in the status SMS, thus no other way to get the current phone number. 177d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mNumber = 178d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHelper 179d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian .getContext() 180d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian .getSystemService(TelephonyManager.class) 181d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian .createForPhoneAccountHandle(mHandle) 182d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian .getLine1Number(); 183d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 184d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 185d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian @WorkerThread 186d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public void subscribe() { 187d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Assert.isNotMainThread(); 188d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Cellular data is required to subscribe. 189d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // processSubscription() is called after network is available. 190d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.i(TAG, "Subscribing"); 191d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 192d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try (NetworkWrapper wrapper = VvmNetworkRequest.getNetwork(mHelper, mHandle, mStatus)) { 193d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Network network = wrapper.get(); 194d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.d(TAG, "provisioning: network available"); 195d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mRequestQueue = 196d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Volley.newRequestQueue(mHelper.getContext(), new NetworkSpecifiedHurlStack(network)); 197d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian processSubscription(); 198d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (RequestFailedException e) { 199d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHelper.handleEvent(mStatus, OmtpEvents.VVM3_VMG_CONNECTION_FAILED); 200d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mTask.fail(); 201d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 202d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 203d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 204d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private void processSubscription() { 205d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 206d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String gatewayUrl = getSelfProvisioningGateway(); 207d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String selfProvisionResponse = getSelfProvisionResponse(gatewayUrl); 20810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian String subscribeLink = 20910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian findSubscribeLink(getSubscribeLinkPatterns(mHelper.getContext()), selfProvisionResponse); 210d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian clickSubscribeLink(subscribeLink); 211d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (ProvisioningException e) { 212d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.e(TAG, e.toString()); 213d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mTask.fail(); 214d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 215d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 216d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 217d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** Get the URL to perform self-provisioning from the voicemail management gateway. */ 218d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private String getSelfProvisioningGateway() throws ProvisioningException { 219d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.i(TAG, "retrieving SPG URL"); 220d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String response = vvm3XmlRequest(OPERATION_GET_SPG_URL); 221d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return extractText(response, SPG_URL_TAG); 222d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 223d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 224d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 225d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Sent a request to the self-provisioning gateway, which will return us with a webpage. The page 226d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * might contain a "Subscribe to Basic Visual Voice Mail" link to complete the subscription. The 227d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * cookie from this response and cellular data is required to click the link. 228d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 229d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private String getSelfProvisionResponse(String url) throws ProvisioningException { 230d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.i(TAG, "Retrieving self provisioning response"); 231d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 232d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian RequestFuture<String> future = RequestFuture.newFuture(); 233d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 234d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian StringRequest stringRequest = 235d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian new StringRequest(Request.Method.POST, url, future, future) { 236d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian @Override 237d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian protected Map<String, String> getParams() { 238d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Map<String, String> params = new ArrayMap<>(); 239d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian params.put(SPG_VZW_MDN_PARAM, mNumber); 240d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian params.put(SPG_VZW_SERVICE_PARAM, SPG_VZW_SERVICE_BASIC); 241d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian params.put(SPG_DEVICE_MODEL_PARAM, SPG_DEVICE_MODEL_ANDROID); 242d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian params.put(SPG_APP_TOKEN_PARAM, SPG_APP_TOKEN); 243d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Language to display the subscription page. The page is never shown to the user 244d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // so just use English. 245d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian params.put(SPG_LANGUAGE_PARAM, SPG_LANGUAGE_EN); 246d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return params; 247d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 248d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian }; 249d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 250d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mRequestQueue.add(stringRequest); 251d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 252d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); 253d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (InterruptedException | ExecutionException | TimeoutException e) { 254d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHelper.handleEvent(mStatus, OmtpEvents.VVM3_SPG_CONNECTION_FAILED); 255d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throw new ProvisioningException(e.toString()); 256d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 257d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 258d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 259d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private void clickSubscribeLink(String subscribeLink) throws ProvisioningException { 260d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.i(TAG, "Clicking subscribe link"); 261d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian RequestFuture<String> future = RequestFuture.newFuture(); 262d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 263d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian StringRequest stringRequest = 264d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian new StringRequest(Request.Method.POST, subscribeLink, future, future); 265d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mRequestQueue.add(stringRequest); 266d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 267d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // A new STATUS SMS will be sent after this request. 268d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); 269d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (TimeoutException | ExecutionException | InterruptedException e) { 270d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHelper.handleEvent(mStatus, OmtpEvents.VVM3_SPG_CONNECTION_FAILED); 271d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throw new ProvisioningException(e.toString()); 272d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 273d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // It could take very long for the STATUS SMS to return. Waiting for it is unreliable. 274d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Just leave the CONFIG STATUS as CONFIGURING and end the task. The user can always 275d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // manually retry if it took too long. 276d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 277d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 278d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private String vvm3XmlRequest(String operation) throws ProvisioningException { 279d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.d(TAG, "Sending vvm3XmlRequest for " + operation); 280d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String voicemailManagementGateway = mData.getString(VMG_URL_KEY); 281d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (voicemailManagementGateway == null) { 282d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian VvmLog.e(TAG, "voicemailManagementGateway url unknown"); 283d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 284d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 285d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String transactionId = createTransactionId(); 286d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String body = 287d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String.format( 288d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Locale.US, VMG_XML_REQUEST_FORMAT, transactionId, mNumber, operation, Build.MODEL); 289d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 290d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian RequestFuture<String> future = RequestFuture.newFuture(); 291d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian StringRequest stringRequest = 292d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian new StringRequest(Request.Method.POST, voicemailManagementGateway, future, future) { 293d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian @Override 294d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public byte[] getBody() throws AuthFailureError { 295d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return body.getBytes(); 296d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 297d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian }; 298d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mRequestQueue.add(stringRequest); 299d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 300d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 301d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String response = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); 302d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (!transactionId.equals(extractText(response, TRANSACTION_ID_TAG))) { 303d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throw new ProvisioningException("transactionId mismatch"); 304d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 305d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return response; 306d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (InterruptedException | ExecutionException | TimeoutException e) { 307d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHelper.handleEvent(mStatus, OmtpEvents.VVM3_VMG_CONNECTION_FAILED); 308d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throw new ProvisioningException(e.toString()); 309d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 310d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 311d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 31210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @VisibleForTesting 31310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian static List<Pattern> getSubscribeLinkPatterns(Context context) { 31410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian String patternsJsonString = 31510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian ConfigProviderBindings.get(context) 31610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .getString( 31710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian VVM3_SUBSCRIBE_LINK_PATTERNS_JSON_ARRAY, VVM3_SUBSCRIBE_LINK_DEFAULT_PATTERNS); 31810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian List<Pattern> patterns = new ArrayList<>(); 31910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian try { 32010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian JSONArray patternsArray = new JSONArray(patternsJsonString); 32110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian for (int i = 0; i < patternsArray.length(); i++) { 32210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian patterns.add(Pattern.compile(patternsArray.getString(i))); 32310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 32410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } catch (JSONException e) { 32510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian throw new IllegalArgumentException("Unable to parse patterns" + e); 32610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 32710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return patterns; 32810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 32910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 33010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @VisibleForTesting 33110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian static String findSubscribeLink(@NonNull List<Pattern> patterns, String response) 33210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian throws ProvisioningException { 33310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (patterns.isEmpty()) { 33410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian throw new IllegalArgumentException("empty patterns"); 33510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 336d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Spanned doc = Html.fromHtml(response, Html.FROM_HTML_MODE_LEGACY); 337d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian URLSpan[] spans = doc.getSpans(0, doc.length(), URLSpan.class); 338d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian StringBuilder fulltext = new StringBuilder(); 33910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 340d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian for (URLSpan span : spans) { 341d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian String text = doc.subSequence(doc.getSpanStart(span), doc.getSpanEnd(span)).toString(); 34210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian for (Pattern pattern : patterns) { 34310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (pattern.matcher(text).matches()) { 34410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return span.getURL(); 34510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 346d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 347d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian fulltext.append(text); 348d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 349d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throw new ProvisioningException("Subscribe link not found: " + fulltext); 350d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 351d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 352d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private String createTransactionId() { 353d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return String.valueOf(Math.abs(new Random().nextLong())); 354d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 355d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 356d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private String extractText(String xml, String tag) throws ProvisioningException { 357d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Pattern pattern = Pattern.compile("<" + tag + ">(.*)<\\/" + tag + ">"); 358d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Matcher matcher = pattern.matcher(xml); 359d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (matcher.find()) { 360d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return matcher.group(1); 361d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 362d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throw new ProvisioningException("Tag " + tag + " not found in xml response"); 363d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 364d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 365d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static class NetworkSpecifiedHurlStack extends HurlStack { 366d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 367d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private final Network mNetwork; 368d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 369d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public NetworkSpecifiedHurlStack(Network network) { 370d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mNetwork = network; 371d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 372d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 373d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian @Override 374d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian protected HttpURLConnection createConnection(URL url) throws IOException { 375d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return (HttpURLConnection) mNetwork.openConnection(url); 376d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 377d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 378d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian} 379