Merge "Changes the order of operations for E911 Calls" into nyc-mr1-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4a95259..4906ba2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -664,14 +664,16 @@
             </intent-filter>
         </provider>
         <receiver android:name="com.android.phone.vvm.omtp.sms.OmtpMessageReceiver"
-            android:exported="true">
+            android:exported="true"
+            androidprv:systemUserOnly="true">
             <intent-filter>
                 <action android:name="android.intent.action.VOICEMAIL_SMS_RECEIVED"/>
             </intent-filter>
         </receiver>
         <receiver
             android:name="com.android.phone.vvm.omtp.SimChangeReceiver"
-            android:exported="true">
+            android:exported="true"
+            androidprv:systemUserOnly="true">
             <intent-filter>
                 <action android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
                 <action android:name="android.intent.action.SIM_STATE_CHANGED" />
@@ -680,7 +682,8 @@
         <receiver
             android:name="com.android.phone.vvm.omtp.VvmBootCompletedReceiver"
             android:exported="true"
-            android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
+            android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
+            androidprv:systemUserOnly="true">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
             </intent-filter>
@@ -688,7 +691,8 @@
         <receiver
             android:name="com.android.phone.vvm.omtp.fetch.FetchVoicemailReceiver"
             android:exported="true"
-            android:permission="com.android.voicemail.permission.READ_VOICEMAIL">
+            android:permission="com.android.voicemail.permission.READ_VOICEMAIL"
+            androidprv:systemUserOnly="true">
             <intent-filter>
                 <action android:name="android.intent.action.FETCH_VOICEMAIL" />
                     <data
@@ -700,14 +704,16 @@
         <receiver
             android:name="com.android.phone.vvm.omtp.sync.OmtpVvmSyncReceiver"
             android:exported="true"
-            android:permission="com.android.voicemail.permission.READ_VOICEMAIL">
+            android:permission="com.android.voicemail.permission.READ_VOICEMAIL"
+            androidprv:systemUserOnly="true">
             <intent-filter>
                 <action android:name="android.provider.action.SYNC_VOICEMAIL"/>
             </intent-filter>
         </receiver>
         <receiver
             android:name="com.android.phone.vvm.omtp.sync.VoicemailProviderChangeReceiver"
-            android:exported="true">
+            android:exported="true"
+            androidprv:systemUserOnly="true">
             <intent-filter>
                 <action android:name="android.intent.action.PROVIDER_CHANGED" />
                 <data
@@ -724,7 +730,8 @@
             android:name="com.android.phone.vvm.omtp.sms.OmtpProvisioningService"
             android:exported="false" />
 
-        <receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver">
+        <receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver"
+            androidprv:systemUserOnly="true">
             <intent-filter>
                 <action android:name="android.intent.action.PACKAGE_INSTALL" />
                 <action android:name="android.intent.action.PACKAGE_ADDED" />
diff --git a/src/com/android/phone/DumpsysHandler.java b/src/com/android/phone/DumpsysHandler.java
index cf3bde2..d2ae38f 100644
--- a/src/com/android/phone/DumpsysHandler.java
+++ b/src/com/android/phone/DumpsysHandler.java
@@ -1,7 +1,9 @@
 
 package com.android.phone;
 
-import com.android.phone.vvm.omtp.LocalLogHelper;
+import android.content.Context;
+
+import com.android.phone.vvm.omtp.utils.VvmDumpHandler;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -11,8 +13,9 @@
  */
 public class DumpsysHandler {
 
-    public static void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
+    public static void dump(Context context, FileDescriptor fd, PrintWriter writer,
+            String[] args) {
         // Dump OMTP visual voicemail log.
-        LocalLogHelper.dump(fd, writer, args);
+        VvmDumpHandler.dump(context, fd, writer, args);
     }
 }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 783878c..808a5d6 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -637,8 +637,53 @@
         notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange();
     }
 
-    private void handleAirplaneModeChange(int newMode) {
-        if (newMode == AIRPLANE_ON) {
+    private void handleAirplaneModeChange(Context context, int newMode) {
+        int cellState = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.CELL_ON, PhoneConstants.CELL_ON_FLAG);
+        boolean isAirplaneNewlyOn = (newMode == 1);
+        switch (cellState) {
+            case PhoneConstants.CELL_OFF_FLAG:
+                // Airplane mode does not affect the cell radio if user
+                // has turned it off.
+                break;
+            case PhoneConstants.CELL_ON_FLAG:
+                maybeTurnCellOff(context, isAirplaneNewlyOn);
+                break;
+            case PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG:
+                maybeTurnCellOn(context, isAirplaneNewlyOn);
+                break;
+        }
+    }
+
+    /*
+     * Returns true if the radio must be turned off when entering airplane mode.
+     */
+    private boolean isCellOffInAirplaneMode(Context context) {
+        String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_RADIOS);
+        return airplaneModeRadios == null
+                || airplaneModeRadios.contains(Settings.Global.RADIO_CELL);
+    }
+
+    private void setRadioPowerOff(Context context) {
+        Log.i(LOG_TAG, "Turning radio off - airplane");
+        Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
+                 PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
+        Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
+        PhoneUtils.setRadioPower(false);
+    }
+
+    private void setRadioPowerOn(Context context) {
+        Log.i(LOG_TAG, "Turning radio on - airplane");
+        Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
+                PhoneConstants.CELL_ON_FLAG);
+        Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT,
+                1);
+        PhoneUtils.setRadioPower(true);
+    }
+
+    private void maybeTurnCellOff(Context context, boolean isAirplaneNewlyOn) {
+        if (isAirplaneNewlyOn) {
             // If we are trying to turn off the radio, make sure there are no active
             // emergency calls.  If there are, switch airplane mode back to off.
             if (PhoneUtils.isInEmergencyCall(mCM)) {
@@ -647,13 +692,17 @@
                 Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
                         .show();
                 Log.i(LOG_TAG, "Ignoring airplane mode: emergency call. Turning airplane off");
+            } else if (isCellOffInAirplaneMode(context)) {
+                setRadioPowerOff(context);
             } else {
-                Log.i(LOG_TAG, "Turning radio off - airplane");
-                PhoneUtils.setRadioPower(false);
+                Log.i(LOG_TAG, "Ignoring airplane mode: settings prevent cell radio power off");
             }
-        } else {
-            Log.i(LOG_TAG, "Turning radio on - airplane");
-            PhoneUtils.setRadioPower(true);
+        }
+    }
+
+    private void maybeTurnCellOn(Context context, boolean isAirplaneNewlyOn) {
+        if (!isAirplaneNewlyOn) {
+            setRadioPowerOn(context);
         }
     }
 
@@ -671,7 +720,7 @@
                 if (airplaneMode != AIRPLANE_OFF) {
                     airplaneMode = AIRPLANE_ON;
                 }
-                handleAirplaneModeChange(airplaneMode);
+                handleAirplaneModeChange(context, airplaneMode);
             } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index ecefe24..ed014d3 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -3296,7 +3296,7 @@
      * bug report is being generated.
      */
     @Override
-    protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
-        DumpsysHandler.dump(fd, writer, args);
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        DumpsysHandler.dump(mPhone.getContext(), fd, writer, args);
     }
 }
diff --git a/src/com/android/phone/common/mail/internet/MimeUtility.java b/src/com/android/phone/common/mail/internet/MimeUtility.java
index ba5036f..7402a4c 100644
--- a/src/com/android/phone/common/mail/internet/MimeUtility.java
+++ b/src/com/android/phone/common/mail/internet/MimeUtility.java
@@ -19,7 +19,6 @@
 import android.util.Base64;
 import android.util.Base64DataException;
 import android.util.Base64InputStream;
-import android.util.Log;
 
 import com.android.phone.common.mail.Body;
 import com.android.phone.common.mail.BodyPart;
@@ -27,6 +26,7 @@
 import com.android.phone.common.mail.MessagingException;
 import com.android.phone.common.mail.Multipart;
 import com.android.phone.common.mail.Part;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.james.mime4j.codec.EncoderUtil;
@@ -267,14 +267,14 @@
              * If we are not able to process the body there's nothing we can do about it. Return
              * null and let the upper layers handle the missing content.
              */
-            Log.e(LOG_TAG, "Unable to getTextFromPart " + oom.toString());
+            VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + oom.toString());
         }
         catch (Exception e) {
             /*
              * If we are not able to process the body there's nothing we can do about it. Return
              * null and let the upper layers handle the missing content.
              */
-            Log.e(LOG_TAG, "Unable to getTextFromPart " + e.toString());
+            VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + e.toString());
         }
         return null;
     }
diff --git a/src/com/android/phone/common/mail/store/ImapConnection.java b/src/com/android/phone/common/mail/store/ImapConnection.java
index de40f2c..0360e3e 100644
--- a/src/com/android/phone/common/mail/store/ImapConnection.java
+++ b/src/com/android/phone/common/mail/store/ImapConnection.java
@@ -15,7 +15,6 @@
  */
 package com.android.phone.common.mail.store;
 
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Base64;
 
@@ -180,18 +179,48 @@
             }
         } catch (ImapException ie) {
             LogUtils.d(TAG, "ImapException", ie);
-            final String status = ie.getStatus();
-            final String code = ie.getResponseCode();
-            final String alertText = ie.getAlertText();
+            String status = ie.getStatus();
+            String statusMessage = ie.getStatusMessage();
+            String alertText = ie.getAlertText();
 
-            // if the response code indicates expired or bad credentials, throw a special exception
-            if (ImapConstants.AUTHENTICATIONFAILED.equals(code) ||
-                    ImapConstants.EXPIRED.equals(code) ||
-                    (ImapConstants.NO.equals(status) && TextUtils.isEmpty(code))) {
-                mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_BAD_IMAP_CREDENTIAL);
+            if (ImapConstants.NO.equals(status)) {
+                switch (statusMessage) {
+                    case ImapConstants.NO_UNKNOWN_USER:
+                        mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_AUTH_UNKNOWN_USER);
+                        break;
+                    case ImapConstants.NO_UNKNOWN_CLIENT:
+                        mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_AUTH_UNKNOWN_DEVICE);
+                        break;
+                    case ImapConstants.NO_INVALID_PASSWORD:
+                        mImapStore.getImapHelper()
+                                .handleEvent(OmtpEvents.DATA_AUTH_INVALID_PASSWORD);
+                        break;
+                    case ImapConstants.NO_MAILBOX_NOT_INITIALIZED:
+                        mImapStore.getImapHelper()
+                                .handleEvent(OmtpEvents.DATA_AUTH_MAILBOX_NOT_INITIALIZED);
+                        break;
+                    case ImapConstants.NO_SERVICE_IS_NOT_PROVISIONED:
+                        mImapStore.getImapHelper()
+                                .handleEvent(OmtpEvents.DATA_AUTH_SERVICE_NOT_PROVISIONED);
+                        break;
+                    case ImapConstants.NO_SERVICE_IS_NOT_ACTIVATED:
+                        mImapStore.getImapHelper()
+                                .handleEvent(OmtpEvents.DATA_AUTH_SERVICE_NOT_ACTIVATED);
+                        break;
+                    case ImapConstants.NO_USER_IS_BLOCKED:
+                        mImapStore.getImapHelper()
+                                .handleEvent(OmtpEvents.DATA_AUTH_USER_IS_BLOCKED);
+                        break;
+                    case ImapConstants.NO_APPLICATION_ERROR:
+                        mImapStore.getImapHelper()
+                                .handleEvent(OmtpEvents.DATA_REJECTED_SERVER_RESPONSE);
+                    default:
+                        mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_BAD_IMAP_CREDENTIAL);
+                }
                 throw new AuthenticationFailedException(alertText, ie);
             }
 
+            mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_REJECTED_SERVER_RESPONSE);
             throw new MessagingException(alertText, ie);
         }
     }
@@ -359,16 +388,11 @@
         if (!(response.isOk() || response.isContinuationRequest())) {
             final String toString = response.toString();
             final String status = response.getStatusOrEmpty().getString();
+            final String statusMessage = response.getStatusResponseTextOrEmpty().getString();
             final String alert = response.getAlertTextOrEmpty().getString();
             final String responseCode = response.getResponseCodeOrEmpty().getString();
             destroyResponses();
-            mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_REJECTED_SERVER_RESPONSE);
-            // if the response code indicates an error occurred within the server, indicate that
-            if (ImapConstants.UNAVAILABLE.equals(responseCode)) {
-
-                throw new MessagingException(MessagingException.SERVER_ERROR, alert);
-            }
-            throw new ImapException(toString, status, alert, responseCode);
+            throw new ImapException(toString, status, statusMessage, alert, responseCode);
         }
         return responses;
     }
diff --git a/src/com/android/phone/common/mail/store/ImapStore.java b/src/com/android/phone/common/mail/store/ImapStore.java
index c8095e5..179d0f2 100644
--- a/src/com/android/phone/common/mail/store/ImapStore.java
+++ b/src/com/android/phone/common/mail/store/ImapStore.java
@@ -130,13 +130,15 @@
         private static final long serialVersionUID = 1L;
 
         private final String mStatus;
+        private final String mStatusMessage;
         private final String mAlertText;
         private final String mResponseCode;
 
-        public ImapException(String message, String status, String alertText,
+        public ImapException(String message, String status, String statusMessage, String alertText,
                 String responseCode) {
             super(message);
             mStatus = status;
+            mStatusMessage = statusMessage;
             mAlertText = alertText;
             mResponseCode = responseCode;
         }
@@ -145,6 +147,10 @@
             return mStatus;
         }
 
+        public String getStatusMessage() {
+            return mStatusMessage;
+        }
+
         public String getAlertText() {
             return mAlertText;
         }
diff --git a/src/com/android/phone/common/mail/store/imap/DigestMd5Utils.java b/src/com/android/phone/common/mail/store/imap/DigestMd5Utils.java
index e6376a3..f78dbdf 100644
--- a/src/com/android/phone/common/mail/store/imap/DigestMd5Utils.java
+++ b/src/com/android/phone/common/mail/store/imap/DigestMd5Utils.java
@@ -19,12 +19,12 @@
 import android.annotation.Nullable;
 import android.util.ArrayMap;
 import android.util.Base64;
-import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.phone.common.mail.MailTransport;
 import com.android.phone.common.mail.MessagingException;
 import com.android.phone.common.mail.store.ImapStore;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
@@ -244,7 +244,7 @@
                     }
                 }
             } catch (IndexOutOfBoundsException e) {
-                Log.e(TAG, e.toString());
+                VvmLog.e(TAG, e.toString());
                 return null;
             }
             return mResult;
diff --git a/src/com/android/phone/common/mail/store/imap/ImapConstants.java b/src/com/android/phone/common/mail/store/imap/ImapConstants.java
index 9e6e247..a2eab13 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapConstants.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapConstants.java
@@ -98,6 +98,32 @@
     public static final String NIL = "NIL";
 
     /**
+     * NO responses
+     */
+    public static final String NO_COMMAND_NOT_ALLOWED = "command not allowed";
+    public static final String NO_RESERVATION_FAILED = "reservation failed";
+    public static final String NO_APPLICATION_ERROR = "application error";
+    public static final String NO_INVALID_PARAMETER = "invalid parameter";
+    public static final String NO_INVALID_COMMAND = "invalid command";
+    public static final String NO_UNKNOWN_COMMAND = "unknown command";
+    // AUTHENTICATE
+    // The subscriber can not be located in the system.
+    public static final String NO_UNKNOWN_USER = "unknown user";
+    // The Client Type or Protocol Version is unknown.
+    public static final String NO_UNKNOWN_CLIENT = "unknown client";
+    // The password received from the client does not match the password defined in the subscriber's profile.
+    public static final String NO_INVALID_PASSWORD = "invalid password";
+    // The subscriber's mailbox has not yet been initialised via the TUI
+    public static final String NO_MAILBOX_NOT_INITIALIZED = "mailbox not initialized";
+    // The subscriber has not been provisioned for the VVM service.
+    public static final String NO_SERVICE_IS_NOT_PROVISIONED =
+            "service is not provisioned";
+    // The subscriber is provisioned for the VVM service but the VVM service is currently not active
+    public static final String NO_SERVICE_IS_NOT_ACTIVATED = "service is not activated";
+    // The Voice Mail Blocked flag in the subscriber's profile is set to YES.
+    public static final String NO_USER_IS_BLOCKED = "user is blocked";
+
+    /**
      * extensions
      */
     public static final String GETQUOTA = "GETQUOTA";
@@ -105,11 +131,6 @@
     public static final String QUOTAROOT = "QUOTAROOT";
     public static final String QUOTA = "QUOTA";
 
-    /** response codes within IMAP responses */
-    public static final String EXPIRED = "EXPIRED";
-    public static final String AUTHENTICATIONFAILED = "AUTHENTICATIONFAILED";
-    public static final String UNAVAILABLE = "UNAVAILABLE";
-
     /**
      * capabilities
      */
diff --git a/src/com/android/phone/common/mail/store/imap/ImapMemoryLiteral.java b/src/com/android/phone/common/mail/store/imap/ImapMemoryLiteral.java
index aac66c2..4811590 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapMemoryLiteral.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapMemoryLiteral.java
@@ -16,9 +16,8 @@
 
 package com.android.phone.common.mail.store.imap;
 
-import android.util.Log;
-
 import com.android.phone.common.mail.FixedLengthInputStream;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -45,7 +44,7 @@
             pos += read;
         }
         if (pos != mData.length) {
-            Log.w(TAG, "");
+            VvmLog.w(TAG, "length mismatch");
         }
     }
 
@@ -60,7 +59,7 @@
         try {
             return new String(mData, "US-ASCII");
         } catch (UnsupportedEncodingException e) {
-            Log.e(TAG, "Unsupported encoding: ", e);
+            VvmLog.e(TAG, "Unsupported encoding: ", e);
         }
         return null;
     }
diff --git a/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java b/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
index d0413df..a6d2df6 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
@@ -22,6 +22,7 @@
 import com.android.phone.common.mail.FixedLengthInputStream;
 import com.android.phone.common.mail.MessagingException;
 import com.android.phone.common.mail.PeekableInputStream;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -83,9 +84,7 @@
 
     private static IOException newEOSException() {
         final String message = "End of stream reached";
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, message);
-        }
+        VvmLog.d(TAG, message);
         return new IOException(message);
     }
 
@@ -144,9 +143,6 @@
         ImapResponse response = null;
         try {
             response = parseResponse();
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "<<< " + response.toString());
-            }
         } catch (RuntimeException e) {
             // Parser crash -- log network activities.
             onParseError(e);
@@ -183,7 +179,7 @@
             }
         } catch (IOException ignore) {
         }
-        Log.w(TAG, "Exception detected: " + e.getMessage());
+        VvmLog.w(TAG, "Exception detected: " + e.getMessage());
     }
 
     /**
diff --git a/src/com/android/phone/common/mail/store/imap/ImapSimpleString.java b/src/com/android/phone/common/mail/store/imap/ImapSimpleString.java
index 3d5263b..9d65236 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapSimpleString.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapSimpleString.java
@@ -16,7 +16,7 @@
 
 package com.android.phone.common.mail.store.imap;
 
-import android.util.Log;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -49,7 +49,7 @@
         try {
             return new ByteArrayInputStream(mString.getBytes("US-ASCII"));
         } catch (UnsupportedEncodingException e) {
-            Log.e(TAG, "Unsupported encoding: ", e);
+            VvmLog.e(TAG, "Unsupported encoding: ", e);
         }
         return null;
     }
diff --git a/src/com/android/phone/common/mail/store/imap/ImapString.java b/src/com/android/phone/common/mail/store/imap/ImapString.java
index a33ba24..dd7133c 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapString.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapString.java
@@ -16,7 +16,7 @@
 
 package com.android.phone.common.mail.store.imap;
 
-import android.util.Log;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -137,7 +137,7 @@
             mParsedDate = DATE_TIME_FORMAT.parse(getString());
             return true;
         } catch (ParseException e) {
-            Log.w("ImapString", getString() + " can't be parsed as a date.");
+            VvmLog.w("ImapString", getString() + " can't be parsed as a date.");
             return false;
         }
     }
diff --git a/src/com/android/phone/common/mail/utils/LogUtils.java b/src/com/android/phone/common/mail/utils/LogUtils.java
index 711af9b..6bd7be6 100644
--- a/src/com/android/phone/common/mail/utils/LogUtils.java
+++ b/src/com/android/phone/common/mail/utils/LogUtils.java
@@ -20,6 +20,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.util.List;
 import java.util.regex.Pattern;
@@ -184,7 +185,7 @@
      */
     public static int v(String tag, String format, Object... args) {
         if (isLoggable(tag, VERBOSE)) {
-            return Log.v(tag, String.format(format, args));
+            return VvmLog.v(tag, String.format(format, args));
         }
         return 0;
     }
@@ -202,7 +203,7 @@
      */
     public static int v(String tag, Throwable tr, String format, Object... args) {
         if (isLoggable(tag, VERBOSE)) {
-            return Log.v(tag, String.format(format, args), tr);
+            return VvmLog.v(tag, String.format(format, args), tr);
         }
         return 0;
     }
@@ -219,7 +220,7 @@
      */
     public static int d(String tag, String format, Object... args) {
         if (isLoggable(tag, DEBUG)) {
-            return Log.d(tag, String.format(format, args));
+            return VvmLog.d(tag, String.format(format, args));
         }
         return 0;
     }
@@ -237,7 +238,7 @@
      */
     public static int d(String tag, Throwable tr, String format, Object... args) {
         if (isLoggable(tag, DEBUG)) {
-            return Log.d(tag, String.format(format, args), tr);
+            return VvmLog.d(tag, String.format(format, args), tr);
         }
         return 0;
     }
@@ -254,7 +255,7 @@
      */
     public static int i(String tag, String format, Object... args) {
         if (isLoggable(tag, INFO)) {
-            return Log.i(tag, String.format(format, args));
+            return VvmLog.i(tag, String.format(format, args));
         }
         return 0;
     }
@@ -272,7 +273,7 @@
      */
     public static int i(String tag, Throwable tr, String format, Object... args) {
         if (isLoggable(tag, INFO)) {
-            return Log.i(tag, String.format(format, args), tr);
+            return VvmLog.i(tag, String.format(format, args), tr);
         }
         return 0;
     }
@@ -289,7 +290,7 @@
      */
     public static int w(String tag, String format, Object... args) {
         if (isLoggable(tag, WARN)) {
-            return Log.w(tag, String.format(format, args));
+            return VvmLog.w(tag, String.format(format, args));
         }
         return 0;
     }
@@ -307,7 +308,7 @@
      */
     public static int w(String tag, Throwable tr, String format, Object... args) {
         if (isLoggable(tag, WARN)) {
-            return Log.w(tag, String.format(format, args), tr);
+            return VvmLog.w(tag, String.format(format, args), tr);
         }
         return 0;
     }
@@ -324,7 +325,7 @@
      */
     public static int e(String tag, String format, Object... args) {
         if (isLoggable(tag, ERROR)) {
-            return Log.e(tag, String.format(format, args));
+            return VvmLog.e(tag, String.format(format, args));
         }
         return 0;
     }
@@ -342,7 +343,7 @@
      */
     public static int e(String tag, Throwable tr, String format, Object... args) {
         if (isLoggable(tag, ERROR)) {
-            return Log.e(tag, String.format(format, args), tr);
+            return VvmLog.e(tag, String.format(format, args), tr);
         }
         return 0;
     }
@@ -362,7 +363,7 @@
      *            additional arguments are ignored.
      */
     public static int wtf(String tag, String format, Object... args) {
-        return Log.wtf(tag, String.format(format, args), new Error());
+        return VvmLog.wtf(tag, String.format(format, args), new Error());
     }
 
     /**
@@ -381,7 +382,7 @@
      *            additional arguments are ignored.
      */
     public static int wtf(String tag, Throwable tr, String format, Object... args) {
-        return Log.wtf(tag, String.format(format, args), tr);
+        return VvmLog.wtf(tag, String.format(format, args), tr);
     }
 
 
diff --git a/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java b/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
index d960dc4..e4230ff 100644
--- a/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
+++ b/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
@@ -35,6 +35,7 @@
 import com.android.phone.common.mail.MessagingException;
 import com.android.phone.vvm.omtp.OmtpConstants;
 import com.android.phone.vvm.omtp.OmtpConstants.ChangePinResult;
+import com.android.phone.vvm.omtp.OmtpEvents;
 import com.android.phone.vvm.omtp.imap.ImapHelper;
 import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
 
@@ -192,6 +193,7 @@
                     // Wipe the default old PIN so the old PIN input box will be shown to the user
                     // on the next time.
                     setDefaultOldPIN(mContext, mPhoneAccountHandle, null);
+                    helper.handleEvent(OmtpEvents.CONFIG_PIN_SET);
                 }
             } catch (MessagingException e) {
                 finishPinChange();
diff --git a/src/com/android/phone/vvm/omtp/DefaultOmtpEventHandler.java b/src/com/android/phone/vvm/omtp/DefaultOmtpEventHandler.java
index 6816d4c..6837bf5 100644
--- a/src/com/android/phone/vvm/omtp/DefaultOmtpEventHandler.java
+++ b/src/com/android/phone/vvm/omtp/DefaultOmtpEventHandler.java
@@ -22,7 +22,6 @@
 
 import com.android.phone.VoicemailStatus;
 import com.android.phone.vvm.omtp.OmtpEvents.Type;
-import com.android.services.telephony.Log;
 
 public class DefaultOmtpEventHandler {
 
@@ -43,7 +42,7 @@
                 handleOtherEvent(context, subId, event);
                 break;
             default:
-                Log.wtf(TAG, "invalid event type " + event.getType() + " for " + event);
+                VvmLog.wtf(TAG, "invalid event type " + event.getType() + " for " + event);
         }
     }
 
@@ -51,13 +50,14 @@
             OmtpEvents event) {
         switch (event) {
             case CONFIG_REQUEST_STATUS_SUCCESS:
+            case CONFIG_PIN_SET:
                 VoicemailStatus.edit(context, subId)
                         .setConfigurationState(VoicemailContract.Status.CONFIGURATION_STATE_OK)
                         .setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_OK)
                         .apply();
                 break;
             default:
-                Log.wtf(TAG, "invalid configuration event " + event);
+                VvmLog.wtf(TAG, "invalid configuration event " + event);
         }
     }
 
@@ -103,6 +103,13 @@
                         .apply();
                 break;
             case DATA_BAD_IMAP_CREDENTIAL:
+            case DATA_AUTH_UNKNOWN_USER:
+            case DATA_AUTH_UNKNOWN_DEVICE:
+            case DATA_AUTH_INVALID_PASSWORD:
+            case DATA_AUTH_MAILBOX_NOT_INITIALIZED:
+            case DATA_AUTH_SERVICE_NOT_PROVISIONED:
+            case DATA_AUTH_SERVICE_NOT_ACTIVATED:
+            case DATA_AUTH_USER_IS_BLOCKED:
                 VoicemailStatus.edit(context, subId)
                         .setDataChannelState(
                                 VoicemailContract.Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION)
@@ -120,7 +127,7 @@
                 break;
 
             default:
-                Log.wtf(TAG, "invalid data channel event " + event);
+                VvmLog.wtf(TAG, "invalid data channel event " + event);
         }
     }
 
@@ -139,7 +146,7 @@
                         .apply();
                 break;
             default:
-                Log.wtf(TAG, "invalid notification channel event " + event);
+                VvmLog.wtf(TAG, "invalid notification channel event " + event);
         }
     }
 
@@ -154,7 +161,7 @@
                         .apply();
                 break;
             default:
-                Log.wtf(TAG, "invalid other event " + event);
+                VvmLog.wtf(TAG, "invalid other event " + event);
         }
     }
 }
diff --git a/src/com/android/phone/vvm/omtp/LocalLogHelper.java b/src/com/android/phone/vvm/omtp/LocalLogHelper.java
deleted file mode 100644
index 13a55c0..0000000
--- a/src/com/android/phone/vvm/omtp/LocalLogHelper.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import android.util.LocalLog;
-
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Helper methods for adding to OMTP visual voicemail local logs.
- */
-public class LocalLogHelper {
-
-    private static final int MAX_OMTP_VVM_LOGS = 100;
-
-    private static final LocalLog sLocalLog = new LocalLog(MAX_OMTP_VVM_LOGS);
-
-    public static void log(String tag, String log) {
-        sLocalLog.log(tag + ": " + log);
-    }
-
-    public static void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
-        IndentingPrintWriter indentingPrintWriter = new IndentingPrintWriter(printwriter, "  ");
-        indentingPrintWriter.println("OmtpVvm:");
-        indentingPrintWriter.increaseIndent();
-        sLocalLog.dump(fd, indentingPrintWriter, args);
-        indentingPrintWriter.decreaseIndent();
-    }
-}
diff --git a/src/com/android/phone/vvm/omtp/OmtpEvents.java b/src/com/android/phone/vvm/omtp/OmtpEvents.java
index 6de692e..f42db72 100644
--- a/src/com/android/phone/vvm/omtp/OmtpEvents.java
+++ b/src/com/android/phone/vvm/omtp/OmtpEvents.java
@@ -31,43 +31,55 @@
     // Configuration State
     CONFIG_REQUEST_STATUS_SUCCESS(Type.CONFIGURATION, true),
 
+    CONFIG_PIN_SET(Type.CONFIGURATION, true),
+    // The voicemail PIN is replaced with a generated PIN, user should change it.
+    CONFIG_DEFAULT_PIN_REPLACED(Type.CONFIGURATION, true),
+
     // Data channel State
 
     // Successfully downloaded/uploaded data from the server, which means the data channel is clear.
     DATA_IMAP_OPERATION_COMPLETED(Type.DATA_CHANNEL, true),
-
     // The port provided in the STATUS SMS is invalid.
-    DATA_INVALID_PORT(Type.DATA_CHANNEL, false),
+    DATA_INVALID_PORT(Type.DATA_CHANNEL),
     // No connection to the internet, and the carrier requires cellular data
-    DATA_NO_CONNECTION_CELLULAR_REQUIRED(Type.DATA_CHANNEL, false),
+    DATA_NO_CONNECTION_CELLULAR_REQUIRED(Type.DATA_CHANNEL),
     // No connection to the internet.
-    DATA_NO_CONNECTION(Type.DATA_CHANNEL, false),
+    DATA_NO_CONNECTION(Type.DATA_CHANNEL),
     // Address lookup for the server hostname failed. DNS error?
-    DATA_CANNOT_RESOLVE_HOST_ON_NETWORK(Type.DATA_CHANNEL, false),
+    DATA_CANNOT_RESOLVE_HOST_ON_NETWORK(Type.DATA_CHANNEL),
     // All destination address that resolves to the server hostname are rejected or timed out
-    DATA_ALL_SOCKET_CONNECTION_FAILED(Type.DATA_CHANNEL, false),
+    DATA_ALL_SOCKET_CONNECTION_FAILED(Type.DATA_CHANNEL),
     // Failed to establish SSL with the server, either with a direct SSL connection or by
     // STARTTLS command
-    DATA_CANNOT_ESTABLISH_SSL_SESSION(Type.DATA_CHANNEL, false),
+    DATA_CANNOT_ESTABLISH_SSL_SESSION(Type.DATA_CHANNEL),
     // Identity of the server cannot be verified.
-    DATA_SSL_INVALID_HOST_NAME(Type.DATA_CHANNEL, false),
+    DATA_SSL_INVALID_HOST_NAME(Type.DATA_CHANNEL),
     // The server rejected our username/password
-    DATA_BAD_IMAP_CREDENTIAL(Type.DATA_CHANNEL, false),
+    DATA_BAD_IMAP_CREDENTIAL(Type.DATA_CHANNEL),
+
+    DATA_AUTH_UNKNOWN_USER(Type.DATA_CHANNEL),
+    DATA_AUTH_UNKNOWN_DEVICE(Type.DATA_CHANNEL),
+    DATA_AUTH_INVALID_PASSWORD(Type.DATA_CHANNEL),
+    DATA_AUTH_MAILBOX_NOT_INITIALIZED(Type.DATA_CHANNEL),
+    DATA_AUTH_SERVICE_NOT_PROVISIONED(Type.DATA_CHANNEL),
+    DATA_AUTH_SERVICE_NOT_ACTIVATED(Type.DATA_CHANNEL),
+    DATA_AUTH_USER_IS_BLOCKED(Type.DATA_CHANNEL),
+
     // A command to the server didn't result with an "OK" or continuation request
-    DATA_REJECTED_SERVER_RESPONSE(Type.DATA_CHANNEL, false),
+    DATA_REJECTED_SERVER_RESPONSE(Type.DATA_CHANNEL),
     // The server did not greet us with a "OK", possibly not a IMAP server.
-    DATA_INVALID_INITIAL_SERVER_RESPONSE(Type.DATA_CHANNEL, false),
+    DATA_INVALID_INITIAL_SERVER_RESPONSE(Type.DATA_CHANNEL),
     // An IOException occurred while trying to open an ImapConnection
     // TODO: reduce scope
-    DATA_IOE_ON_OPEN(Type.DATA_CHANNEL, false),
+    DATA_IOE_ON_OPEN(Type.DATA_CHANNEL),
     // The SELECT command on a mailbox is rejected
-    DATA_MAILBOX_OPEN_FAILED(Type.DATA_CHANNEL, false),
+    DATA_MAILBOX_OPEN_FAILED(Type.DATA_CHANNEL),
     // An IOException has occurred
     // TODO: reduce scope
-    DATA_GENERIC_IMAP_IOE(Type.DATA_CHANNEL, false),
+    DATA_GENERIC_IMAP_IOE(Type.DATA_CHANNEL),
     // An SslException has occurred while opening an ImapConnection
     // TODO: reduce scope
-    DATA_SSL_EXCEPTION(Type.DATA_CHANNEL, false),
+    DATA_SSL_EXCEPTION(Type.DATA_CHANNEL),
 
     // Notification Channel
 
@@ -78,8 +90,20 @@
 
 
     // Other
-    OTHER_SOURCE_REMOVED(Type.OTHER, false);
+    OTHER_SOURCE_REMOVED(Type.OTHER, false),
 
+    // VVM3
+    VVM3_NEW_USER_SETUP_FAILED,
+    // Table 4. client internal error handling
+    VVM3_VMG_DNS_FAILURE,
+    VVM3_SPG_DNS_FAILURE,
+    VVM3_VMG_CONNECTION_FAILED,
+    VVM3_SPG_CONNECTION_FAILED,
+    VVM3_VMG_TIMEOUT,
+    VVM3_STATUS_SMS_TIMEOUT,
+
+    VVM3_SUBSCRIBER_PROVISIONED,
+    VVM3_SUBSCRIBER_BLOCKED;
 
     public static class Type {
 
@@ -103,6 +127,15 @@
         mIsSuccess = isSuccess;
     }
 
+    OmtpEvents(int type) {
+        mType = type;
+        mIsSuccess = false;
+    }
+
+    OmtpEvents() {
+        mType = Type.OTHER;
+        mIsSuccess = false;
+    }
 
     @Type.Values
     public int getType() {
diff --git a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
index dd394b4..02318c6 100644
--- a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
+++ b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
@@ -27,7 +27,6 @@
 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.VoicemailStatus;
@@ -327,29 +326,48 @@
     }
 
     public void handleEvent(OmtpEvents event) {
+        VvmLog.i(TAG, "OmtpEvent:" + event);
         if (mProtocol != null) {
             mProtocol.handleEvent(mContext, mSubId, event);
         }
     }
 
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("OmtpVvmCarrierConfigHelper [");
+        builder.append("subId: ").append(getSubId())
+                .append(", carrierConfig: ").append(mCarrierConfig != null)
+                .append(", telephonyConfig: ").append(mTelephonyConfig != null)
+                .append(", type: ").append(getVvmType())
+                .append(", destinationNumber: ").append(getDestinationNumber())
+                .append(", applicationPort: ").append(getApplicationPort())
+                .append(", sslPort: ").append(getSslPort())
+                .append(", isEnabledByDefault: ").append(isEnabledByDefault())
+                .append(", isCellularDataRequired: ").append(isCellularDataRequired())
+                .append(", isPrefetchEnabled: ").append(isPrefetchEnabled())
+                .append(", isLegacyModeEnabled: ").append(isLegacyModeEnabled())
+                .append("]");
+        return builder.toString();
+    }
+
     @Nullable
     private PersistableBundle getCarrierConfig() {
         if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
-            Log.w(TAG, "Invalid subscriptionId or subscriptionId not provided in intent.");
+            VvmLog
+                    .w(TAG, "Invalid subscriptionId or subscriptionId not provided in intent.");
             return null;
         }
 
         CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (carrierConfigManager == null) {
-            Log.w(TAG, "No carrier config service found.");
+            VvmLog.w(TAG, "No carrier config service found.");
             return null;
         }
 
         PersistableBundle config = carrierConfigManager.getConfigForSubId(mSubId);
 
         if (TextUtils.isEmpty(config.getString(CarrierConfigManager.KEY_VVM_TYPE_STRING))) {
-            Log.w(TAG, "Carrier config missing VVM type, ignoring.");
             return null;
         }
         return config;
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index 323b423..f22711a 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -21,13 +21,11 @@
 import android.content.pm.IPackageManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.util.Log;
 
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.PhoneConstants;
@@ -50,14 +48,9 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (UserHandle.myUserId() != UserHandle.USER_SYSTEM) {
-            Log.v(TAG, "Received broadcast for user that is not system.");
-            return;
-        }
-
         final String action = intent.getAction();
         if (action == null) {
-            Log.w(TAG, "Null action for intent.");
+            VvmLog.w(TAG, "Null action for intent.");
             return;
         }
 
@@ -65,7 +58,7 @@
             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
                         intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
-                    Log.i(TAG, "Sim removed, removing inactive accounts");
+                    VvmLog.i(TAG, "Sim removed, removing inactive accounts");
                     OmtpVvmSourceManager.getInstance(context).removeInactiveSources();
                 }
                 break;
@@ -74,14 +67,14 @@
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
 
                 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                    Log.i(TAG, "Received SIM change for invalid subscription id.");
+                    VvmLog.i(TAG, "Received SIM change for invalid subscription id.");
                     return;
                 }
-                Log.d(TAG, "Carrier config changed");
+                VvmLog.d(TAG, "Carrier config changed");
                 if (UserManager.get(context).isUserUnlocked() && !isCryptKeeperMode()) {
                     processSubId(context, subId);
                 } else {
-                    Log.d(TAG, "User locked, activation request delayed until unlock");
+                    VvmLog.d(TAG, "User locked, activation request delayed until unlock");
                     // After the device is unlocked, VvmBootCompletedReceiver will iterate through
                     // all call capable subIds, nothing need to be done here.
                 }
@@ -96,8 +89,8 @@
             PhoneAccountHandle phoneAccount = PhoneAccountHandleConverter.fromSubId(subId);
 
             if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(context, phoneAccount)) {
-                Log.i(TAG, "Sim state or carrier config changed: requesting"
-                        + " activation for " + phoneAccount.getId());
+                VvmLog.i(TAG, "Sim state or carrier config changed: requesting"
+                        + " activation for " + subId);
 
                 // Add a phone state listener so that changes to the communication channels
                 // can be recorded.
@@ -107,16 +100,17 @@
             } else {
                 if (carrierConfigHelper.isLegacyModeEnabled()) {
                     // SMS still need to be filtered under legacy mode.
+                    VvmLog.i(TAG, "activating SMS filter for legacy mode");
                     carrierConfigHelper.activateSmsFilter();
                 }
                 // It may be that the source was not registered to begin with but we want
                 // to run through the steps to remove the source just in case.
                 OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
-                Log.v(TAG, "Sim change for disabled account.");
+                VvmLog.v(TAG, "Sim change for disabled account.");
             }
         } else {
             String mccMnc = context.getSystemService(TelephonyManager.class).getSimOperator(subId);
-            Log.d(TAG,
+            VvmLog.d(TAG,
                     "visual voicemail not supported for carrier " + mccMnc + " on subId " + subId);
         }
     }
diff --git a/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java b/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
index b60c7b6..fe1f4cb 100644
--- a/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
+++ b/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
@@ -21,7 +21,6 @@
 import android.content.Intent;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
-import android.util.Log;
 
 import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
 
@@ -46,11 +45,11 @@
             return;
         }
 
-        Log.v(TAG, "processing subId list");
+        VvmLog.v(TAG, "processing subId list");
         for (PhoneAccountHandle handle : TelecomManager.from(context)
                 .getCallCapablePhoneAccounts()) {
             int subId = PhoneAccountHandleConverter.toSubId(handle);
-            Log.v(TAG, "processing subId " + subId);
+            VvmLog.v(TAG, "processing subId " + subId);
             SimChangeReceiver.processSubId(context, subId);
         }
     }
diff --git a/src/com/android/phone/vvm/omtp/VvmLog.java b/src/com/android/phone/vvm/omtp/VvmLog.java
new file mode 100644
index 0000000..82d42af
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/VvmLog.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Helper methods for adding to OMTP visual voicemail local logs.
+ */
+public class VvmLog {
+
+    private static final int MAX_OMTP_VVM_LOGS = 100;
+
+    private static final LocalLog sLocalLog = new LocalLog(MAX_OMTP_VVM_LOGS);
+
+    public static void log(String tag, String log) {
+        sLocalLog.log(tag + ": " + log);
+    }
+
+    public static void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
+        IndentingPrintWriter indentingPrintWriter = new IndentingPrintWriter(printwriter, "  ");
+        indentingPrintWriter.increaseIndent();
+        sLocalLog.dump(fd, indentingPrintWriter, args);
+        indentingPrintWriter.decreaseIndent();
+    }
+
+    public static int e(String tag, String log) {
+        log(tag, log);
+        return Log.e(tag, log);
+    }
+
+    public static int e(String tag, String log, Throwable e) {
+        log(tag, log + " " + e);
+        return Log.e(tag, log, e);
+    }
+
+    public static int w(String tag, String log) {
+        log(tag, log);
+        return Log.w(tag, log);
+    }
+
+    public static int w(String tag, String log, Throwable e) {
+        log(tag, log + " " + e);
+        return Log.w(tag, log, e);
+    }
+
+    public static int i(String tag, String log) {
+        log(tag, log);
+        return Log.i(tag, log);
+    }
+
+    public static int i(String tag, String log, Throwable e) {
+        log(tag, log + " " + e);
+        return Log.i(tag, log, e);
+    }
+
+    public static int d(String tag, String log) {
+        log(tag, log);
+        return Log.d(tag, log);
+    }
+
+    public static int d(String tag, String log, Throwable e) {
+        log(tag, log + " " + e);
+        return Log.d(tag, log, e);
+    }
+
+    public static int v(String tag, String log) {
+        log(tag, log);
+        return Log.v(tag, log);
+    }
+
+    public static int v(String tag, String log, Throwable e) {
+        log(tag, log + " " + e);
+        return Log.v(tag, log, e);
+    }
+
+    public static int wtf(String tag, String log) {
+        log(tag, log);
+        return Log.wtf(tag, log);
+    }
+
+    public static int wtf(String tag, String log, Throwable e) {
+        log(tag, log + " " + e);
+        return Log.wtf(tag, log, e);
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java b/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
index 08faba7..8a0495b 100644
--- a/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
+++ b/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
@@ -31,6 +31,9 @@
  * enabled dialer vvm sources.
  */
 public class VvmPackageInstallReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "VvmPkgInstallReceiver";
+
     @Override
     public void onReceive(Context context, Intent intent) {
         if (intent.getData() == null) {
@@ -59,6 +62,7 @@
                 // Force deactivate the client. The user can re-enable it in the settings.
                 // There are no need to update the settings for deactivation. At this point, if the
                 // default value is used it should be false because a carrier package is present.
+                VvmLog.i(TAG, "Carrier VVM package installed, disabling system VVM client");
                 OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
                 carrierConfigHelper.startDeactivation();
             }
diff --git a/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java b/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
index 3438de2..4b81fdb 100644
--- a/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
+++ b/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
@@ -20,7 +20,6 @@
 import android.telecom.PhoneAccountHandle;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
-import android.util.Log;
 
 import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneUtils;
@@ -65,16 +64,16 @@
                     new VoicemailStatusQueryHelper(mContext);
             if (voicemailStatusQueryHelper.isVoicemailSourceConfigured(mPhoneAccount)) {
                 if (!voicemailStatusQueryHelper.isNotificationsChannelActive(mPhoneAccount)) {
-                    Log.v(TAG, "Notifications channel is active for " + mPhoneAccount.getId());
+                    VvmLog
+                            .v(TAG, "Notifications channel is active for " + subId);
                     helper.handleEvent(OmtpEvents.NOTIFICATION_IN_SERVICE);
                     PhoneGlobals.getInstance().clearMwiIndicator(subId);
                 }
             }
 
             if (OmtpVvmSourceManager.getInstance(mContext).isVvmSourceRegistered(mPhoneAccount)) {
-                Log.v(TAG, "Signal returned: requesting resync for " + mPhoneAccount.getId());
-                LocalLogHelper.log(TAG,
-                        "Signal returned: requesting resync for " + mPhoneAccount.getId());
+                VvmLog
+                        .v(TAG, "Signal returned: requesting resync for " + subId);
                 // If the source is already registered, run a full sync in case something was missed
                 // while signal was down.
                 Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
@@ -82,16 +81,15 @@
                         true /* firstAttempt */);
                 mContext.startService(serviceIntent);
             } else {
-                Log.v(TAG, "Signal returned: reattempting activation for " + mPhoneAccount.getId());
-                LocalLogHelper.log(TAG,
-                        "Signal returned: reattempting activation for " + mPhoneAccount.getId());
+                VvmLog.v(TAG,
+                        "Signal returned: reattempting activation for " + subId);
                 // Otherwise initiate an activation because this means that an OMTP source was
                 // recognized but either the activation text was not successfully sent or a response
                 // was not received.
                 helper.startActivation();
             }
         } else {
-            Log.v(TAG, "Notifications channel is inactive for " + mPhoneAccount.getId());
+            VvmLog.v(TAG, "Notifications channel is inactive for " + subId);
             mContext.stopService(OmtpVvmSyncService.getSyncIntent(
                     mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, mPhoneAccount,
                     true /* firstAttempt */));
@@ -100,6 +98,9 @@
                 return;
             }
             helper.handleEvent(OmtpEvents.NOTIFICATION_SERVICE_LOST);
+            if (helper.isCellularDataRequired()) {
+                helper.handleEvent(OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED);
+            }
         }
         mPreviousState = state;
     }
diff --git a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
index 0095f53..fe3911c 100644
--- a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
+++ b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
@@ -30,10 +30,10 @@
 import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.phone.PhoneUtils;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
 import com.android.phone.vvm.omtp.imap.ImapHelper;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
 import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
@@ -45,7 +45,7 @@
 
     private static final String TAG = "FetchVoicemailReceiver";
 
-    final static String[] PROJECTION = new String[] {
+    final static String[] PROJECTION = new String[]{
             Voicemails.SOURCE_DATA,      // 0
             Voicemails.PHONE_ACCOUNT_ID, // 1
             Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, // 2
@@ -74,23 +74,28 @@
     @Override
     public void onReceive(final Context context, Intent intent) {
         if (VoicemailContract.ACTION_FETCH_VOICEMAIL.equals(intent.getAction())) {
+            VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL received");
             mContext = context;
             mContentResolver = context.getContentResolver();
             mUri = intent.getData();
 
             if (mUri == null) {
-                Log.w(TAG, VoicemailContract.ACTION_FETCH_VOICEMAIL + " intent sent with no data");
+                VvmLog.w(TAG,
+                        VoicemailContract.ACTION_FETCH_VOICEMAIL + " intent sent with no data");
                 return;
             }
 
             if (!context.getPackageName().equals(
                     mUri.getQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE))) {
                 // Ignore if the fetch request is for a voicemail not from this package.
+                VvmLog.e(TAG,
+                        "ACTION_FETCH_VOICEMAIL from foreign pacakge " + context.getPackageName());
                 return;
             }
 
             Cursor cursor = mContentResolver.query(mUri, PROJECTION, null, null, null);
             if (cursor == null) {
+                VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL query returned null");
                 return;
             }
             try {
@@ -103,7 +108,7 @@
                         accountId = telephonyManager.getSimSerialNumber();
 
                         if (TextUtils.isEmpty(accountId)) {
-                            Log.e(TAG, "Account null and no default sim found.");
+                            VvmLog.e(TAG, "Account null and no default sim found.");
                             return;
                         }
                     }
@@ -114,14 +119,14 @@
                             cursor.getString(PHONE_ACCOUNT_ID));
                     if (!OmtpVvmSourceManager.getInstance(context)
                             .isVvmSourceRegistered(mPhoneAccount)) {
-                        Log.w(TAG, "Account not registered - cannot retrieve message.");
+                        VvmLog.w(TAG, "Account not registered - cannot retrieve message.");
                         return;
                     }
 
                     int subId = PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount);
                     OmtpVvmCarrierConfigHelper carrierConfigHelper =
                             new OmtpVvmCarrierConfigHelper(context, subId);
-
+                    VvmLog.i(TAG, "Requesting network to fetch voicemail");
                     mNetworkCallback = new fetchVoicemailNetworkRequestCallback(context,
                             mPhoneAccount);
                     mNetworkCallback.requestNetwork();
@@ -153,15 +158,17 @@
             public void run() {
                 try {
                     while (mRetryCount > 0) {
+                        VvmLog.i(TAG, "fetching voicemail, retry count=" + mRetryCount);
                         ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
                         if (!imapHelper.isSuccessfullyInitialized()) {
-                            Log.w(TAG, "Can't retrieve Imap credentials.");
+                            VvmLog.w(TAG, "Can't retrieve Imap credentials.");
                             return;
                         }
 
                         boolean success = imapHelper.fetchVoicemailPayload(
                                 new VoicemailFetchedCallback(mContext, mUri), mUid);
                         if (!success && mRetryCount > 0) {
+                            VvmLog.i(TAG, "fetch voicemail failed, retrying");
                             mRetryCount--;
                         } else {
                             return;
diff --git a/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java b/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
index 3862d54..387ca5a 100644
--- a/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
+++ b/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
@@ -20,8 +20,8 @@
 import android.content.Context;
 import android.net.Uri;
 import android.provider.VoicemailContract.Voicemails;
-import android.util.Log;
 
+import com.android.phone.vvm.omtp.VvmLog;
 import com.android.phone.vvm.omtp.imap.VoicemailPayload;
 
 import libcore.io.IoUtils;
@@ -51,7 +51,7 @@
      * @param voicemailPayload The object containing the content data for the voicemail
      */
     public void setVoicemailContent(VoicemailPayload voicemailPayload) {
-        Log.d(TAG, String.format("Writing new voicemail content: %s", mUri));
+        VvmLog.d(TAG, String.format("Writing new voicemail content: %s", mUri));
         OutputStream outputStream = null;
 
         try {
@@ -61,7 +61,7 @@
                 outputStream.write(inputBytes);
             }
         } catch (IOException e) {
-            Log.w(TAG, String.format("File not found for %s", mUri));
+            VvmLog.w(TAG, String.format("File not found for %s", mUri));
             return;
         } finally {
             IoUtils.closeQuietly(outputStream);
@@ -73,7 +73,8 @@
         values.put(Voicemails.HAS_CONTENT, true);
         int updatedCount = mContentResolver.update(mUri, values, null, null);
         if (updatedCount != 1) {
-            Log.e(TAG, "Updating voicemail should have updated 1 row, was: " + updatedCount);
+            VvmLog
+                    .e(TAG, "Updating voicemail should have updated 1 row, was: " + updatedCount);
         }
     }
 }
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index ce9e9c3..216b6a4 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -25,7 +25,6 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.Voicemail;
 import android.util.Base64;
-import android.util.Log;
 
 import com.android.phone.PhoneUtils;
 import com.android.phone.VoicemailStatus;
@@ -50,6 +49,7 @@
 import com.android.phone.vvm.omtp.OmtpConstants.ChangePinResult;
 import com.android.phone.vvm.omtp.OmtpEvents;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
 import com.android.phone.vvm.omtp.fetch.VoicemailFetchedCallback;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSyncService.TranscriptionFetchedCallback;
 
@@ -135,11 +135,11 @@
         return mImapStore != null;
     }
 
-    public boolean isRoaming(){
+    public boolean isRoaming() {
         ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         NetworkInfo info = connectivityManager.getNetworkInfo(mNetwork);
-        if(info == null){
+        if (info == null) {
             return false;
         }
         return info.isRoaming();
@@ -153,12 +153,16 @@
         return mImapStore.getConnection();
     }
 
-    /** The caller thread will block until the method returns. */
+    /**
+     * The caller thread will block until the method returns.
+     */
     public boolean markMessagesAsRead(List<Voicemail> voicemails) {
         return setFlags(voicemails, Flag.SEEN);
     }
 
-    /** The caller thread will block until the method returns. */
+    /**
+     * The caller thread will block until the method returns.
+     */
     public boolean markMessagesAsDeleted(List<Voicemail> voicemails) {
         return setFlags(voicemails, Flag.DELETED);
     }
@@ -232,7 +236,7 @@
      * transcription exists.
      */
     private Voicemail getVoicemailFromMessageStructure(
-            MessageStructureWrapper messageStructureWrapper) throws MessagingException{
+            MessageStructureWrapper messageStructureWrapper) throws MessagingException {
         Message messageDetails = messageStructureWrapper.messageStructure;
 
         TranscriptionFetchedListener listener = new TranscriptionFetchedListener();
@@ -240,7 +244,7 @@
             FetchProfile fetchProfile = new FetchProfile();
             fetchProfile.add(messageStructureWrapper.transcriptionBodyPart);
 
-            mFolder.fetch(new Message[] {messageDetails}, fetchProfile, listener);
+            mFolder.fetch(new Message[]{messageDetails}, fetchProfile, listener);
         }
 
         // Found an audio attachment, this is a valid voicemail.
@@ -257,8 +261,8 @@
     }
 
     /**
-     * The "from" field of a visual voicemail IMAP message is the number of the caller who left
-     * the message. Extract this number from the list of "from" addresses.
+     * The "from" field of a visual voicemail IMAP message is the number of the caller who left the
+     * message. Extract this number from the list of "from" addresses.
      *
      * @param fromAddresses A list of addresses that comprise the "from" line.
      * @return The number of the voicemail sender.
@@ -297,7 +301,7 @@
 
         // The IMAP folder fetch method will call "messageRetrieved" on the listener when the
         // message is successfully retrieved.
-        mFolder.fetch(new Message[] {message}, fetchProfile, listener);
+        mFolder.fetch(new Message[]{message}, fetchProfile, listener);
         return listener.getMessageStructure();
     }
 
@@ -341,7 +345,7 @@
         FetchProfile fetchProfile = new FetchProfile();
         fetchProfile.add(FetchProfile.Item.BODY);
 
-        mFolder.fetch(new Message[] {message}, fetchProfile, listener);
+        mFolder.fetch(new Message[]{message}, fetchProfile, listener);
         return listener.getVoicemailPayload();
     }
 
@@ -367,7 +371,7 @@
 
                     // This method is called synchronously so the transcription will be populated
                     // in the listener once the next method is called.
-                    mFolder.fetch(new Message[] {message}, fetchProfile, listener);
+                    mFolder.fetch(new Message[]{message}, fetchProfile, listener);
                     callback.setVoicemailTranscription(listener.getVoicemailTranscription());
                 }
             }
@@ -481,7 +485,7 @@
             return;
         }
         if (quota.occupied == mQuotaOccupied && quota.total == mQuotaTotal) {
-            Log.v(TAG, "Quota hasn't changed");
+            VvmLog.v(TAG, "Quota hasn't changed");
             return;
         }
         mQuotaOccupied = quota.occupied;
@@ -493,17 +497,20 @@
                 .putInt(getSharedPrefsKey(PREF_KEY_QUOTA_OCCUPIED), mQuotaOccupied)
                 .putInt(getSharedPrefsKey(PREF_KEY_QUOTA_TOTAL), mQuotaTotal)
                 .apply();
-        Log.v(TAG, "Quota changed to " + mQuotaOccupied + "/" + mQuotaTotal);
+        VvmLog.v(TAG, "Quota changed to " + mQuotaOccupied + "/" + mQuotaTotal);
     }
+
     /**
-     * A wrapper to hold a message with its header details and the structure for transcriptions
-     * (so they can be fetched in the future).
+     * A wrapper to hold a message with its header details and the structure for transcriptions (so
+     * they can be fetched in the future).
      */
     public class MessageStructureWrapper {
+
         public Message messageStructure;
         public BodyPart transcriptionBodyPart;
 
-        public MessageStructureWrapper() { }
+        public MessageStructureWrapper() {
+        }
     }
 
     /**
@@ -511,6 +518,7 @@
      */
     private final class MessageStructureFetchedListener
             implements ImapFolder.MessageRetrievalListener {
+
         private MessageStructureWrapper mMessageStructure;
 
         public MessageStructureFetchedListener() {
@@ -542,7 +550,6 @@
          * @param message The IMAP message.
          * @return The MessageStructureWrapper object corresponding to an IMAP message and
          * transcription.
-         * @throws MessagingException
          */
         private MessageStructureWrapper getMessageOrNull(Message message)
                 throws MessagingException {
@@ -579,9 +586,12 @@
      * Listener for the message body being fetched.
      */
     private final class MessageBodyFetchedListener implements ImapFolder.MessageRetrievalListener {
+
         private VoicemailPayload mVoicemailPayload;
 
-        /** Returns the fetch voicemail payload. */
+        /**
+         * Returns the fetch voicemail payload.
+         */
         public VoicemailPayload getVoicemailPayload() {
             return mVoicemailPayload;
         }
@@ -602,18 +612,18 @@
         private VoicemailPayload getVoicemailPayloadFromMessage(Message message)
                 throws MessagingException, IOException {
             Multipart multipart = (Multipart) message.getBody();
+            List<String> mimeTypes = new ArrayList<>();
             for (int i = 0; i < multipart.getCount(); ++i) {
                 BodyPart bodyPart = multipart.getBodyPart(i);
                 String bodyPartMimeType = bodyPart.getMimeType().toLowerCase();
-                LogUtils.d(TAG, "bodyPart mime type: " + bodyPartMimeType);
-
+                mimeTypes.add(bodyPartMimeType);
                 if (bodyPartMimeType.startsWith("audio/")) {
                     byte[] bytes = getDataFromBody(bodyPart.getBody());
                     LogUtils.d(TAG, String.format("Fetched %s bytes of data", bytes.length));
                     return new VoicemailPayload(bodyPartMimeType, bytes);
                 }
             }
-            LogUtils.e(TAG, "No audio attachment found on this voicemail");
+            LogUtils.e(TAG, "No audio attachment found on this voicemail, mimeTypes:" + mimeTypes);
             return null;
         }
     }
@@ -623,9 +633,12 @@
      */
     private final class TranscriptionFetchedListener implements
             ImapFolder.MessageRetrievalListener {
+
         private String mVoicemailTranscription;
 
-        /** Returns the fetched voicemail transcription. */
+        /**
+         * Returns the fetched voicemail transcription.
+         */
         public String getVoicemailTranscription() {
             return mVoicemailTranscription;
         }
diff --git a/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java b/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java
index d265bd0..748fd39 100644
--- a/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java
+++ b/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java
@@ -18,9 +18,9 @@
 
 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.VvmLog;
 import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
 
 public class ProtocolHelper {
@@ -33,7 +33,7 @@
         int applicationPort = config.getApplicationPort();
         String destinationNumber = config.getDestinationNumber();
         if (TextUtils.isEmpty(destinationNumber)) {
-            Log.w(TAG, "No destination number for this carrier.");
+            VvmLog.w(TAG, "No destination number for this carrier.");
             return null;
         }
 
diff --git a/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java
index dbf38c2..5f54a50 100644
--- a/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java
+++ b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java
@@ -18,7 +18,8 @@
 
 import android.annotation.Nullable;
 import android.telephony.TelephonyManager;
-import android.util.Log;
+
+import com.android.phone.vvm.omtp.VvmLog;
 
 public class VisualVoicemailProtocolFactory {
 
@@ -39,7 +40,7 @@
             case VVM_TYPE_VVM3:
                 return new Vvm3Protocol();
             default:
-                Log.e(TAG, "Unexpected visual voicemail type: " + type);
+                VvmLog.e(TAG, "Unexpected visual voicemail type: " + type);
         }
         return null;
     }
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3EventHandler.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3EventHandler.java
new file mode 100644
index 0000000..1b4144a
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3EventHandler.java
@@ -0,0 +1,260 @@
+/*
+ * 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.IntDef;
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.util.Log;
+
+import com.android.phone.VoicemailStatus;
+import com.android.phone.settings.VoicemailChangePinDialogPreference;
+import com.android.phone.vvm.omtp.DefaultOmtpEventHandler;
+import com.android.phone.vvm.omtp.OmtpEvents;
+import com.android.phone.vvm.omtp.OmtpEvents.Type;
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handles {@link OmtpEvents} when {@link Vvm3Protocol} is being used. This handler writes custom
+ * error codes into the voicemail status table so support on the dialer side is required.
+ *
+ * TODO(b/29577838) disable VVM3 by default so support on system dialer can be ensured.
+ */
+public class Vvm3EventHandler {
+
+    private static final String TAG = "Vvm3EventHandler";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({VMS_DNS_FAILURE, VMG_DNS_FAILURE, SPG_DNS_FAILURE, VMS_NO_CELLULAR, VMG_NO_CELLULAR,
+            SPG_NO_CELLULAR, VMS_TIMEOUT, VMG_TIMEOUT, STATUS_SMS_TIMEOUT, SUBSCRIBER_BLOCKED,
+            UNKNOWN_USER, UNKNOWN_DEVICE, INVALID_PASSWORD, MAILBOX_NOT_INITIALIZED,
+            SERVICE_NOT_PROVISIONED, SERVICE_NOT_ACTIVATED, USER_BLOCKED, IMAP_GETQUOTA_ERROR,
+            IMAP_SELECT_ERROR, IMAP_ERROR, VMG_INTERNAL_ERROR, VMG_DB_ERROR,
+            VMG_COMMUNICATION_ERROR, SPG_URL_NOT_FOUND, VMG_UNKNOWN_ERROR, PIN_NOT_SET})
+    public @interface ErrorCode {
+
+    }
+
+    public static final int VMS_DNS_FAILURE = -9001;
+    public static final int VMG_DNS_FAILURE = -9002;
+    public static final int SPG_DNS_FAILURE = -9003;
+    public static final int VMS_NO_CELLULAR = -9004;
+    public static final int VMG_NO_CELLULAR = -9005;
+    public static final int SPG_NO_CELLULAR = -9006;
+    public static final int VMS_TIMEOUT = -9007;
+    public static final int VMG_TIMEOUT = -9008;
+    public static final int STATUS_SMS_TIMEOUT = -9009;
+
+    public static final int SUBSCRIBER_BLOCKED = -9990;
+    public static final int UNKNOWN_USER = -9991;
+    public static final int UNKNOWN_DEVICE = -9992;
+    public static final int INVALID_PASSWORD = -9993;
+    public static final int MAILBOX_NOT_INITIALIZED = -9994;
+    public static final int SERVICE_NOT_PROVISIONED = -9995;
+    public static final int SERVICE_NOT_ACTIVATED = -9996;
+    public static final int USER_BLOCKED = -9998;
+    public static final int IMAP_GETQUOTA_ERROR = -9997;
+    public static final int IMAP_SELECT_ERROR = -9989;
+    public static final int IMAP_ERROR = -9999;
+
+    public static final int VMG_INTERNAL_ERROR = -101;
+    public static final int VMG_DB_ERROR = -102;
+    public static final int VMG_COMMUNICATION_ERROR = -103;
+    public static final int SPG_URL_NOT_FOUND = -301;
+
+    // Non VVM3 codes:
+    public static final int VMG_UNKNOWN_ERROR = -1;
+    public static final int PIN_NOT_SET = -100;
+
+
+    public static void handleEvent(Context context, int subId, OmtpEvents event) {
+        boolean handled = false;
+        switch (event.getType()) {
+            case Type.CONFIGURATION:
+                handled = handleConfigurationEvent(context, subId, event);
+                break;
+            case Type.DATA_CHANNEL:
+                handled = handleDataChannelEvent(context, subId, event);
+                break;
+            case Type.NOTIFICATION_CHANNEL:
+                handled = handleNotificationChannelEvent(context, subId, event);
+                break;
+            case Type.OTHER:
+                handled = handleOtherEvent(context, subId, event);
+                break;
+            default:
+                com.android.services.telephony.Log
+                        .wtf(TAG, "invalid event type " + event.getType() + " for " + event);
+        }
+        if (!handled) {
+            DefaultOmtpEventHandler.handleEvent(context, subId, event);
+        }
+    }
+
+    private static boolean handleConfigurationEvent(Context context, int subId,
+            OmtpEvents event) {
+        switch (event) {
+            case CONFIG_REQUEST_STATUS_SUCCESS:
+                PhoneAccountHandle handle = PhoneAccountHandleConverter.fromSubId(subId);
+                if (VoicemailChangePinDialogPreference.getDefaultOldPin(context, handle) == null) {
+                    return false;
+                } else {
+                    postError(context, subId, PIN_NOT_SET);
+                }
+                break;
+            case CONFIG_DEFAULT_PIN_REPLACED:
+                postError(context, subId, PIN_NOT_SET);
+                break;
+            default:
+                return false;
+        }
+        return true;
+    }
+
+    private static boolean handleDataChannelEvent(Context context, int subId,
+            OmtpEvents event) {
+        switch (event) {
+            case DATA_NO_CONNECTION:
+            case DATA_NO_CONNECTION_CELLULAR_REQUIRED:
+                postError(context, subId, VMS_NO_CELLULAR);
+                break;
+            case DATA_CANNOT_RESOLVE_HOST_ON_NETWORK:
+                postError(context, subId, VMS_DNS_FAILURE);
+                break;
+            case DATA_BAD_IMAP_CREDENTIAL:
+                postError(context, subId, IMAP_ERROR);
+                break;
+            case DATA_AUTH_UNKNOWN_USER:
+                postError(context, subId, UNKNOWN_USER);
+                break;
+            case DATA_AUTH_UNKNOWN_DEVICE:
+                postError(context, subId, UNKNOWN_DEVICE);
+                break;
+            case DATA_AUTH_INVALID_PASSWORD:
+                postError(context, subId, INVALID_PASSWORD);
+                break;
+            case DATA_AUTH_MAILBOX_NOT_INITIALIZED:
+                postError(context, subId, MAILBOX_NOT_INITIALIZED);
+                break;
+            case DATA_AUTH_SERVICE_NOT_PROVISIONED:
+                postError(context, subId, SERVICE_NOT_PROVISIONED);
+                break;
+            case DATA_AUTH_SERVICE_NOT_ACTIVATED:
+                postError(context, subId, SERVICE_NOT_ACTIVATED);
+                break;
+            case DATA_AUTH_USER_IS_BLOCKED:
+                postError(context, subId, USER_BLOCKED);
+                break;
+
+            case DATA_INVALID_PORT:
+            case DATA_SSL_INVALID_HOST_NAME:
+            case DATA_CANNOT_ESTABLISH_SSL_SESSION:
+            case DATA_IOE_ON_OPEN:
+            case DATA_REJECTED_SERVER_RESPONSE:
+            case DATA_INVALID_INITIAL_SERVER_RESPONSE:
+            case DATA_SSL_EXCEPTION:
+            case DATA_ALL_SOCKET_CONNECTION_FAILED:
+                postError(context, subId, IMAP_ERROR);
+                break;
+
+            default:
+                return false;
+        }
+        return true;
+    }
+
+    private static boolean handleNotificationChannelEvent(Context context, int subId,
+            OmtpEvents event) {
+        return false;
+    }
+
+    private static boolean handleOtherEvent(Context context, int subId, OmtpEvents event) {
+        switch (event) {
+            case VVM3_NEW_USER_SETUP_FAILED:
+                postError(context, subId, MAILBOX_NOT_INITIALIZED);
+                break;
+            case VVM3_VMG_DNS_FAILURE:
+                postError(context, subId, VMG_DNS_FAILURE);
+                break;
+            case VVM3_SPG_DNS_FAILURE:
+                postError(context, subId, SPG_DNS_FAILURE);
+                break;
+            case VVM3_VMG_CONNECTION_FAILED:
+                postError(context, subId, VMG_NO_CELLULAR);
+                break;
+            case VVM3_SPG_CONNECTION_FAILED:
+                postError(context, subId, SPG_NO_CELLULAR);
+                break;
+            case VVM3_VMG_TIMEOUT:
+                postError(context, subId, VMG_TIMEOUT);
+                break;
+
+            case VVM3_SUBSCRIBER_PROVISIONED:
+                postError(context, subId, SERVICE_NOT_ACTIVATED);
+            case VVM3_SUBSCRIBER_BLOCKED:
+                postError(context, subId, SUBSCRIBER_BLOCKED);
+            default:
+                return false;
+        }
+        return true;
+    }
+
+    private static void postError(Context context, int subId, @ErrorCode int errorCode) {
+        VoicemailStatus.Editor editor = VoicemailStatus.edit(context, subId);
+
+        switch (errorCode) {
+            case VMG_DNS_FAILURE:
+            case SPG_DNS_FAILURE:
+            case VMG_NO_CELLULAR:
+            case SPG_NO_CELLULAR:
+            case VMG_TIMEOUT:
+            case SUBSCRIBER_BLOCKED:
+            case UNKNOWN_USER:
+            case UNKNOWN_DEVICE:
+            case INVALID_PASSWORD:
+            case MAILBOX_NOT_INITIALIZED:
+            case SERVICE_NOT_PROVISIONED:
+            case SERVICE_NOT_ACTIVATED:
+            case USER_BLOCKED:
+            case VMG_UNKNOWN_ERROR:
+            case SPG_URL_NOT_FOUND:
+            case VMG_INTERNAL_ERROR:
+            case VMG_DB_ERROR:
+            case VMG_COMMUNICATION_ERROR:
+            case PIN_NOT_SET:
+                editor.setConfigurationState(errorCode);
+                break;
+            case VMS_NO_CELLULAR:
+            case VMS_DNS_FAILURE:
+            case VMS_TIMEOUT:
+            case IMAP_GETQUOTA_ERROR:
+            case IMAP_SELECT_ERROR:
+            case IMAP_ERROR:
+                editor.setDataChannelState(errorCode);
+                break;
+            case STATUS_SMS_TIMEOUT:
+                editor.setNotificationChannelState(errorCode);
+                break;
+            default:
+                Log.wtf(TAG, "unknown error code: " + errorCode);
+        }
+        editor.apply();
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
index e62d1cf..df84034 100644
--- a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
@@ -17,17 +17,19 @@
 package com.android.phone.vvm.omtp.protocol;
 
 import android.annotation.Nullable;
+import android.content.Context;
 import android.net.Network;
 import android.os.Bundle;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.SmsManager;
-import android.util.Log;
 
 import com.android.phone.common.mail.MessagingException;
 import com.android.phone.settings.VisualVoicemailSettingsUtil;
 import com.android.phone.settings.VoicemailChangePinDialogPreference;
 import com.android.phone.vvm.omtp.OmtpConstants;
+import com.android.phone.vvm.omtp.OmtpEvents;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
 import com.android.phone.vvm.omtp.imap.ImapHelper;
 import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
 import com.android.phone.vvm.omtp.sms.StatusMessage;
@@ -53,20 +55,20 @@
 
     private static String ISO639_Spanish = "es";
 
-    private static String VVM3_VM_LANGUAGE_ENGLISH_STANDARD = "1";
-    private static String VVM3_VM_LANGUAGE_SPANISH_STANDARD = "2";
+    // Default prompt level when using the telephone user interface.
+    // Standard prompt when the user call into the voicemail, and no prompts when someone else is
+    // leaving a voicemail.
+    private static String VVM3_VM_LANGUAGE_ENGLISH_STANDARD_NO_GUEST_PROMPTS = "5";
+    private static String VVM3_VM_LANGUAGE_SPANISH_STANDARD_NO_GUEST_PROMPTS = "6";
 
     private static final int PIN_LENGTH = 6;
 
-    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.
+        VvmLog.i(TAG, "Activating");
         config.requestStatus();
     }
 
@@ -79,12 +81,12 @@
     @Override
     public void startProvisioning(PhoneAccountHandle phoneAccountHandle,
             OmtpVvmCarrierConfigHelper config, StatusMessage message, Bundle data) {
-        Log.i(TAG, "start vvm3 provisioning");
+        VvmLog.i(TAG, "start vvm3 provisioning");
         if ("U".equals(message.getProvisioningStatus())) {
-            Log.i(TAG, "Provisioning status: Unknown, subscribing");
+            VvmLog.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");
+            VvmLog.i(TAG, "setting up new user");
             VisualVoicemailSettingsUtil.setVisualVoicemailCredentialsFromStatusMessage(
                     config.getContext(), phoneAccountHandle, message);
             startProvisionNewUser(phoneAccountHandle, config, message);
@@ -98,6 +100,11 @@
     }
 
     @Override
+    public void handleEvent(Context context, int subId, OmtpEvents event) {
+        Vvm3EventHandler.handleEvent(context, subId, event);
+    }
+
+    @Override
     public String getCommand(String command) {
         if (command == OmtpConstants.IMAP_CHANGE_TUI_PWD_FORMAT) {
             return IMAP_CHANGE_TUI_PWD_FORMAT;
@@ -132,7 +139,7 @@
         @Override
         public void onAvailable(Network network) {
             super.onAvailable(network);
-            Log.i(TAG, "new user: network available");
+            VvmLog.i(TAG, "new user: network available");
             ImapHelper helper = new ImapHelper(mContext, mPhoneAccount, network);
 
             try {
@@ -143,45 +150,48 @@
                 if (Locale.getDefault().getLanguage()
                         .equals(new Locale(ISO639_Spanish).getLanguage())) {
                     // Spanish
-                    helper.changeVoicemailTuiLanguage(VVM3_VM_LANGUAGE_SPANISH_STANDARD);
+                    helper.changeVoicemailTuiLanguage(
+                            VVM3_VM_LANGUAGE_SPANISH_STANDARD_NO_GUEST_PROMPTS);
                 } else {
                     // English
-                    helper.changeVoicemailTuiLanguage(VVM3_VM_LANGUAGE_ENGLISH_STANDARD);
+                    helper.changeVoicemailTuiLanguage(
+                            VVM3_VM_LANGUAGE_ENGLISH_STANDARD_NO_GUEST_PROMPTS);
                 }
-                Log.i(TAG, "new user: language set");
+                VvmLog.i(TAG, "new user: language set");
 
                 if (setPin(helper)) {
                     // Only close new user tutorial if the PIN has been changed.
                     helper.closeNewUserTutorial();
-                    Log.i(TAG, "new user: NUT closed");
+                    VvmLog.i(TAG, "new user: NUT closed");
 
                     mConfig.requestStatus();
                 }
             } catch (MessagingException | IOException e) {
-                Log.e(TAG, e.toString());
+                helper.handleEvent(OmtpEvents.VVM3_NEW_USER_SETUP_FAILED);
+                VvmLog.e(TAG, e.toString());
             }
         }
 
         private boolean setPin(ImapHelper helper) throws IOException, MessagingException {
             String defaultPin = getDefaultPin();
             if (defaultPin == null) {
+                VvmLog.i(TAG, "cannot generate default PIN");
                 return false;
             }
 
             if (VoicemailChangePinDialogPreference.getDefaultOldPin(mContext, mPhoneAccount)
                     != null) {
                 // The pin was already set
+                VvmLog.i(TAG, "PIN already set");
                 return true;
             }
             String newPin = generatePin();
             if (helper.changePin(defaultPin, newPin) == OmtpConstants.CHANGE_PIN_SUCCESS) {
                 VoicemailChangePinDialogPreference
                         .setDefaultOldPIN(mContext, mPhoneAccount, newPin);
-
-                // TODO(b/29082418): set CONFIGURATION_STATE to VVM3_CONFIGURATION_PIN_NOT_SET
-                // to prompt the user to set the PIN
+                helper.handleEvent(OmtpEvents.CONFIG_DEFAULT_PIN_REPLACED);
             }
-            Log.i(TAG, "new user: PIN set");
+            VvmLog.i(TAG, "new user: PIN set");
             return true;
         }
 
@@ -192,12 +202,12 @@
             try {
                 String number = username.substring(0, username.indexOf('@'));
                 if (number.length() < 4) {
-                    Log.e(TAG, "unable to extract number from IMAP username");
+                    VvmLog.e(TAG, "unable to extract number from IMAP username");
                     return null;
                 }
                 return "1" + number.substring(number.length() - 4);
             } catch (StringIndexOutOfBoundsException e) {
-                Log.e(TAG, "unable to extract number from IMAP username");
+                VvmLog.e(TAG, "unable to extract number from IMAP username");
                 return null;
             }
 
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java
index c314ff5..980701e 100644
--- a/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java
@@ -26,10 +26,11 @@
 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.OmtpEvents;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
 import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
 import com.android.volley.AuthFailureError;
 import com.android.volley.Request;
@@ -151,6 +152,7 @@
         Assert.isNotMainThread();
         // Cellular data is required to subscribe.
         // processSubscription() is called after network is available.
+        VvmLog.i(TAG, "Subscribing");
         new Vvm3ProvisioningNetworkRequestCallback(mHelper, mHandle).requestNetwork();
     }
 
@@ -161,7 +163,7 @@
             String subscribeLink = findSubscribeLink(selfProvisionResponse);
             clickSubscribeLink(subscribeLink);
         } catch (ProvisioningException e) {
-            Log.e(TAG, e.toString());
+            VvmLog.e(TAG, e.toString());
         }
     }
 
@@ -169,6 +171,7 @@
      * 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);
     }
@@ -179,6 +182,8 @@
      * 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) {
@@ -200,11 +205,13 @@
         try {
             return future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
         } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            mHelper.handleEvent(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,
@@ -214,15 +221,16 @@
         try {
             future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
         } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            mHelper.handleEvent(OmtpEvents.VVM3_SPG_CONNECTION_FAILED);
             throw new ProvisioningException(e.toString());
         }
     }
 
     private String vvm3XmlRequest(String operation) throws ProvisioningException {
-        Log.d(TAG, "Sending vvm3XmlRequest for " + operation);
+        VvmLog.d(TAG, "Sending vvm3XmlRequest for " + operation);
         String voicemailManagementGateway = mData.getString(VMG_URL_KEY);
         if (voicemailManagementGateway == null) {
-            Log.e(TAG, "voicemailManagementGateway url unknown");
+            VvmLog.e(TAG, "voicemailManagementGateway url unknown");
             return null;
         }
         String transactionId = createTransactionId();
@@ -246,6 +254,7 @@
             }
             return response;
         } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            mHelper.handleEvent(OmtpEvents.VVM3_VMG_CONNECTION_FAILED);
             throw new ProvisioningException(e.toString());
         }
     }
@@ -285,7 +294,7 @@
         @Override
         public void onAvailable(Network network) {
             super.onAvailable(network);
-            Log.d(TAG, "provisioning: network available");
+            VvmLog.d(TAG, "provisioning: network available");
             mRequestQueue = Volley
                     .newRequestQueue(mContext, new NetworkSpecifiedHurlStack(network));
             processSubscription();
diff --git a/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
index 53422c4..ba5bd70 100644
--- a/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
+++ b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
@@ -21,12 +21,12 @@
 import android.os.Bundle;
 import android.provider.VoicemailContract;
 import android.telecom.PhoneAccountHandle;
-import android.util.Log;
 
 import com.android.internal.telephony.Phone;
 import com.android.phone.PhoneUtils;
 import com.android.phone.vvm.omtp.OmtpConstants;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
 
 /**
  * Class ot handle voicemail SMS under legacy mode
@@ -38,14 +38,14 @@
     private static final String TAG = "LegacyModeSmsHandler";
 
     public static void handle(Context context, Intent intent, PhoneAccountHandle handle) {
-        Log.v(TAG, "processing VVM SMS on legacy mode");
+        VvmLog.v(TAG, "processing VVM SMS on legacy mode");
         String eventType = intent.getExtras()
                 .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
         Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
 
         if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
             SyncMessage message = new SyncMessage(data);
-            Log.v(TAG, "Received SYNC sms for " + handle.getId() +
+            VvmLog.v(TAG, "Received SYNC sms for " + handle.getId() +
                     " with event " + message.getSyncTriggerEvent());
 
             switch (message.getSyncTriggerEvent()) {
@@ -55,7 +55,7 @@
                     // change.
                     // For some carriers new message count could be set to 0 even if there are still
                     // unread messages, to clear the message waiting indicator.
-                    Log.v(TAG, "updating MWI");
+                    VvmLog.v(TAG, "updating MWI");
                     Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(handle);
                     // Setting voicemail message count to non-zero will show the telephony voicemail
                     // notification, and zero will clear it.
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index 6bb053f..c930a98 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -25,14 +25,13 @@
 import android.provider.VoicemailContract;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.Voicemail;
-import android.util.Log;
 
 import com.android.phone.PhoneGlobals;
 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.OmtpEvents;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSyncService;
 import com.android.phone.vvm.omtp.sync.VoicemailsQueryHelper;
@@ -49,7 +48,7 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         if (!UserManager.get(context).isUserUnlocked()) {
-            Log.i(TAG, "Received message on locked device");
+            VvmLog.i(TAG, "Received message on locked device");
             // A full sync will happen after the device is unlocked, so nothing need to be done.
             return;
         }
@@ -59,7 +58,7 @@
         PhoneAccountHandle phone = PhoneAccountHandleConverter.fromSubId(subId);
 
         if (phone == null) {
-            Log.i(TAG, "Received message for null phone account");
+            VvmLog.i(TAG, "Received message for null phone account");
             return;
         }
 
@@ -68,7 +67,7 @@
             if (helper.isLegacyModeEnabled()) {
                 LegacyModeSmsHandler.handle(context, intent, phone);
             } else {
-                Log.i(TAG, "Received vvm message for disabled vvm source.");
+                VvmLog.i(TAG, "Received vvm message for disabled vvm source.");
             }
             return;
         }
@@ -80,23 +79,20 @@
         if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
             SyncMessage message = new SyncMessage(data);
 
-            Log.v(TAG, "Received SYNC sms for " + phone.getId() +
-                    " with event " + message.getSyncTriggerEvent());
-            LocalLogHelper.log(TAG, "Received SYNC sms for " + phone.getId() +
+            VvmLog.v(TAG, "Received SYNC sms for " + subId +
                     " with event " + message.getSyncTriggerEvent());
             processSync(phone, message);
         } else if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
-            Log.v(TAG, "Received STATUS sms for " + phone.getId());
-            LocalLogHelper.log(TAG, "Received Status sms for " + phone.getId());
+            VvmLog.v(TAG, "Received Status sms for " + subId);
             StatusMessage message = new StatusMessage(data);
             if (message.getProvisioningStatus().equals(OmtpConstants.SUBSCRIBER_READY)) {
                 updateSource(phone, subId, message);
             } else {
-                Log.v(TAG, "Subscriber not ready, start provisioning");
+                VvmLog.v(TAG, "Subscriber not ready, start provisioning");
                 mContext.startService(OmtpProvisioningService.getProvisionIntent(mContext, intent));
             }
         } else {
-            Log.e(TAG, "Unknown prefix: " + eventType);
+            VvmLog.e(TAG, "Unknown prefix: " + eventType);
         }
     }
 
@@ -138,7 +134,8 @@
                 // Not implemented in V1
                 break;
             default:
-               Log.e(TAG, "Unrecognized sync trigger event: " + message.getSyncTriggerEvent());
+                VvmLog.e(TAG,
+                        "Unrecognized sync trigger event: " + message.getSyncTriggerEvent());
                break;
         }
 
@@ -171,7 +168,7 @@
 
             PhoneGlobals.getInstance().clearMwiIndicator(subId);
         } else {
-            Log.e(TAG, "Visual voicemail not available for subscriber.");
+            VvmLog.e(TAG, "Visual voicemail not available for subscriber.");
         }
     }
 }
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageSender.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageSender.java
index 356f5e4..9a775f0 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageSender.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageSender.java
@@ -20,9 +20,10 @@
 import android.telephony.SmsManager;
 
 import com.android.phone.vvm.omtp.OmtpConstants;
-import com.android.services.telephony.Log;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.io.UnsupportedEncodingException;
+import java.util.Locale;
 
 /**
  * Send client originated OMTP messages to the OMTP server.
@@ -73,7 +74,8 @@
     protected void sendSms(String text, PendingIntent sentIntent) {
         // If application port is set to 0 then send simple text message, else send data message.
         if (mApplicationPort == 0) {
-            Log.v(TAG, String.format("Sending TEXT sms '%s' to %s", text, mDestinationNumber));
+            VvmLog
+                    .v(TAG, String.format("Sending TEXT sms '%s' to %s", text, mDestinationNumber));
             mSmsManager.sendTextMessageWithSelfPermissions(mDestinationNumber, null, text,
                     sentIntent, null, false);
         } else {
@@ -83,8 +85,9 @@
             } catch (UnsupportedEncodingException e) {
                 throw new IllegalStateException("Failed to encode: " + text);
             }
-            Log.v(TAG, String.format("Sending BINARY sms '%s' to %s:%d", text, mDestinationNumber,
-                    mApplicationPort));
+            VvmLog.v(TAG,
+                    String.format(Locale.US, "Sending BINARY sms '%s' to %s:%d", text,
+                            mDestinationNumber, mApplicationPort));
             mSmsManager.sendDataMessageWithSelfPermissions(mDestinationNumber, null,
                     mApplicationPort, data, sentIntent, null);
         }
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java
index 0902b6d..415fc91 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java
@@ -20,7 +20,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.provider.VoicemailContract;
-import android.util.Log;
+
+import com.android.phone.vvm.omtp.VvmLog;
 
 public class OmtpVvmSyncReceiver extends BroadcastReceiver {
 
@@ -29,7 +30,7 @@
     @Override
     public void onReceive(final Context context, Intent intent) {
         if (VoicemailContract.ACTION_SYNC_VOICEMAIL.equals(intent.getAction())) {
-            Log.v(TAG, "Sync intent received");
+            VvmLog.v(TAG, "Sync intent received");
             Intent syncIntent = OmtpVvmSyncService
                     .getSyncIntent(context, OmtpVvmSyncService.SYNC_FULL_SYNC, null, true);
             intent.putExtra(OmtpVvmSyncService.EXTRA_IS_MANUAL_SYNC, true);
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index c0411ec..74b1f66 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -27,14 +27,13 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.Voicemail;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.phone.PhoneUtils;
 import com.android.phone.VoicemailStatus;
 import com.android.phone.settings.VisualVoicemailSettingsUtil;
-import com.android.phone.vvm.omtp.LocalLogHelper;
 import com.android.phone.vvm.omtp.OmtpEvents;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
 import com.android.phone.vvm.omtp.fetch.VoicemailFetchedCallback;
 import com.android.phone.vvm.omtp.imap.ImapHelper;
 
@@ -169,21 +168,21 @@
     @Override
     protected void onHandleIntent(Intent intent) {
         if (intent == null) {
-            Log.d(TAG, "onHandleIntent: could not handle null intent");
+            VvmLog.d(TAG, "onHandleIntent: could not handle null intent");
             return;
         }
         String action = intent.getAction();
         PhoneAccountHandle phoneAccount = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT);
-        LocalLogHelper.log(TAG, "Sync requested: " + action +
+        VvmLog.log(TAG, "Sync requested: " + action +
                 " for all accounts: " + String.valueOf(phoneAccount == null));
 
         boolean isManualSync = intent.getBooleanExtra(EXTRA_IS_MANUAL_SYNC, false);
         Voicemail voicemail = intent.getParcelableExtra(EXTRA_VOICEMAIL);
         if (phoneAccount != null) {
-            Log.v(TAG, "Sync requested: " + action + " - for account: " + phoneAccount);
+            VvmLog.v(TAG, "Sync requested: " + action + " - for account: " + phoneAccount);
             setupAndSendRequest(phoneAccount, voicemail, action, isManualSync);
         } else {
-            Log.v(TAG, "Sync requested: " + action + " - for all accounts");
+            VvmLog.v(TAG, "Sync requested: " + action + " - for all accounts");
             OmtpVvmSourceManager vvmSourceManager =
                     OmtpVvmSourceManager.getInstance(this);
             Set<PhoneAccountHandle> sources = vvmSourceManager.getOmtpVvmSources();
@@ -196,7 +195,7 @@
     private void setupAndSendRequest(PhoneAccountHandle phoneAccount, Voicemail voicemail,
             String action, boolean isManualSync) {
         if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount)) {
-            Log.v(TAG, "Sync requested for disabled account");
+            VvmLog.v(TAG, "Sync requested for disabled account");
             return;
         }
 
@@ -208,7 +207,7 @@
                     : MINIMUM_MANUAL_SYNC_INTERVAL_MILLIS;
             if (currentTime - lastSyncTime < minimumInterval) {
                 // If it's been less than a minute since the last sync, bail.
-                Log.v(TAG, "Avoiding duplicate full sync: synced recently for "
+                VvmLog.v(TAG, "Avoiding duplicate full sync: synced recently for "
                         + phoneAccount.getId());
 
                 /**
@@ -238,7 +237,7 @@
             while (retryCount > 0) {
                 ImapHelper imapHelper = new ImapHelper(this, phoneAccount, network);
                 if (!imapHelper.isSuccessfullyInitialized()) {
-                    Log.w(TAG, "Can't retrieve Imap credentials.");
+                    VvmLog.w(TAG, "Can't retrieve Imap credentials.");
                     VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
                             phoneAccount);
                     return;
@@ -257,7 +256,7 @@
                 if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount) &&
                         !success) {
                     retryCount--;
-                    Log.v(TAG, "Retrying " + action);
+                    VvmLog.v(TAG, "Retrying " + action);
                 } else {
                     // Nothing more to do here, just exit.
                     VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
@@ -285,7 +284,7 @@
             downloadSuccess = download(imapHelper, account);
         }
 
-        Log.v(TAG, "upload succeeded: [" + String.valueOf(uploadSuccess)
+        VvmLog.v(TAG, "upload succeeded: [" + String.valueOf(uploadSuccess)
                 + "] download succeeded: [" + String.valueOf(downloadSuccess) + "]");
 
         boolean success = uploadSuccess && downloadSuccess;
@@ -330,9 +329,9 @@
             super.onAvailable(network);
             NetworkInfo info = getConnectivityManager().getNetworkInfo(network);
             if (info == null) {
-                Log.d(TAG, "Network Type: Unknown");
+                VvmLog.d(TAG, "Network Type: Unknown");
             } else {
-                Log.d(TAG, "Network Type: " + info.getTypeName());
+                VvmLog.d(TAG, "Network Type: " + info.getTypeName());
             }
 
             doSync(network, this, mPhoneAccount, mVoicemail, mAction);
@@ -428,7 +427,7 @@
         long retryInterval = VisualVoicemailSettingsUtil.getVisualVoicemailRetryInterval(this,
                 phoneAccount);
 
-        Log.v(TAG, "Retrying " + action + " in " + retryInterval + "ms");
+        VvmLog.v(TAG, "Retrying " + action + " in " + retryInterval + "ms");
 
         AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
         alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + retryInterval,
diff --git a/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java b/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
index 4e09527..11526ce 100644
--- a/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
+++ b/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
@@ -24,11 +24,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.telecom.PhoneAccountHandle;
-import android.util.Log;
 
 import com.android.phone.PhoneUtils;
 import com.android.phone.vvm.omtp.OmtpEvents;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
 
 /**
  * Base class for network request call backs for visual voicemail syncing with the Imap server. This
@@ -80,11 +80,11 @@
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
 
         if (mCarrierConfigHelper.isCellularDataRequired()) {
-            Log.d(TAG, "Transport type: CELLULAR");
+            VvmLog.d(TAG, "Transport type: CELLULAR");
             builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                     .setNetworkSpecifier(Integer.toString(mSubId));
         } else {
-            Log.d(TAG, "Transport type: ANY");
+            VvmLog.d(TAG, "Transport type: ANY");
         }
         return builder.build();
     }
@@ -96,7 +96,7 @@
     @Override
     @CallSuper
     public void onLost(Network network) {
-        Log.d(TAG, "onLost");
+        VvmLog.d(TAG, "onLost");
         mResultReceived = true;
         onFailed(NETWORK_REQUEST_FAILED_LOST);
     }
@@ -117,7 +117,7 @@
 
     public void requestNetwork() {
         if (mRequestSent == true) {
-            Log.e(TAG, "requestNetwork() called twice");
+            VvmLog.e(TAG, "requestNetwork() called twice");
             return;
         }
         mRequestSent = true;
@@ -138,7 +138,7 @@
     }
 
     public void releaseNetwork() {
-        Log.d(TAG, "releaseNetwork");
+        VvmLog.d(TAG, "releaseNetwork");
         getConnectivityManager().unregisterNetworkCallback(this);
     }
 
@@ -152,7 +152,7 @@
 
     @CallSuper
     public void onFailed(String reason) {
-        Log.d(TAG, "onFailed: " + reason);
+        VvmLog.d(TAG, "onFailed: " + reason);
         if (mCarrierConfigHelper.isCellularDataRequired()) {
             mCarrierConfigHelper.handleEvent(OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED);
         } else {
diff --git a/src/com/android/phone/vvm/omtp/utils/VvmDumpHandler.java b/src/com/android/phone/vvm/omtp/utils/VvmDumpHandler.java
new file mode 100644
index 0000000..227cf42
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/utils/VvmDumpHandler.java
@@ -0,0 +1,32 @@
+package com.android.phone.vvm.omtp.utils;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class VvmDumpHandler {
+
+    public static void dump(Context context, FileDescriptor fd, PrintWriter writer,
+            String[] args) {
+        IndentingPrintWriter indentedWriter = new IndentingPrintWriter(writer, "  ");
+        indentedWriter.println("******* OmtpVvm *******");
+        indentedWriter.println("======= Configs =======");
+        indentedWriter.increaseIndent();
+        for (PhoneAccountHandle handle : TelecomManager.from(context)
+                .getCallCapablePhoneAccounts()) {
+            int subId = PhoneAccountHandleConverter.toSubId(handle);
+            OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(context, subId);
+            indentedWriter.println(config.toString());
+        }
+        indentedWriter.decreaseIndent();
+        indentedWriter.println("======== Logs =========");
+        VvmLog.dump(fd, indentedWriter, args);
+    }
+}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index e495de5..c0917e5 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -759,6 +759,12 @@
         }
         mIsMultiParty = mOriginalConnection.isMultiparty();
 
+        if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) {
+            putExtra(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
+        } else {
+            removeExtras(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
+        }
+
         // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
         // should be executed *after* the above setters have run.
         updateState();
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 0ffef86..9f252a2 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -295,7 +295,9 @@
         // when voice RAT is OOS but Data RAT is present.
         int state = phone.getServiceState().getState();
         if (state == ServiceState.STATE_OUT_OF_SERVICE) {
-            if (phone.getServiceState().getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE) {
+            int dataNetType = phone.getServiceState().getDataNetworkType();
+            if (dataNetType == TelephonyManager.NETWORK_TYPE_LTE ||
+                    dataNetType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
                 state = phone.getServiceState().getDataRegState();
             }
         }