Merge "Support restricted card state." into nyc-mr1-dev
diff --git a/res/xml/vvm_config.xml b/res/xml/vvm_config.xml
index 57a1050..79edaa6 100644
--- a/res/xml/vvm_config.xml
+++ b/res/xml/vvm_config.xml
@@ -20,11 +20,6 @@
     <string-array name="mccmnc">
       <item value="TEST"/>
     </string-array>
-
-    <int name="vvm_port_number_int" value="20481"/>
-    <string name="vvm_destination_number_string">887</string>
-    <string name="vvm_type_string">vvm_type_omtp</string>
-    <boolean name="vvm_cellular_data_required_bool" value="true"/>
   </pbundle_as_map>
 
   <pbundle_as_map>
@@ -89,4 +84,55 @@
       <item value="AUTH=DIGEST-MD5"/>
     </string-array>
   </pbundle_as_map>
+
+  <pbundle_as_map>
+    <!-- Verizon USA -->
+    <string-array name="mccmnc">
+      <item value="310004"/>
+      <item value="310010"/>
+      <item value="310012"/>
+      <item value="310013"/>
+      <item value="310590"/>
+      <item value="310890"/>
+      <item value="310910"/>
+      <item value="311110"/>
+      <item value="311270"/>
+      <item value="311271"/>
+      <item value="311272"/>
+      <item value="311273"/>
+      <item value="311274"/>
+      <item value="311275"/>
+      <item value="311276"/>
+      <item value="311277"/>
+      <item value="311278"/>
+      <item value="311279"/>
+      <item value="311280"/>
+      <item value="311281"/>
+      <item value="311282"/>
+      <item value="311283"/>
+      <item value="311284"/>
+      <item value="311285"/>
+      <item value="311286"/>
+      <item value="311287"/>
+      <item value="311288"/>
+      <item value="311289"/>
+      <item value="311390"/>
+      <item value="311480"/>
+      <item value="311481"/>
+      <item value="311482"/>
+      <item value="311483"/>
+      <item value="311484"/>
+      <item value="311485"/>
+      <item value="311486"/>
+      <item value="311487"/>
+      <item value="311488"/>
+      <item value="311489"/>
+    </string-array>
+
+    <int name="vvm_port_number_int" value="0"/>
+    <string name="vvm_destination_number_string">900080006200</string>
+    <string name="vvm_type_string">vvm_type_vvm3</string>
+    <string name="vvm_client_prefix_string">//VZWVVM</string>
+    <boolean name="vvm_cellular_data_required_bool" value="true"/>
+  </pbundle_as_map>
 </list>
\ No newline at end of file
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index acffb79..bfc52b7 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -40,6 +40,7 @@
 import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
+import android.service.carrier.CarrierIdentifier;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -52,7 +53,9 @@
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
+import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
@@ -72,6 +75,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ProxyController;
+import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.uicc.IccIoResult;
@@ -138,6 +142,10 @@
     private static final int EVENT_PERFORM_NETWORK_SCAN_DONE = 40;
     private static final int CMD_SET_NETWORK_SELECTION_MODE_MANUAL = 41;
     private static final int EVENT_SET_NETWORK_SELECTION_MODE_MANUAL_DONE = 42;
+    private static final int CMD_SET_ALLOWED_CARRIERS = 43;
+    private static final int EVENT_SET_ALLOWED_CARRIERS_DONE = 44;
+    private static final int CMD_GET_ALLOWED_CARRIERS = 45;
+    private static final int EVENT_GET_ALLOWED_CARRIERS_DONE = 46;
 
     /** The singleton instance. */
     private static PhoneInterfaceManager sInstance;
@@ -760,6 +768,68 @@
                     }
                     break;
 
+                case CMD_SET_ALLOWED_CARRIERS:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_SET_ALLOWED_CARRIERS_DONE, request);
+                    mPhone.setAllowedCarriers(
+                            (List<CarrierIdentifier>) request.argument,
+                            onCompleted);
+                    break;
+
+                case EVENT_SET_ALLOWED_CARRIERS_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null && ar.result != null) {
+                        request.result = ar.result;
+                    } else {
+                        if (ar.result == null) {
+                            loge("setAllowedCarriers: Empty response");
+                        } else if (ar.exception instanceof CommandException) {
+                            loge("setAllowedCarriers: CommandException: " +
+                                    ar.exception);
+                        } else {
+                            loge("setAllowedCarriers: Unknown exception");
+                        }
+                    }
+                    // Result cannot be null. Return -1 on error.
+                    if (request.result == null) {
+                        request.result = new int[]{-1};
+                    }
+                    synchronized (request) {
+                        request.notifyAll();
+                    }
+                    break;
+
+                case CMD_GET_ALLOWED_CARRIERS:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_ALLOWED_CARRIERS_DONE, request);
+                    mPhone.getAllowedCarriers(onCompleted);
+                    break;
+
+                case EVENT_GET_ALLOWED_CARRIERS_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null && ar.result != null) {
+                        request.result = ar.result;
+                    } else {
+                        if (ar.result == null) {
+                            loge("getAllowedCarriers: Empty response");
+                        } else if (ar.exception instanceof CommandException) {
+                            loge("getAllowedCarriers: CommandException: " +
+                                    ar.exception);
+                        } else {
+                            loge("getAllowedCarriers: Unknown exception");
+                        }
+                    }
+                    // Result cannot be null. Return empty list of CarrierIdentifier.
+                    if (request.result == null) {
+                        request.result = new ArrayList<CarrierIdentifier>(0);
+                    }
+                    synchronized (request) {
+                        request.notifyAll();
+                    }
+                    break;
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -1828,55 +1898,36 @@
     }
 
     @Override
-    public void setVisualVoicemailSmsFilterEnabled(int subId, boolean value) {
+    public void enableVisualVoicemailSmsFilter(String callingPackage, int subId,
+            VisualVoicemailSmsFilterSettings settings) {
+        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         VisualVoicemailSmsFilterConfig
-                .setVisualVoicemailSmsFilterEnabled(mPhone.getContext(), subId, value);
+                .enableVisualVoicemailSmsFilter(mPhone.getContext(), callingPackage, subId,
+                        settings);
     }
 
     @Override
-    public boolean isVisualVoicemailSmsFilterEnabled(String packageName, int subId) {
-        return VisualVoicemailSmsFilterConfig
-                .isVisualVoicemailSmsFilterEnabled(mPhone.getContext(), packageName, subId);
-    }
-
-    @Override
-    public void setVisualVoicemailSmsFilterClientPrefix(int subId, String prefix) {
+    public void disableVisualVoicemailSmsFilter(String callingPackage, int subId) {
+        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         VisualVoicemailSmsFilterConfig
-                .setVisualVoicemailSmsFilterClientPrefix(mPhone.getContext(), subId, prefix);
+                .disableVisualVoicemailSmsFilter(mPhone.getContext(), callingPackage, subId);
     }
 
     @Override
-    public String getVisualVoicemailSmsFilterClientPrefix(String packageName, int subId) {
+    public VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(
+            String callingPackage, int subId) {
+        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         return VisualVoicemailSmsFilterConfig
-                .getVisualVoicemailSmsFilterClientPrefix(mPhone.getContext(), packageName, subId);
+                .getVisualVoicemailSmsFilterSettings(mPhone.getContext(), callingPackage, subId);
     }
 
     @Override
-    public void setVisualVoicemailSmsFilterOriginatingNumbers(int subId, String[] numbers) {
-        VisualVoicemailSmsFilterConfig
-                .setVisualVoicemailSmsFilterOriginatingNumbers(mPhone.getContext(), subId, numbers);
-    }
-
-    @Override
-    public String[] getVisualVoicemailSmsFilterOriginatingNumbers(String packageName, int subId) {
+    public VisualVoicemailSmsFilterSettings getSystemVisualVoicemailSmsFilterSettings(
+            String packageName, int subId) {
+        enforceReadPrivilegedPermission();
         return VisualVoicemailSmsFilterConfig
-                .getVisualVoicemailSmsFilterOriginatingNumbers(mPhone.getContext(), packageName,
-                        subId);
+                .getVisualVoicemailSmsFilterSettings(mPhone.getContext(), packageName, subId);
     }
-
-    @Override
-    public void setVisualVoicemailSmsFilterDestinationPort(int subId, int port) {
-        VisualVoicemailSmsFilterConfig
-                .setVisualVoicemailSmsFilterDestinationPort(mPhone.getContext(), subId, port);
-    }
-
-    @Override
-    public int getVisualVoicemailSmsFilterDestinationPort(String packageName, int subId) {
-        return VisualVoicemailSmsFilterConfig
-                .getVisualVoicemailSmsFilterDestinationPort(mPhone.getContext(), packageName,
-                        subId);
-    }
-
     /**
      * Returns the unread count of voicemails
      */
@@ -3099,7 +3150,6 @@
         return VoicemailNotificationSettingsUtil.isVibrationEnabled(phone);
     }
 
-
     /**
      * Make sure either called from same process as self (phone) or IPC caller has read privilege.
      *
@@ -3134,7 +3184,6 @@
         return aid;
     }
 
-
     /**
      * Return the Electronic Serial Number.
      *
@@ -3157,4 +3206,45 @@
         return esn;
     }
 
+    /**
+     * Get snapshot of Telephony histograms
+     * @return List of Telephony histograms
+     * @hide
+     */
+    @Override
+    public List<TelephonyHistogram> getTelephonyHistograms() {
+        enforceModifyPermissionOrCarrierPrivilege(getDefaultSubscription());
+        return RIL.getTelephonyRILTimingHistograms();
+    }
+
+    /**
+     * {@hide}
+     * Set the allowed carrier list for slotId
+     * Require system privileges. In the future we may add this to carrier APIs.
+     *
+     * @return The number of carriers set successfully, should match length of carriers
+     */
+    @Override
+    public int setAllowedCarriers(int slotId, List<CarrierIdentifier> carriers) {
+        enforceModifyPermission();
+        int subId = SubscriptionManager.getSubId(slotId)[0];
+        int[] retVal = (int[]) sendRequest(CMD_SET_ALLOWED_CARRIERS, carriers, subId);
+        return retVal[0];
+    }
+
+    /**
+     * {@hide}
+     * Get the allowed carrier list for slotId.
+     * Require system privileges. In the future we may add this to carrier APIs.
+     *
+     * @return List of {@link android.service.telephony.CarrierIdentifier}; empty list
+     * means all carriers are allowed.
+     */
+    @Override
+    public List<CarrierIdentifier> getAllowedCarriers(int slotId) {
+        enforceReadPrivilegedPermission();
+        int subId = SubscriptionManager.getSubId(slotId)[0];
+        return (List<CarrierIdentifier>) sendRequest(CMD_GET_ALLOWED_CARRIERS, null, subId);
+    }
+
 }
diff --git a/src/com/android/phone/VisualVoicemailSmsFilterConfig.java b/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
index e0139ee..2b2e2f5 100644
--- a/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
+++ b/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
@@ -15,13 +15,15 @@
  */
 package com.android.phone;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.preference.PreferenceManager;
-import android.telephony.TelephonyManager;
+import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.util.ArraySet;
 
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -38,104 +40,126 @@
     private static final String ORIGINATING_NUMBERS_KEY = "_originating_numbers";
     private static final String DESTINATION_PORT_KEY = "_destination_port";
 
-    public static void setVisualVoicemailSmsFilterEnabled(Context context, int subId,
-            boolean value) {
-        setBoolean(context, subId, ENABLED_KEY, value);
+    public static void enableVisualVoicemailSmsFilter(Context context, String callingPackage,
+            int subId,
+            VisualVoicemailSmsFilterSettings settings) {
+        new Editor(context, callingPackage, subId)
+                .setBoolean(ENABLED_KEY, true)
+                .setString(PREFIX_KEY, settings.clientPrefix)
+                .setStringList(ORIGINATING_NUMBERS_KEY, settings.originatingNumbers)
+                .setInt(DESTINATION_PORT_KEY, settings.destinationPort)
+                .apply();
     }
 
-    public static boolean isVisualVoicemailSmsFilterEnabled(Context context, String packageName,
+    public static void disableVisualVoicemailSmsFilter(Context context, String callingPackage,
             int subId) {
-        return getBoolean(context, packageName, subId, ENABLED_KEY);
+        new Editor(context, callingPackage, subId)
+                .setBoolean(ENABLED_KEY, false)
+                .apply();
     }
 
-    public static void setVisualVoicemailSmsFilterClientPrefix(Context context, int subId,
-            String prefix) {
-        setString(context, subId, PREFIX_KEY, prefix);
-    }
-
-    public static String getVisualVoicemailSmsFilterClientPrefix(Context context,
+    @Nullable
+    public static VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(
+            Context context,
             String packageName, int subId) {
-        return getString(context, packageName, subId, PREFIX_KEY);
+        Reader reader = new Reader(context, packageName, subId);
+        if (!reader.getBoolean(ENABLED_KEY, false)) {
+            return null;
+        }
+        return new VisualVoicemailSmsFilterSettings.Builder()
+                .setClientPrefix(reader.getString(PREFIX_KEY,
+                        VisualVoicemailSmsFilterSettings.DEFAULT_CLIENT_PREFIX))
+                .setOriginatingNumbers(reader.getStringSet(ORIGINATING_NUMBERS_KEY,
+                        VisualVoicemailSmsFilterSettings.DEFAULT_ORIGINATING_NUMBERS))
+                .setDestinationPort(reader.getInt(DESTINATION_PORT_KEY,
+                        VisualVoicemailSmsFilterSettings.DEFAULT_DESTINATION_PORT))
+                .build();
     }
-
-    public static void setVisualVoicemailSmsFilterOriginatingNumbers(Context context, int subId,
-            String[] numbers) {
-        ArraySet<String> set = new ArraySet<>();
-        set.addAll(Arrays.asList(numbers));
-        setStringSet(context, subId, ORIGINATING_NUMBERS_KEY, set);
-    }
-
-    public static String[] getVisualVoicemailSmsFilterOriginatingNumbers(Context context,
-            String packageName, int subId) {
-        Set<String> numbers = getStringSet(context, packageName, subId, ORIGINATING_NUMBERS_KEY);
-        return numbers.toArray(new String[numbers.size()]);
-    }
-
-    public static void setVisualVoicemailSmsFilterDestinationPort(Context context, int subId,
-            int port) {
-        setInt(context, subId, DESTINATION_PORT_KEY, port);
-    }
-
-    public static int getVisualVoicemailSmsFilterDestinationPort(Context context,
-            String packageName, int subId) {
-        return getInt(context, packageName, subId, DESTINATION_PORT_KEY,
-                TelephonyManager.VVM_SMS_FILTER_DESTINATION_PORT_ANY);
-    }
-
-    private static int getInt(Context context, String packageName, int subId, String key,
-            int defaultValue) {
-        SharedPreferences prefs = getSharedPreferences(context);
-        return prefs.getInt(makePerPhoneAccountKey(packageName, subId, key), defaultValue);
-    }
-
-    private static void setInt(Context context, int subId, String key, int value) {
-        SharedPreferences.Editor editor = getSharedPreferences(context).edit();
-        editor.putInt(makePerPhoneAccountKey(context.getOpPackageName(), subId, key), value);
-        editor.apply();
-    }
-
-    private static boolean getBoolean(Context context, String packageName, int subId, String key) {
-        SharedPreferences prefs = getSharedPreferences(context);
-        return prefs.getBoolean(makePerPhoneAccountKey(packageName, subId, key), false);
-    }
-
-    private static void setBoolean(Context context, int subId, String key, boolean value) {
-        SharedPreferences.Editor editor = getSharedPreferences(context).edit();
-        editor.putBoolean(makePerPhoneAccountKey(context.getOpPackageName(), subId, key), value);
-        editor.apply();
-    }
-
-    private static String getString(Context context, String packageName, int subId, String key) {
-        SharedPreferences prefs = getSharedPreferences(context);
-        return prefs.getString(makePerPhoneAccountKey(packageName, subId, key), null);
-    }
-
-    private static void setString(Context context, int subId, String key, String value) {
-        SharedPreferences.Editor editor = getSharedPreferences(context).edit();
-        editor.putString(makePerPhoneAccountKey(context.getOpPackageName(), subId, key), value);
-        editor.apply();
-    }
-
-    private static Set<String> getStringSet(Context context, String packageName, int subId,
-            String key) {
-        return getSharedPreferences(context)
-                .getStringSet(makePerPhoneAccountKey(packageName, subId, key), null);
-    }
-
-    private static void setStringSet(Context context, int subId, String key, Set<String> value) {
-        SharedPreferences.Editor editor = getSharedPreferences(context).edit();
-        editor.putStringSet(makePerPhoneAccountKey(context.getOpPackageName(), subId, key), value);
-        editor.apply();
-    }
-
     private static SharedPreferences getSharedPreferences(Context context) {
         return PreferenceManager
                 .getDefaultSharedPreferences(context.createDeviceProtectedStorageContext());
     }
 
-    private static String makePerPhoneAccountKey(String packageName, int subId, String key) {
-        // TODO: make sure subId is persistent enough to serve as a key
+    private static String makePerPhoneAccountKeyPrefix(String packageName, int subId) {
+        // subId is persistent across reboot and upgrade, but not across devices.
+        // ICC id is better as a key but it involves more trouble to get one as subId is more
+        // commonly passed around.
         return VVM_SMS_FILTER_COFIG_SHARED_PREFS_KEY_PREFIX + packageName + "_"
-                + subId + key;
+                + subId;
+    }
+
+    private static class Editor {
+
+        private final SharedPreferences.Editor mPrefsEditor;
+        private final String mKeyPrefix;
+
+        public Editor(Context context, String packageName, int subId) {
+            mPrefsEditor = getSharedPreferences(context).edit();
+            mKeyPrefix = makePerPhoneAccountKeyPrefix(packageName, subId);
+        }
+
+        private Editor setInt(String key, int value) {
+            mPrefsEditor.putInt(makeKey(key), value);
+            return this;
+        }
+
+        private Editor setString(String key, String value) {
+            mPrefsEditor.putString(makeKey(key), value);
+            return this;
+        }
+
+        private Editor setBoolean(String key, boolean value) {
+            mPrefsEditor.putBoolean(makeKey(key), value);
+            return this;
+        }
+
+        private Editor setStringList(String key, List<String> value) {
+            mPrefsEditor.putStringSet(makeKey(key), new ArraySet(value));
+            return this;
+        }
+
+        public void apply() {
+            mPrefsEditor.apply();
+        }
+
+        private String makeKey(String key) {
+            return mKeyPrefix + key;
+        }
+    }
+
+
+    private static class Reader {
+
+        private final SharedPreferences mPrefs;
+        private final String mKeyPrefix;
+
+        public Reader(Context context, String packageName, int subId) {
+            mPrefs = getSharedPreferences(context);
+            mKeyPrefix = makePerPhoneAccountKeyPrefix(packageName, subId);
+        }
+
+        private int getInt(String key, int defaultValue) {
+            return mPrefs.getInt(makeKey(key), defaultValue);
+        }
+
+        private String getString(String key, String defaultValue) {
+            return mPrefs.getString(makeKey(key), defaultValue);
+        }
+
+        private boolean getBoolean(String key, boolean defaultValue) {
+            return mPrefs.getBoolean(makeKey(key), defaultValue);
+        }
+
+        private List<String> getStringSet(String key, List<String> defaultValue) {
+            Set<String> result = mPrefs.getStringSet(makeKey(key), null);
+            if (result == null) {
+                return defaultValue;
+            }
+            return new ArrayList<>(result);
+        }
+
+        private String makeKey(String key) {
+            return mKeyPrefix + key;
+        }
     }
 }
diff --git a/src/com/android/phone/common/mail/store/ImapConnection.java b/src/com/android/phone/common/mail/store/ImapConnection.java
index 914ab10..ad47acc 100644
--- a/src/com/android/phone/common/mail/store/ImapConnection.java
+++ b/src/com/android/phone/common/mail/store/ImapConnection.java
@@ -260,7 +260,11 @@
             }
             for (int i = 0; i < response.size(); i++) {
                 String capability = response.getStringOrEmpty(i).getString();
-                if (disabledCapabilities != null && !disabledCapabilities.contains(capability)) {
+                if (disabledCapabilities != null) {
+                    if (!disabledCapabilities.contains(capability)) {
+                        mCapabilities.add(capability);
+                    }
+                } else {
                     mCapabilities.add(capability);
                 }
             }
diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java
index a08fe30..d403161 100644
--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java
+++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java
@@ -30,7 +30,6 @@
 import android.preference.PreferenceScreen;
 import android.preference.SwitchPreference;
 import android.provider.ContactsContract.CommonDataKinds;
-import android.telephony.TelephonyManager;
 import android.text.BidiFormatter;
 import android.text.TextDirectionHeuristics;
 import android.text.TextUtils;
@@ -41,10 +40,9 @@
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.phone.R;
 import com.android.phone.EditPhoneNumberPreference;
 import com.android.phone.PhoneGlobals;
-import com.android.phone.PhoneUtils;
+import com.android.phone.R;
 import com.android.phone.SubscriptionInfoHelper;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
@@ -261,8 +259,7 @@
 
         mVoicemailVisualVoicemail = (SwitchPreference) findPreference(
                 getResources().getString(R.string.voicemail_visual_voicemail_key));
-        if (TelephonyManager.VVM_TYPE_OMTP.equals(mOmtpVvmCarrierConfigHelper.getVvmType()) ||
-                TelephonyManager.VVM_TYPE_CVVM.equals(mOmtpVvmCarrierConfigHelper.getVvmType())) {
+        if (mOmtpVvmCarrierConfigHelper.isValid()) {
             mVoicemailVisualVoicemail.setOnPreferenceChangeListener(this);
             mVoicemailVisualVoicemail.setChecked(
                     VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mPhone));
diff --git a/src/com/android/phone/vvm/omtp/OmtpConstants.java b/src/com/android/phone/vvm/omtp/OmtpConstants.java
index 5fc0317..896ffe0 100644
--- a/src/com/android/phone/vvm/omtp/OmtpConstants.java
+++ b/src/com/android/phone/vvm/omtp/OmtpConstants.java
@@ -136,8 +136,6 @@
      * <p>
      * Referred by {@link OmtpConstants#PROVISIONING_STATUS}.
      */
-    // TODO: As per the spec the code could be either be with or w/o quotes  = "N"/N). Currently
-    // this only handles the w/o quotes values.
     public static final String SUBSCRIBER_NEW = "N";
     public static final String SUBSCRIBER_READY = "R";
     public static final String SUBSCRIBER_PROVISIONED = "P";
diff --git a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
index df706d5..64e7e31 100644
--- a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
+++ b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
@@ -18,19 +18,19 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.SmsManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.phone.vvm.omtp.sms.OmtpCvvmMessageSender;
-import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
-import com.android.phone.vvm.omtp.sms.OmtpStandardMessageSender;
+import com.android.phone.vvm.omtp.protocol.VisualVoicemailProtocol;
+import com.android.phone.vvm.omtp.protocol.VisualVoicemailProtocolFactory;
 
 import java.util.Arrays;
 import java.util.Set;
@@ -87,7 +87,7 @@
     private final int mSubId;
     private final PersistableBundle mCarrierConfig;
     private final String mVvmType;
-
+    private final VisualVoicemailProtocol mProtocol;
     private final PersistableBundle mTelephonyConfig;
 
     public OmtpVvmCarrierConfigHelper(Context context, int subId) {
@@ -101,6 +101,7 @@
                 .getConfig(telephonyManager.getNetworkOperator(subId));
 
         mVvmType = getVvmType();
+        mProtocol = VisualVoicemailProtocolFactory.create(mVvmType);
     }
 
     @VisibleForTesting
@@ -111,6 +112,23 @@
         mCarrierConfig = carrierConfig;
         mTelephonyConfig = telephonyConfig;
         mVvmType = getVvmType();
+        mProtocol = VisualVoicemailProtocolFactory.create(mVvmType);
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public int getSubId() {
+        return mSubId;
+    }
+
+    /**
+     * return whether the carrier's visual voicemail is supported, with KEY_VVM_TYPE_STRING set as a
+     * known protocol.
+     */
+    public boolean isValid() {
+        return mProtocol != null;
     }
 
     @Nullable
@@ -145,11 +163,6 @@
         return names;
     }
 
-    public boolean isOmtpVvmType() {
-        return (TelephonyManager.VVM_TYPE_OMTP.equals(mVvmType) ||
-                TelephonyManager.VVM_TYPE_CVVM.equals(mVvmType));
-    }
-
     /**
      * For checking upon sim insertion whether visual voicemail should be enabled. This method does
      * so by checking if the carrier's voicemail app is installed.
@@ -171,20 +184,16 @@
     }
 
     public boolean isCellularDataRequired() {
-        return (boolean) getValue(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL);
+        return (boolean) getValue(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL, false);
     }
 
     public boolean isPrefetchEnabled() {
-        return (boolean) getValue(KEY_VVM_PREFETCH_BOOL);
+        return (boolean) getValue(KEY_VVM_PREFETCH_BOOL, true);
     }
 
 
     public int getApplicationPort() {
-        Integer port = (Integer) getValue(KEY_VVM_PORT_NUMBER_INT);
-        if (port != null) {
-            return port;
-        }
-        return 0;
+        return (int) getValue(KEY_VVM_PORT_NUMBER_INT, 0);
     }
 
     @Nullable
@@ -200,11 +209,7 @@
      * TODO: make config public and add to CarrierConfigManager
      */
     public int getSslPort() {
-        Integer port = (Integer) getValue(KEY_VVM_SSL_PORT_NUMBER_INT);
-        if (port != null) {
-            return port;
-        }
-        return 0;
+        return (int) getValue(KEY_VVM_SSL_PORT_NUMBER_INT, 0);
     }
 
     /**
@@ -250,23 +255,32 @@
 
     public void startActivation() {
         TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
-        telephonyManager.setVisualVoicemailSmsFilterEnabled(mSubId, true);
-        telephonyManager.setVisualVoicemailSmsFilterClientPrefix(mSubId, getClientPrefix());
+        telephonyManager.enableVisualVoicemailSmsFilter(mSubId,
+                new VisualVoicemailSmsFilterSettings.Builder().setClientPrefix(getClientPrefix())
+                        .build());
 
-        OmtpMessageSender messageSender = getMessageSender();
-        if (messageSender != null) {
-            Log.i(TAG, "Requesting VVM activation for subId: " + mSubId);
-            messageSender.requestVvmActivation(null);
+        if (mProtocol != null) {
+            mProtocol.startActivation(this);
         }
     }
 
     public void startDeactivation() {
         mContext.getSystemService(TelephonyManager.class)
-                .setVisualVoicemailSmsFilterEnabled(mSubId, false);
-        OmtpMessageSender messageSender = getMessageSender();
-        if (messageSender != null) {
-            Log.i(TAG, "Requesting VVM deactivation for subId: " + mSubId);
-            messageSender.requestVvmDeactivation(null);
+                .disableVisualVoicemailSmsFilter(mSubId);
+        if (mProtocol != null) {
+            mProtocol.startDeactivation(this);
+        }
+    }
+
+    public void startProvisioning(Bundle data) {
+        if (mProtocol != null) {
+            mProtocol.startProvisioning(this, data);
+        }
+    }
+
+    public void requestStatus() {
+        if (mProtocol != null) {
+            mProtocol.requestStatus(this);
         }
     }
 
@@ -293,39 +307,13 @@
         return config;
     }
 
-    private OmtpMessageSender getMessageSender() {
-        if (mCarrierConfig == null && mTelephonyConfig == null) {
-            Log.w(TAG, "Empty carrier config.");
-            return null;
-        }
-
-        int applicationPort = getApplicationPort();
-        String destinationNumber = getDestinationNumber();
-        if (TextUtils.isEmpty(destinationNumber)) {
-            Log.w(TAG, "No destination number for this carrier.");
-            return null;
-        }
-
-        OmtpMessageSender messageSender = null;
-        SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(mSubId);
-        switch (mVvmType) {
-            case TelephonyManager.VVM_TYPE_OMTP:
-                messageSender = new OmtpStandardMessageSender(smsManager, (short) applicationPort,
-                        destinationNumber, null, OmtpConstants.PROTOCOL_VERSION1_1, null);
-                break;
-            case TelephonyManager.VVM_TYPE_CVVM:
-                messageSender = new OmtpCvvmMessageSender(smsManager, (short) applicationPort,
-                        destinationNumber);
-                break;
-            default:
-                Log.w(TAG, "Unexpected visual voicemail type: " + mVvmType);
-        }
-
-        return messageSender;
+    @Nullable
+    private Object getValue(String key) {
+        return getValue(key, null);
     }
 
     @Nullable
-    private Object getValue(String key) {
+    private Object getValue(String key, Object defaultValue) {
         Object result;
         if (mCarrierConfig != null) {
             result = mCarrierConfig.get(key);
@@ -339,6 +327,6 @@
                 return result;
             }
         }
-        return null;
+        return defaultValue;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index 0a37493..830243a 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -87,7 +87,7 @@
     public static void processSubId(Context context, int subId) {
         OmtpVvmCarrierConfigHelper carrierConfigHelper =
                 new OmtpVvmCarrierConfigHelper(context, subId);
-        if (carrierConfigHelper.isOmtpVvmType()) {
+        if (carrierConfigHelper.isValid()) {
             PhoneAccountHandle phoneAccount = PhoneUtils.makePstnPhoneAccountHandle(
                     SubscriptionManager.getPhoneId(subId));
 
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index 404c771..4c2192e 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -120,8 +120,6 @@
                 VoicemailContract.Status.QUOTA_UNAVAILABLE);
         mQuotaTotal = mPrefs.getInt(getSharedPrefsKey(PREF_KEY_QUOTA_TOTAL),
                 VoicemailContract.Status.QUOTA_UNAVAILABLE);
-
-        Log.v(TAG, "Quota:" + mQuotaOccupied + "/" + mQuotaTotal);
     }
 
     /**
diff --git a/src/com/android/phone/vvm/omtp/protocol/CvvmProtocol.java b/src/com/android/phone/vvm/omtp/protocol/CvvmProtocol.java
new file mode 100644
index 0000000..9c4d531
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/CvvmProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * 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.telephony.SmsManager;
+
+import com.android.phone.vvm.omtp.sms.OmtpCvvmMessageSender;
+import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
+
+/**
+ * A flavor of OMTP protocol with a different mobile originated (MO) format
+ */
+public class CvvmProtocol extends VisualVoicemailProtocol {
+
+    @Override
+    public OmtpMessageSender createMessageSender(SmsManager smsManager, short applicationPort,
+            String destinationNumber) {
+        return new OmtpCvvmMessageSender(smsManager, applicationPort, destinationNumber);
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/protocol/OmtpProtocol.java b/src/com/android/phone/vvm/omtp/protocol/OmtpProtocol.java
new file mode 100644
index 0000000..d002652
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/OmtpProtocol.java
@@ -0,0 +1,33 @@
+/*
+ * 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.telephony.SmsManager;
+
+import com.android.phone.vvm.omtp.OmtpConstants;
+import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
+import com.android.phone.vvm.omtp.sms.OmtpStandardMessageSender;
+
+public class OmtpProtocol extends VisualVoicemailProtocol {
+
+    @Override
+    public OmtpMessageSender createMessageSender(SmsManager smsManager, short applicationPort,
+            String destinationNumber) {
+        return new OmtpStandardMessageSender(smsManager, applicationPort, destinationNumber,
+                null, OmtpConstants.PROTOCOL_VERSION1_1, null);
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java b/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java
new file mode 100644
index 0000000..d265bd0
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java
@@ -0,0 +1,43 @@
+/*
+ * 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.telephony.SmsManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
+
+public class ProtocolHelper {
+
+    private static final String TAG = "ProtocolHelper";
+
+    public static OmtpMessageSender getMessageSender(VisualVoicemailProtocol protocol,
+            OmtpVvmCarrierConfigHelper config) {
+
+        int applicationPort = config.getApplicationPort();
+        String destinationNumber = config.getDestinationNumber();
+        if (TextUtils.isEmpty(destinationNumber)) {
+            Log.w(TAG, "No destination number for this carrier.");
+            return null;
+        }
+
+        SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(config.getSubId());
+        return protocol.createMessageSender(smsManager, (short) applicationPort, destinationNumber);
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocol.java b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocol.java
new file mode 100644
index 0000000..bc7dc82
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocol.java
@@ -0,0 +1,54 @@
+/*
+ * 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.os.Bundle;
+import android.telephony.SmsManager;
+
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
+
+public abstract class VisualVoicemailProtocol {
+
+    public void startActivation(OmtpVvmCarrierConfigHelper config) {
+        OmtpMessageSender messageSender = ProtocolHelper.getMessageSender(this, config);
+        if (messageSender != null) {
+            messageSender.requestVvmActivation(null);
+        }
+    }
+
+    public void startDeactivation(OmtpVvmCarrierConfigHelper config) {
+        OmtpMessageSender messageSender = ProtocolHelper.getMessageSender(this, config);
+        if (messageSender != null) {
+            messageSender.requestVvmDeactivation(null);
+        }
+    }
+
+    public void startProvisioning(OmtpVvmCarrierConfigHelper config, Bundle data) {
+        // Do nothing
+    }
+
+    public void requestStatus(OmtpVvmCarrierConfigHelper config) {
+        OmtpMessageSender messageSender = ProtocolHelper.getMessageSender(this, config);
+        if (messageSender != null) {
+            messageSender.requestVvmStatus(null);
+        }
+    }
+
+    public abstract OmtpMessageSender createMessageSender(SmsManager smsManager,
+            short applicationPort, String destinationNumber);
+}
diff --git a/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java
new file mode 100644
index 0000000..dbf38c2
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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.Nullable;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+public class VisualVoicemailProtocolFactory {
+
+    private static final String TAG = "VvmProtocolFactory";
+
+    private static final String VVM_TYPE_VVM3 = "vvm_type_vvm3";
+
+    @Nullable
+    public static VisualVoicemailProtocol create(String type) {
+        if (type == null) {
+            return null;
+        }
+        switch (type) {
+            case TelephonyManager.VVM_TYPE_OMTP:
+                return new OmtpProtocol();
+            case TelephonyManager.VVM_TYPE_CVVM:
+                return new CvvmProtocol();
+            case VVM_TYPE_VVM3:
+                return new Vvm3Protocol();
+            default:
+                Log.e(TAG, "Unexpected visual voicemail type: " + type);
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
new file mode 100644
index 0000000..f53d270
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
@@ -0,0 +1,63 @@
+/*
+ * 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.os.Bundle;
+import android.telephony.SmsManager;
+import android.util.Log;
+
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
+import com.android.phone.vvm.omtp.sms.Vvm3MessageSender;
+
+/**
+ * A flavor of OMTP protocol with a different provisioning process
+ */
+public class Vvm3Protocol extends VisualVoicemailProtocol {
+
+    private static String TAG = "Vvm3Protocol";
+
+    public Vvm3Protocol() {
+        Log.d(TAG, "Vvm3Protocol created");
+    }
+
+    @Override
+    public void startActivation(OmtpVvmCarrierConfigHelper config) {
+        // VVM3 does not support activation SMS.
+        // Send a status request which will start the provisioning process if the user is not
+        // provisioned.
+        config.requestStatus();
+    }
+
+    @Override
+    public void startDeactivation(OmtpVvmCarrierConfigHelper config) {
+        // VVM3 does not support deactivation.
+        // do nothing.
+    }
+
+    @Override
+    public void startProvisioning(OmtpVvmCarrierConfigHelper config, Bundle data) {
+        Log.d(TAG, "start vvm3 provisioning");
+        // TODO: implement (b/28697797).
+    }
+
+    @Override
+    public OmtpMessageSender createMessageSender(SmsManager smsManager, short applicationPort,
+            String destinationNumber) {
+        return new Vvm3MessageSender(smsManager, applicationPort, destinationNumber);
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index 67ffef7..f2beabe 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -33,6 +33,7 @@
 import com.android.phone.settings.VisualVoicemailSettingsUtil;
 import com.android.phone.vvm.omtp.LocalLogHelper;
 import com.android.phone.vvm.omtp.OmtpConstants;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSyncService;
 import com.android.phone.vvm.omtp.sync.VoicemailsQueryHelper;
@@ -44,7 +45,6 @@
     private static final String TAG = "OmtpMessageReceiver";
 
     private Context mContext;
-    private PhoneAccountHandle mPhoneAccount;
 
     @Override
     public void onReceive(Context context, Intent intent) {
@@ -55,17 +55,17 @@
         }
 
         mContext = context;
-        mPhoneAccount = PhoneUtils.makePstnPhoneAccountHandle(
+        PhoneAccountHandle phone = PhoneUtils.makePstnPhoneAccountHandle(
                 SubscriptionManager.getPhoneId(
                         intent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID)));
 
-        if (mPhoneAccount == null) {
-            Log.w(TAG, "Received message for null phone account");
+        if (phone == null) {
+            Log.i(TAG, "Received message for null phone account");
             return;
         }
 
-        if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mContext, mPhoneAccount)) {
-            Log.v(TAG, "Received vvm message for disabled vvm source.");
+        if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mContext, phone)) {
+            Log.i(TAG, "Received vvm message for disabled vvm source.");
             return;
         }
 
@@ -76,21 +76,32 @@
         if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
             SyncMessage message = new SyncMessage(data);
 
-            Log.v(TAG, "Received SYNC sms for " + mPhoneAccount.getId() +
+            Log.v(TAG, "Received SYNC sms for " + phone.getId() +
                     " with event " + message.getSyncTriggerEvent());
-            LocalLogHelper.log(TAG, "Received SYNC sms for " + mPhoneAccount.getId() +
+            LocalLogHelper.log(TAG, "Received SYNC sms for " + phone.getId() +
                     " with event " + message.getSyncTriggerEvent());
-            processSync(message);
+            processSync(phone, message);
         } else if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
-            Log.v(TAG, "Received STATUS sms for " + mPhoneAccount.getId());
-            LocalLogHelper.log(TAG, "Received Status sms for " + mPhoneAccount.getId());
+            Log.v(TAG, "Received STATUS sms for " + phone.getId());
+            LocalLogHelper.log(TAG, "Received Status sms for " + phone.getId());
             StatusMessage message = new StatusMessage(data);
-            updateSource(message);
+            if (message.getProvisioningStatus().equals(OmtpConstants.SUBSCRIBER_READY)) {
+                updateSource(phone, message);
+            } else {
+                Log.v(TAG, "Subscriber not ready, start provisioning");
+                startProvisioning(phone, data);
+            }
         } else {
             Log.e(TAG, "Unknown prefix: " + eventType);
         }
     }
 
+    private void startProvisioning(PhoneAccountHandle phone, Bundle data) {
+        OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext,
+                PhoneUtils.getSubIdForPhoneAccountHandle(phone));
+        helper.startProvisioning(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
@@ -99,13 +110,13 @@
      *
      * @param message The sync message to extract data from.
      */
-    private void processSync(SyncMessage message) {
+    private void processSync(PhoneAccountHandle phone, SyncMessage message) {
         Intent serviceIntent = null;
         switch (message.getSyncTriggerEvent()) {
             case OmtpConstants.NEW_MESSAGE:
                 Voicemail.Builder builder = Voicemail.createForInsertion(
                         message.getTimestampMillis(), message.getSender())
-                        .setPhoneAccount(mPhoneAccount)
+                        .setPhoneAccount(phone)
                         .setSourceData(message.getId())
                         .setDuration(message.getLength())
                         .setSourcePackage(mContext.getPackageName());
@@ -116,13 +127,13 @@
                     Uri uri = VoicemailContract.Voicemails.insert(mContext, voicemail);
                     voicemail = builder.setId(ContentUris.parseId(uri)).setUri(uri).build();
                     serviceIntent = OmtpVvmSyncService.getSyncIntent(mContext,
-                            OmtpVvmSyncService.SYNC_DOWNLOAD_ONE_TRANSCRIPTION, mPhoneAccount,
+                            OmtpVvmSyncService.SYNC_DOWNLOAD_ONE_TRANSCRIPTION, phone,
                             voicemail, true /* firstAttempt */);
                 }
                 break;
             case OmtpConstants.MAILBOX_UPDATE:
                 serviceIntent = OmtpVvmSyncService.getSyncIntent(
-                        mContext, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY, mPhoneAccount,
+                        mContext, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY, phone,
                         true /* firstAttempt */);
                 break;
             case OmtpConstants.GREETINGS_UPDATE:
@@ -138,12 +149,12 @@
         }
     }
 
-    private void updateSource(StatusMessage message) {
+    private void updateSource(PhoneAccountHandle phone, StatusMessage message) {
         OmtpVvmSourceManager vvmSourceManager =
                 OmtpVvmSourceManager.getInstance(mContext);
 
         if (OmtpConstants.SUCCESS.equals(message.getReturnCode())) {
-            VoicemailContract.Status.setStatus(mContext, mPhoneAccount,
+            VoicemailContract.Status.setStatus(mContext, phone,
                     VoicemailContract.Status.CONFIGURATION_STATE_OK,
                     VoicemailContract.Status.DATA_CHANNEL_STATE_OK,
                     VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK);
@@ -151,24 +162,24 @@
             // Save the IMAP credentials in preferences so they are persistent and can be retrieved.
             VisualVoicemailSettingsUtil.setVisualVoicemailCredentialsFromStatusMessage(
                     mContext,
-                    mPhoneAccount,
+                    phone,
                     message);
 
             // Add the source to indicate that it is active.
-            vvmSourceManager.addSource(mPhoneAccount);
+            vvmSourceManager.addSource(phone);
 
             Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
-                    mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, mPhoneAccount,
+                    mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, phone,
                     true /* firstAttempt */);
             mContext.startService(serviceIntent);
 
             PhoneGlobals.getInstance().clearMwiIndicator(
-                    PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount));
+                    PhoneUtils.getSubIdForPhoneAccountHandle(phone));
         } else {
             Log.w(TAG, "Visual voicemail not available for subscriber.");
             // Override default isEnabled setting to false since visual voicemail is unable to
             // be accessed for some reason.
-            VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mContext, mPhoneAccount,
+            VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mContext, phone,
                     /* isEnabled */ false, /* isUserSet */ true);
         }
     }
diff --git a/src/com/android/phone/vvm/omtp/sms/StatusMessage.java b/src/com/android/phone/vvm/omtp/sms/StatusMessage.java
index 4d8c815..ee1f07d 100644
--- a/src/com/android/phone/vvm/omtp/sms/StatusMessage.java
+++ b/src/com/android/phone/vvm/omtp/sms/StatusMessage.java
@@ -61,7 +61,7 @@
     }
 
     public StatusMessage(Bundle wrappedData) {
-        mProvisioningStatus = wrappedData.getString(OmtpConstants.PROVISIONING_STATUS);
+        mProvisioningStatus = unquote(wrappedData.getString(OmtpConstants.PROVISIONING_STATUS));
         mStatusReturnCode = wrappedData.getString(OmtpConstants.RETURN_CODE);
         mSubscriptionUrl = wrappedData.getString(OmtpConstants.SUBSCRIPTION_URL);
         mServerAddress = wrappedData.getString(OmtpConstants.SERVER_ADDRESS);
@@ -76,6 +76,16 @@
         mSmtpPassword = wrappedData.getString(OmtpConstants.SMTP_PASSWORD);
     }
 
+    private static String unquote(String string) {
+        if (string.length() < 2) {
+            return string;
+        }
+        if (string.startsWith("\"") && string.endsWith("\"")) {
+            return string.substring(1, string.length() - 1);
+        }
+        return string;
+    }
+
     /**
      * @return the subscriber's VVM provisioning status.
      */
diff --git a/src/com/android/phone/vvm/omtp/sms/Vvm3MessageSender.java b/src/com/android/phone/vvm/omtp/sms/Vvm3MessageSender.java
new file mode 100644
index 0000000..24c2d03
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/sms/Vvm3MessageSender.java
@@ -0,0 +1,56 @@
+/*
+ * 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.annotation.Nullable;
+import android.app.PendingIntent;
+import android.telephony.SmsManager;
+
+public class Vvm3MessageSender extends OmtpMessageSender {
+
+    /**
+     * Creates a new instance of Vvm3MessageSender.
+     *
+     * @param smsManager SMS sending library. There is a different SmsManager for each SIM.
+     * @param applicationPort If set to a value > 0 then a binary sms is sent to this port number.
+     * Otherwise, a standard text SMS is sent.
+     */
+    public Vvm3MessageSender(SmsManager smsManager, short applicationPort,
+            String destinationNumber) {
+        super(smsManager, applicationPort, destinationNumber);
+    }
+
+    @Override
+    public void requestVvmActivation(@Nullable PendingIntent sentIntent) {
+        // Activation not supported for VVM3, send a status request instead.
+        requestVvmStatus(sentIntent);
+    }
+
+    @Override
+    public void requestVvmDeactivation(@Nullable PendingIntent sentIntent) {
+        // Deactivation not supported for VVM3, do nothing
+    }
+
+
+    @Override
+    public void requestVvmStatus(@Nullable PendingIntent sentIntent) {
+        // Status message:
+        // STATUS
+        StringBuilder sb = new StringBuilder().append("STATUS");
+        sendSms(sb.toString(), sentIntent);
+    }
+
+}