summaryrefslogtreecommitdiff
path: root/java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java')
-rw-r--r--java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java326
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);
- }
-
- }
-}