diff options
Diffstat (limited to 'java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java')
-rw-r--r-- | java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java | 326 |
1 files changed, 0 insertions, 326 deletions
diff --git a/java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java b/java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java deleted file mode 100644 index 0a4d792b2..000000000 --- a/java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.voicemailomtp.protocol; - -import android.annotation.TargetApi; -import android.net.Network; -import android.os.Build; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.support.annotation.WorkerThread; -import android.telecom.PhoneAccountHandle; -import android.telephony.TelephonyManager; -import android.text.Html; -import android.text.Spanned; -import android.text.style.URLSpan; -import android.util.ArrayMap; -import com.android.voicemailomtp.ActivationTask; -import com.android.voicemailomtp.Assert; -import com.android.voicemailomtp.OmtpEvents; -import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper; -import com.android.voicemailomtp.VoicemailStatus; -import com.android.voicemailomtp.VvmLog; -import com.android.voicemailomtp.sync.VvmNetworkRequest; -import com.android.voicemailomtp.sync.VvmNetworkRequest.NetworkWrapper; -import com.android.voicemailomtp.sync.VvmNetworkRequest.RequestFailedException; -import com.android.volley.AuthFailureError; -import com.android.volley.Request; -import com.android.volley.RequestQueue; -import com.android.volley.toolbox.HurlStack; -import com.android.volley.toolbox.RequestFuture; -import com.android.volley.toolbox.StringRequest; -import com.android.volley.toolbox.Volley; -import java.io.IOException; -import java.net.CookieHandler; -import java.net.CookieManager; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Locale; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Class to subscribe to basic VVM3 visual voicemail, for example, Verizon. Subscription is required - * when the user is unprovisioned. This could happen when the user is on a legacy service, or - * switched over from devices that used other type of visual voicemail. - * - * <p>The STATUS SMS will come with a URL to the voicemail management gateway. From it we can find - * the self provisioning gateway URL that we can modify voicemail services. - * - * <p>A request to the self provisioning gateway to activate basic visual voicemail will return us - * with a web page. If the user hasn't subscribe to it yet it will contain a link to confirm the - * subscription. This link should be clicked through cellular network, and have cookies enabled. - * - * <p>After the process is completed, the carrier should send us another STATUS SMS with a new or - * ready user. - */ -@TargetApi(VERSION_CODES.CUR_DEVELOPMENT) -public class Vvm3Subscriber { - - private static final String TAG = "Vvm3Subscriber"; - - private static final String OPERATION_GET_SPG_URL = "retrieveSPGURL"; - private static final String SPG_URL_TAG = "spgurl"; - private static final String TRANSACTION_ID_TAG = "transactionid"; - //language=XML - private static final String VMG_XML_REQUEST_FORMAT = "" - + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" - + "<VMGVVMRequest>" - + " <MessageHeader>" - + " <transactionid>%1$s</transactionid>" - + " </MessageHeader>" - + " <MessageBody>" - + " <mdn>%2$s</mdn>" - + " <operation>%3$s</operation>" - + " <source>Device</source>" - + " <devicemodel>%4$s</devicemodel>" - + " </MessageBody>" - + "</VMGVVMRequest>"; - - static final String VMG_URL_KEY = "vmg_url"; - - // Self provisioning POST key/values. VVM3 API 2.1.0 12.3 - private static final String SPG_VZW_MDN_PARAM = "VZW_MDN"; - private static final String SPG_VZW_SERVICE_PARAM = "VZW_SERVICE"; - private static final String SPG_VZW_SERVICE_BASIC = "BVVM"; - private static final String SPG_DEVICE_MODEL_PARAM = "DEVICE_MODEL"; - // Value for all android device - private static final String SPG_DEVICE_MODEL_ANDROID = "DROID_4G"; - private static final String SPG_APP_TOKEN_PARAM = "APP_TOKEN"; - private static final String SPG_APP_TOKEN = "q8e3t5u2o1"; - private static final String SPG_LANGUAGE_PARAM = "SPG_LANGUAGE_PARAM"; - private static final String SPG_LANGUAGE_EN = "ENGLISH"; - - private static final String BASIC_SUBSCRIBE_LINK_TEXT = "Subscribe to Basic Visual Voice Mail"; - - private static final int REQUEST_TIMEOUT_SECONDS = 30; - - private final ActivationTask mTask; - private final PhoneAccountHandle mHandle; - private final OmtpVvmCarrierConfigHelper mHelper; - private final VoicemailStatus.Editor mStatus; - private final Bundle mData; - - private final String mNumber; - - private RequestQueue mRequestQueue; - - private static class ProvisioningException extends Exception { - - public ProvisioningException(String message) { - super(message); - } - } - - static { - // Set the default cookie handler to retain session data for the self provisioning gateway. - // Note; this is not ideal as it is application-wide, and can easily get clobbered. - // But it seems to be the preferred way to manage cookie for HttpURLConnection, and manually - // managing cookies will greatly increase complexity. - CookieManager cookieManager = new CookieManager(); - CookieHandler.setDefault(cookieManager); - } - - @WorkerThread - public Vvm3Subscriber(ActivationTask task, PhoneAccountHandle handle, - OmtpVvmCarrierConfigHelper helper, VoicemailStatus.Editor status, Bundle data) { - Assert.isNotMainThread(); - mTask = task; - mHandle = handle; - mHelper = helper; - mStatus = status; - mData = data; - - // Assuming getLine1Number() will work with VVM3. For unprovisioned users the IMAP username - // is not included in the status SMS, thus no other way to get the current phone number. - mNumber = mHelper.getContext().getSystemService(TelephonyManager.class) - .createForPhoneAccountHandle(mHandle).getLine1Number(); - } - - @WorkerThread - public void subscribe() { - Assert.isNotMainThread(); - // Cellular data is required to subscribe. - // processSubscription() is called after network is available. - VvmLog.i(TAG, "Subscribing"); - - try (NetworkWrapper wrapper = VvmNetworkRequest.getNetwork(mHelper, mHandle, mStatus)) { - Network network = wrapper.get(); - VvmLog.d(TAG, "provisioning: network available"); - mRequestQueue = Volley - .newRequestQueue(mHelper.getContext(), new NetworkSpecifiedHurlStack(network)); - processSubscription(); - } catch (RequestFailedException e) { - mHelper.handleEvent(mStatus, OmtpEvents.VVM3_VMG_CONNECTION_FAILED); - mTask.fail(); - } - } - - private void processSubscription() { - try { - String gatewayUrl = getSelfProvisioningGateway(); - String selfProvisionResponse = getSelfProvisionResponse(gatewayUrl); - String subscribeLink = findSubscribeLink(selfProvisionResponse); - clickSubscribeLink(subscribeLink); - } catch (ProvisioningException e) { - VvmLog.e(TAG, e.toString()); - mTask.fail(); - } - } - - /** - * Get the URL to perform self-provisioning from the voicemail management gateway. - */ - private String getSelfProvisioningGateway() throws ProvisioningException { - VvmLog.i(TAG, "retrieving SPG URL"); - String response = vvm3XmlRequest(OPERATION_GET_SPG_URL); - return extractText(response, SPG_URL_TAG); - } - - /** - * Sent a request to the self-provisioning gateway, which will return us with a webpage. The - * page might contain a "Subscribe to Basic Visual Voice Mail" link to complete the - * subscription. The cookie from this response and cellular data is required to click the link. - */ - private String getSelfProvisionResponse(String url) throws ProvisioningException { - VvmLog.i(TAG, "Retrieving self provisioning response"); - - RequestFuture<String> future = RequestFuture.newFuture(); - - StringRequest stringRequest = new StringRequest(Request.Method.POST, url, future, future) { - @Override - protected Map<String, String> getParams() { - Map<String, String> params = new ArrayMap<>(); - params.put(SPG_VZW_MDN_PARAM, mNumber); - params.put(SPG_VZW_SERVICE_PARAM, SPG_VZW_SERVICE_BASIC); - params.put(SPG_DEVICE_MODEL_PARAM, SPG_DEVICE_MODEL_ANDROID); - params.put(SPG_APP_TOKEN_PARAM, SPG_APP_TOKEN); - // Language to display the subscription page. The page is never shown to the user - // so just use English. - params.put(SPG_LANGUAGE_PARAM, SPG_LANGUAGE_EN); - return params; - } - }; - - mRequestQueue.add(stringRequest); - try { - return future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - mHelper.handleEvent(mStatus, OmtpEvents.VVM3_SPG_CONNECTION_FAILED); - throw new ProvisioningException(e.toString()); - } - } - - private void clickSubscribeLink(String subscribeLink) throws ProvisioningException { - VvmLog.i(TAG, "Clicking subscribe link"); - RequestFuture<String> future = RequestFuture.newFuture(); - - StringRequest stringRequest = new StringRequest(Request.Method.POST, - subscribeLink, future, future); - mRequestQueue.add(stringRequest); - try { - // A new STATUS SMS will be sent after this request. - future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); - } catch (TimeoutException | ExecutionException | InterruptedException e) { - mHelper.handleEvent(mStatus, OmtpEvents.VVM3_SPG_CONNECTION_FAILED); - throw new ProvisioningException(e.toString()); - } - // It could take very long for the STATUS SMS to return. Waiting for it is unreliable. - // Just leave the CONFIG STATUS as CONFIGURING and end the task. The user can always - // manually retry if it took too long. - } - - private String vvm3XmlRequest(String operation) throws ProvisioningException { - VvmLog.d(TAG, "Sending vvm3XmlRequest for " + operation); - String voicemailManagementGateway = mData.getString(VMG_URL_KEY); - if (voicemailManagementGateway == null) { - VvmLog.e(TAG, "voicemailManagementGateway url unknown"); - return null; - } - String transactionId = createTransactionId(); - String body = String.format(Locale.US, VMG_XML_REQUEST_FORMAT, - transactionId, mNumber, operation, Build.MODEL); - - RequestFuture<String> future = RequestFuture.newFuture(); - StringRequest stringRequest = new StringRequest(Request.Method.POST, - voicemailManagementGateway, future, future) { - @Override - public byte[] getBody() throws AuthFailureError { - return body.getBytes(); - } - }; - mRequestQueue.add(stringRequest); - - try { - String response = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); - if (!transactionId.equals(extractText(response, TRANSACTION_ID_TAG))) { - throw new ProvisioningException("transactionId mismatch"); - } - return response; - } catch (InterruptedException | ExecutionException | TimeoutException e) { - mHelper.handleEvent(mStatus, OmtpEvents.VVM3_VMG_CONNECTION_FAILED); - throw new ProvisioningException(e.toString()); - } - } - - private String findSubscribeLink(String response) throws ProvisioningException { - Spanned doc = Html.fromHtml(response, Html.FROM_HTML_MODE_LEGACY); - URLSpan[] spans = doc.getSpans(0, doc.length(), URLSpan.class); - StringBuilder fulltext = new StringBuilder(); - for (URLSpan span : spans) { - String text = doc.subSequence(doc.getSpanStart(span), doc.getSpanEnd(span)).toString(); - if (BASIC_SUBSCRIBE_LINK_TEXT.equals(text)) { - return span.getURL(); - } - fulltext.append(text); - } - throw new ProvisioningException("Subscribe link not found: " + fulltext); - } - - private String createTransactionId() { - return String.valueOf(Math.abs(new Random().nextLong())); - } - - private String extractText(String xml, String tag) throws ProvisioningException { - Pattern pattern = Pattern.compile("<" + tag + ">(.*)<\\/" + tag + ">"); - Matcher matcher = pattern.matcher(xml); - if (matcher.find()) { - return matcher.group(1); - } - throw new ProvisioningException("Tag " + tag + " not found in xml response"); - } - - private static class NetworkSpecifiedHurlStack extends HurlStack { - - private final Network mNetwork; - - public NetworkSpecifiedHurlStack(Network network) { - mNetwork = network; - } - - @Override - protected HttpURLConnection createConnection(URL url) throws IOException { - return (HttpURLConnection) mNetwork.openConnection(url); - } - - } -} |