Merge "Support unknown user provisioning for VVM3" into nyc-mr1-dev
diff --git a/Android.mk b/Android.mk
index f3fff79..cbebc44 100644
--- a/Android.mk
+++ b/Android.mk
@@ -11,7 +11,9 @@
 
 LOCAL_JAVA_LIBRARIES := telephony-common voip-common ims-common
 LOCAL_STATIC_JAVA_LIBRARIES := \
-        guava
+        org.apache.http.legacy \
+        guava \
+        volley
 
 LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
 LOCAL_SRC_FILES += \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 334e186..0a9a6a5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -676,18 +676,18 @@
                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
             </intent-filter>
         </receiver>
-       <receiver
-           android:name="com.android.phone.vvm.omtp.fetch.FetchVoicemailReceiver"
-           android:exported="true"
-           android:permission="com.android.voicemail.permission.READ_VOICEMAIL">
-           <intent-filter>
-              <action android:name="android.intent.action.FETCH_VOICEMAIL" />
-               <data
-                   android:scheme="content"
-                   android:host="com.android.voicemail"
-                   android:mimeType="vnd.android.cursor.item/voicemail" />
-          </intent-filter>
-       </receiver>
+        <receiver
+            android:name="com.android.phone.vvm.omtp.fetch.FetchVoicemailReceiver"
+            android:exported="true"
+            android:permission="com.android.voicemail.permission.READ_VOICEMAIL">
+            <intent-filter>
+                <action android:name="android.intent.action.FETCH_VOICEMAIL" />
+                    <data
+                        android:scheme="content"
+                        android:host="com.android.voicemail"
+                        android:mimeType="vnd.android.cursor.item/voicemail" />
+            </intent-filter>
+        </receiver>
         <receiver
             android:name="com.android.phone.vvm.omtp.sync.OmtpVvmSyncReceiver"
             android:exported="true"
@@ -696,27 +696,31 @@
                 <action android:name="android.provider.action.SYNC_VOICEMAIL"/>
             </intent-filter>
         </receiver>
-       <receiver
-           android:name="com.android.phone.vvm.omtp.sync.VoicemailProviderChangeReceiver"
-           android:exported="true">
-           <intent-filter>
-              <action android:name="android.intent.action.PROVIDER_CHANGED" />
-               <data
-                   android:scheme="content"
-                   android:host="com.android.voicemail"
-                   android:mimeType="vnd.android.cursor.dir/voicemails"/>
-          </intent-filter>
-       </receiver>
-       <service
+        <receiver
+            android:name="com.android.phone.vvm.omtp.sync.VoicemailProviderChangeReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.PROVIDER_CHANGED" />
+                <data
+                    android:scheme="content"
+                    android:host="com.android.voicemail"
+                    android:mimeType="vnd.android.cursor.dir/voicemails"/>
+            </intent-filter>
+        </receiver>
+        <service
             android:name="com.android.phone.vvm.omtp.sync.OmtpVvmSyncService"
-            android:exported="false"
-       />
-       <receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver">
-          <intent-filter>
-              <action android:name="android.intent.action.PACKAGE_INSTALL" />
-              <action android:name="android.intent.action.PACKAGE_ADDED" />
-              <data android:scheme="package"/>
-          </intent-filter>
-       </receiver>
+            android:exported="false" />
+
+        <service
+            android:name="com.android.phone.vvm.omtp.sms.OmtpProvisioningService"
+            android:exported="false" />
+
+        <receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.PACKAGE_INSTALL" />
+                <action android:name="android.intent.action.PACKAGE_ADDED" />
+                <data android:scheme="package"/>
+            </intent-filter>
+        </receiver>
     </application>
 </manifest>
diff --git a/src/com/android/phone/Assert.java b/src/com/android/phone/Assert.java
new file mode 100644
index 0000000..d4233b2
--- /dev/null
+++ b/src/com/android/phone/Assert.java
@@ -0,0 +1,40 @@
+/*
+ * 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.phone;
+
+import android.os.Looper;
+
+/**
+ * Assertions which will result in program termination.
+ */
+public class Assert {
+
+    public static void isTrue(boolean condition) {
+        if (!condition) {
+            throw new AssertionError("Expected condition to be true");
+        }
+    }
+
+    public static void isNotMainThread() {
+        isTrue(!Looper.getMainLooper().equals(Looper.myLooper()));
+    }
+
+    public static void fail() {
+        throw new AssertionError("Fail");
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
index d2e30f7..e62d1cf 100644
--- a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
@@ -81,8 +81,8 @@
             OmtpVvmCarrierConfigHelper config, StatusMessage message, Bundle data) {
         Log.i(TAG, "start vvm3 provisioning");
         if ("U".equals(message.getProvisioningStatus())) {
-            Log.i(TAG, "Provisioning status: Unknown. VVM subscription not implemented.");
-            // TODO: implement (b/28697797).
+            Log.i(TAG, "Provisioning status: Unknown, subscribing");
+            new Vvm3Subscriber(phoneAccountHandle, config, data).subscribe();
         } else if ("N".equals(message.getProvisioningStatus())) {
             Log.i(TAG, "setting up new user");
             VisualVoicemailSettingsUtil.setVisualVoicemailCredentialsFromStatusMessage(
@@ -204,8 +204,6 @@
         }
     }
 
-
-
     private static String generatePin() {
         SecureRandom random = new SecureRandom();
         // TODO(b/29102412): generate base on the length requirement from the server
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java
new file mode 100644
index 0000000..c314ff5
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java
@@ -0,0 +1,309 @@
+/*
+ * 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.phone.vvm.omtp.protocol;
+
+import android.annotation.WorkerThread;
+import android.net.Network;
+import android.os.Build;
+import android.os.Bundle;
+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 android.util.Log;
+
+import com.android.phone.Assert;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
+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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * After the process is completed, the carrier should send us another STATUS SMS with a new or ready
+ * user.
+ */
+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>";
+
+    private 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 PhoneAccountHandle mHandle;
+    private final OmtpVvmCarrierConfigHelper mHelper;
+    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(PhoneAccountHandle handle, OmtpVvmCarrierConfigHelper helper,
+            Bundle data) {
+        Assert.isNotMainThread();
+        mHandle = handle;
+        mHelper = helper;
+        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)
+                .getLine1Number(mHelper.getSubId());
+    }
+
+    @WorkerThread
+    public void subscribe() {
+        Assert.isNotMainThread();
+        // Cellular data is required to subscribe.
+        // processSubscription() is called after network is available.
+        new Vvm3ProvisioningNetworkRequestCallback(mHelper, mHandle).requestNetwork();
+    }
+
+    private void processSubscription() {
+        try {
+            String gatewayUrl = getSelfProvisioningGateway();
+            String selfProvisionResponse = getSelfProvisionResponse(gatewayUrl);
+            String subscribeLink = findSubscribeLink(selfProvisionResponse);
+            clickSubscribeLink(subscribeLink);
+        } catch (ProvisioningException e) {
+            Log.e(TAG, e.toString());
+        }
+    }
+
+    /**
+     * Get the URL to perform self-provisioning from the voicemail management gateway.
+     */
+    private String getSelfProvisioningGateway() throws ProvisioningException {
+        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 {
+        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) {
+            throw new ProvisioningException(e.toString());
+        }
+    }
+
+    private void clickSubscribeLink(String subscribeLink) throws ProvisioningException {
+        RequestFuture<String> future = RequestFuture.newFuture();
+
+        StringRequest stringRequest = new StringRequest(Request.Method.POST,
+                subscribeLink, future, future);
+        mRequestQueue.add(stringRequest);
+
+        try {
+            future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            throw new ProvisioningException(e.toString());
+        }
+    }
+
+    private String vvm3XmlRequest(String operation) throws ProvisioningException {
+        Log.d(TAG, "Sending vvm3XmlRequest for " + operation);
+        String voicemailManagementGateway = mData.getString(VMG_URL_KEY);
+        if (voicemailManagementGateway == null) {
+            Log.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) {
+            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);
+        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();
+            }
+        }
+        throw new ProvisioningException("Subscribe link not found");
+    }
+
+    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 class Vvm3ProvisioningNetworkRequestCallback extends VvmNetworkRequestCallback {
+
+        public Vvm3ProvisioningNetworkRequestCallback(OmtpVvmCarrierConfigHelper config,
+                PhoneAccountHandle phoneAccountHandle) {
+            super(config, phoneAccountHandle);
+        }
+
+        @Override
+        public void onAvailable(Network network) {
+            super.onAvailable(network);
+            Log.d(TAG, "provisioning: network available");
+            mRequestQueue = Volley
+                    .newRequestQueue(mContext, new NetworkSpecifiedHurlStack(network));
+            processSubscription();
+        }
+    }
+
+    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);
+        }
+
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index b668ad8..feb3c5a 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -28,7 +28,6 @@
 import android.util.Log;
 
 import com.android.phone.PhoneGlobals;
-import com.android.phone.PhoneUtils;
 import com.android.phone.settings.VisualVoicemailSettingsUtil;
 import com.android.phone.vvm.omtp.LocalLogHelper;
 import com.android.phone.vvm.omtp.OmtpConstants;
@@ -89,19 +88,13 @@
                 updateSource(phone, subId, message);
             } else {
                 Log.v(TAG, "Subscriber not ready, start provisioning");
-                startProvisioning(phone, message, data);
+                mContext.startService(OmtpProvisioningService.getProvisionIntent(mContext, intent));
             }
         } else {
             Log.e(TAG, "Unknown prefix: " + eventType);
         }
     }
 
-    private void startProvisioning(PhoneAccountHandle phone, StatusMessage message, Bundle data) {
-        OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext,
-                PhoneUtils.getSubIdForPhoneAccountHandle(phone));
-        helper.startProvisioning(phone, message, data);
-    }
-
     /**
      * A sync message has two purposes: to signal a new voicemail message, and to indicate the
      * voicemails on the server have changed remotely (usually through the TUI). Save the new
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java b/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java
new file mode 100644
index 0000000..ced9490
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java
@@ -0,0 +1,70 @@
+/*
+ * 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.phone.vvm.omtp.sms;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.phone.PhoneUtils;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
+
+/**
+ * Performs visual voicemail provisioning in background thread. Not exported.
+ */
+public class OmtpProvisioningService extends IntentService {
+
+    public OmtpProvisioningService() {
+        super("OmtpProvisioningService");
+    }
+
+    /**
+     * Create an intent to start OmtpProvisioningService from a {@link
+     * VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} intent.
+     */
+    public static Intent getProvisionIntent(Context context, Intent messageIntent) {
+        Intent serviceIntent = new Intent(context, OmtpProvisioningService.class);
+
+        serviceIntent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID,
+                messageIntent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID));
+        serviceIntent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS,
+                messageIntent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS));
+
+        return serviceIntent;
+    }
+
+    @Override
+    public void onHandleIntent(Intent intent) {
+        int subId = intent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID);
+        PhoneAccountHandle phone = PhoneAccountHandleConverter.fromSubId(subId);
+
+        Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
+
+        StatusMessage message = new StatusMessage(data);
+        startProvisioning(phone, message, data);
+    }
+
+    private void startProvisioning(PhoneAccountHandle phone, StatusMessage message, Bundle data) {
+        OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(this,
+                PhoneUtils.getSubIdForPhoneAccountHandle(phone));
+        helper.startProvisioning(phone, message, data);
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/sms/SyncMessage.java b/src/com/android/phone/vvm/omtp/sms/SyncMessage.java
index 1e565da..4dddba4 100644
--- a/src/com/android/phone/vvm/omtp/sms/SyncMessage.java
+++ b/src/com/android/phone/vvm/omtp/sms/SyncMessage.java
@@ -15,6 +15,7 @@
  */
 package com.android.phone.vvm.omtp.sms;
 
+import android.annotation.Nullable;
 import android.os.Bundle;
 
 import com.android.phone.vvm.omtp.OmtpConstants;
@@ -36,6 +37,7 @@
     // UID of the new message.
     private final String mMessageId;
     // Length of the message.
+    @Nullable
     private final Integer mMessageLength;
     // Content type (voice, video, fax...) of the new message.
     private final String mContentType;
@@ -58,7 +60,12 @@
     public SyncMessage(Bundle wrappedData) {
         mSyncTriggerEvent = wrappedData.getString(OmtpConstants.SYNC_TRIGGER_EVENT);
         mMessageId = wrappedData.getString(OmtpConstants.MESSAGE_UID);
-        mMessageLength = Integer.parseInt(wrappedData.getString(OmtpConstants.MESSAGE_LENGTH));
+        if (wrappedData.getString(OmtpConstants.MESSAGE_LENGTH) != null) {
+            mMessageLength = Integer.parseInt(wrappedData.getString(OmtpConstants.MESSAGE_LENGTH));
+        } else {
+            // Optional field
+            mMessageLength = null;
+        }
         mContentType = wrappedData.getString(OmtpConstants.CONTENT_TYPE);
         mSender = wrappedData.getString(OmtpConstants.SENDER);
         mNewMessageCount = Integer.parseInt(wrappedData.getString(OmtpConstants.NUM_MESSAGE_COUNT));