Move voicemail settings to dialer UI

The voicemail module should not contain any UI code.

Bug: 37258159
Test: DialerSettingsActivityTest,VoicemailSettingsFragmentTest. A future CL is refactoring VoicemailChangePinActivity into a fragment.
PiperOrigin-RevId: 174125949
Change-Id: I89cf6a083b0a0952332440d76e7ae0cb1c801931
diff --git a/java/com/android/dialer/app/settings/DialerSettingsActivity.java b/java/com/android/dialer/app/settings/DialerSettingsActivity.java
index 89c69ca..fbd6f48 100644
--- a/java/com/android/dialer/app/settings/DialerSettingsActivity.java
+++ b/java/com/android/dialer/app/settings/DialerSettingsActivity.java
@@ -41,8 +41,8 @@
 import com.android.dialer.compat.telephony.TelephonyManagerCompat;
 import com.android.dialer.configprovider.ConfigProviderBindings;
 import com.android.dialer.proguard.UsedByReflection;
+import com.android.dialer.voicemail.settings.VoicemailSettingsFragment;
 import com.android.voicemail.VoicemailClient;
-import com.android.voicemail.VoicemailComponent;
 import java.util.List;
 
 /** Activity for dialer settings. */
@@ -187,12 +187,10 @@
       LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "user not primary user");
       return;
     }
-    String voicemailSettingsFragment =
-        VoicemailComponent.get(this).getVoicemailClient().getSettingsFragment();
-    if (voicemailSettingsFragment == null) {
+    if (VERSION.SDK_INT < VERSION_CODES.O) {
       LogUtil.i(
           "DialerSettingsActivity.addVoicemailSettings",
-          "VoicemailClient does not provide settings");
+          "Dialer voicemail settings not supported by system");
       return;
     }
 
@@ -206,7 +204,8 @@
       voicemailSettings.fragment = PhoneAccountSelectionFragment.class.getName();
       Bundle bundle = new Bundle();
       bundle.putString(
-          PhoneAccountSelectionFragment.PARAM_TARGET_FRAGMENT, voicemailSettingsFragment);
+          PhoneAccountSelectionFragment.PARAM_TARGET_FRAGMENT,
+          VoicemailSettingsFragment.class.getName());
       bundle.putString(
           PhoneAccountSelectionFragment.PARAM_PHONE_ACCOUNT_HANDLE_KEY,
           VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE);
@@ -218,7 +217,7 @@
     } else {
       LogUtil.i(
           "DialerSettingsActivity.addVoicemailSettings", "showing single-SIM voicemail settings");
-      voicemailSettings.fragment = voicemailSettingsFragment;
+      voicemailSettings.fragment = VoicemailSettingsFragment.class.getName();
       Bundle bundle = new Bundle();
       bundle.putParcelable(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE, soleAccount);
       voicemailSettings.fragmentArguments = bundle;
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java
index 92c787d..ab269f6 100644
--- a/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java
@@ -30,6 +30,7 @@
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
 import com.android.dialer.util.CallUtil;
+import com.android.dialer.voicemail.settings.VoicemailChangePinActivity;
 import com.android.voicemail.VoicemailClient;
 import com.android.voicemail.VoicemailComponent;
 import java.util.Arrays;
@@ -147,10 +148,9 @@
           public void onClick(View v) {
             Logger.get(context)
                 .logImpression(DialerImpression.Type.VOICEMAIL_ALERT_SET_PIN_CLICKED);
-            context.startActivity(
-                VoicemailComponent.get(context)
-                    .getVoicemailClient()
-                    .getSetPinIntent(context, phoneAccountHandle));
+            Intent intent = new Intent(VoicemailChangePinActivity.ACTION_CHANGE_PIN);
+            intent.putExtra(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+            context.startActivity(intent);
           }
         });
   }
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java b/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java
index 96850ad..1092175 100644
--- a/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java
@@ -44,6 +44,7 @@
 import com.android.dialer.constants.Constants;
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
+import com.android.dialer.voicemail.settings.VoicemailSettingsFragment;
 import com.android.voicemail.VisualVoicemailTypeExtensions;
 import com.android.voicemail.VoicemailClient;
 import com.android.voicemail.VoicemailComponent;
@@ -154,11 +155,7 @@
                                 new ComponentName(context, Constants.get().getSettingsActivity()))
                             .setData(
                                 Uri.fromParts(
-                                    "header",
-                                    VoicemailComponent.get(context)
-                                        .getVoicemailClient()
-                                        .getSettingsFragment(),
-                                    null));
+                                    "header", VoicemailSettingsFragment.class.getName(), null));
                     context.startActivity(intent);
                   }
                 }),
diff --git a/java/com/android/dialer/voicemail/settings/AndroidManifest.xml b/java/com/android/dialer/voicemail/settings/AndroidManifest.xml
new file mode 100644
index 0000000..4a0784f
--- /dev/null
+++ b/java/com/android/dialer/voicemail/settings/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.dialer.voicemail.settings">
+
+  <application>
+    <activity
+        android:exported="false"
+        android:label="@string/voicemail_change_pin_preference_title"
+        android:name="com.android.dialer.voicemail.settings.VoicemailChangePinActivity"
+        android:parentActivityName="com.android.dialer.app.settings.DialerSettingsActivity"
+        android:theme="@style/SettingsStyle"
+        android:windowSoftInputMode="stateVisible|adjustResize">
+      <intent-filter>
+        <action android:name="com.android.dialer.action.CHANGE_PIN"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>
+    </activity>
+  </application>
+</manifest>
diff --git a/java/com/android/voicemail/impl/settings/VoicemailChangePinActivity.java b/java/com/android/dialer/voicemail/settings/VoicemailChangePinActivity.java
similarity index 72%
rename from java/com/android/voicemail/impl/settings/VoicemailChangePinActivity.java
rename to java/com/android/dialer/voicemail/settings/VoicemailChangePinActivity.java
index b22a765..4d53d61 100644
--- a/java/com/android/voicemail/impl/settings/VoicemailChangePinActivity.java
+++ b/java/com/android/dialer/voicemail/settings/VoicemailChangePinActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.voicemail.impl.settings;
+package com.android.dialer.voicemail.settings;
 
 import android.annotation.TargetApi;
 import android.app.Activity;
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnDismissListener;
-import android.net.Network;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Handler;
@@ -45,40 +44,44 @@
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 import android.widget.Toast;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DialerExecutor;
+import com.android.dialer.common.concurrent.DialerExecutor.Worker;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
 import com.android.dialer.logging.DialerImpression;
-import com.android.voicemail.impl.OmtpConstants;
-import com.android.voicemail.impl.OmtpConstants.ChangePinResult;
-import com.android.voicemail.impl.OmtpEvents;
-import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper;
-import com.android.voicemail.impl.R;
-import com.android.voicemail.impl.VisualVoicemailPreferences;
-import com.android.voicemail.impl.VoicemailStatus;
-import com.android.voicemail.impl.VvmLog;
-import com.android.voicemail.impl.imap.ImapHelper;
-import com.android.voicemail.impl.imap.ImapHelper.InitializingException;
-import com.android.voicemail.impl.mail.MessagingException;
-import com.android.voicemail.impl.sync.VvmNetworkRequestCallback;
-import com.android.voicemail.impl.utils.LoggerUtils;
+import com.android.dialer.logging.Logger;
+import com.android.voicemail.PinChanger;
+import com.android.voicemail.PinChanger.ChangePinResult;
+import com.android.voicemail.PinChanger.PinSpecification;
+import com.android.voicemail.VoicemailClient;
+import com.android.voicemail.VoicemailComponent;
+import java.lang.ref.WeakReference;
 
 /**
  * Dialog to change the voicemail PIN. The TUI (Telephony User Interface) PIN is used when accessing
  * traditional voicemail through phone call. The intent to launch this activity must contain {@link
- * #EXTRA_PHONE_ACCOUNT_HANDLE}
+ * VoicemailClient#PARAM_PHONE_ACCOUNT_HANDLE}
  */
 @TargetApi(VERSION_CODES.O)
 public class VoicemailChangePinActivity extends Activity
     implements OnClickListener, OnEditorActionListener, TextWatcher {
 
   private static final String TAG = "VmChangePinActivity";
-
-  public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "extra_phone_account_handle";
-
-  private static final String KEY_DEFAULT_OLD_PIN = "default_old_pin";
+  public static final String ACTION_CHANGE_PIN = "com.android.dialer.action.CHANGE_PIN";
 
   private static final int MESSAGE_HANDLE_RESULT = 1;
 
   private PhoneAccountHandle mPhoneAccountHandle;
-  private OmtpVvmCarrierConfigHelper mConfig;
+  private PinChanger mPinChanger;
+
+  private static class ChangePinParams {
+    PinChanger pinChanger;
+    PhoneAccountHandle phoneAccountHandle;
+    String oldPin;
+    String newPin;
+  }
+
+  private DialerExecutor<ChangePinParams> mChangePinExecutor;
 
   private int mPinMinLength;
   private int mPinMaxLength;
@@ -96,15 +99,7 @@
   private Button mCancelButton;
   private Button mNextButton;
 
-  private Handler mHandler =
-      new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-          if (message.what == MESSAGE_HANDLE_RESULT) {
-            mUiState.handleResult(VoicemailChangePinActivity.this, message.arg1);
-          }
-        }
-      };
+  private Handler mHandler = new ChangePinHandler(new WeakReference<>(this));
 
   private enum State {
     /**
@@ -139,7 +134,7 @@
 
       @Override
       public void handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result) {
-        if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
+        if (result == PinChanger.CHANGE_PIN_SUCCESS) {
           activity.updateState(State.EnterNewPin);
         } else {
           CharSequence message = activity.getChangePinResultMessage(result);
@@ -164,9 +159,9 @@
       @Override
       public void handleResult(
           final VoicemailChangePinActivity activity, @ChangePinResult int result) {
-        if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
+        if (result == PinChanger.CHANGE_PIN_SUCCESS) {
           activity.updateState(State.EnterNewPin);
-        } else if (result == OmtpConstants.CHANGE_PIN_SYSTEM_ERROR) {
+        } else if (result == PinChanger.CHANGE_PIN_SYSTEM_ERROR) {
           activity
               .getWindow()
               .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
@@ -179,13 +174,12 @@
                 }
               });
         } else {
-          VvmLog.e(TAG, "invalid default old PIN: " + activity.getChangePinResultMessage(result));
+          LogUtil.e(TAG, "invalid default old PIN: " + activity.getChangePinResultMessage(result));
           // If the default old PIN is rejected by the server, the PIN is probably changed
           // through other means, or the generated pin is invalid
           // Wipe the default old PIN so the old PIN input box will be shown to the user
           // on the next time.
-          setDefaultOldPIN(activity, activity.mPhoneAccountHandle, null);
-          activity.handleOmtpEvent(OmtpEvents.CONFIG_PIN_SET);
+          activity.mPinChanger.setScrambledPin(null);
           activity.updateState(State.EnterOldPin);
         }
       }
@@ -270,24 +264,22 @@
 
       @Override
       public void handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result) {
-        if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
+        if (result == PinChanger.CHANGE_PIN_SUCCESS) {
           // If the PIN change succeeded we no longer know what the old (current) PIN is.
           // Wipe the default old PIN so the old PIN input box will be shown to the user
           // on the next time.
-          setDefaultOldPIN(activity, activity.mPhoneAccountHandle, null);
-          activity.handleOmtpEvent(OmtpEvents.CONFIG_PIN_SET);
+          activity.mPinChanger.setScrambledPin(null);
 
           activity.finish();
-          LoggerUtils.logImpressionOnMainThread(
-              activity, DialerImpression.Type.VVM_CHANGE_PIN_COMPLETED);
+          Logger.get(activity).logImpression(DialerImpression.Type.VVM_CHANGE_PIN_COMPLETED);
           Toast.makeText(
                   activity, activity.getString(R.string.change_pin_succeeded), Toast.LENGTH_SHORT)
               .show();
         } else {
           CharSequence message = activity.getChangePinResultMessage(result);
-          VvmLog.i(TAG, "Change PIN failed: " + message);
+          LogUtil.i(TAG, "Change PIN failed: " + message);
           activity.showError(message);
-          if (result == OmtpConstants.CHANGE_PIN_MISMATCH) {
+          if (result == PinChanger.CHANGE_PIN_MISMATCH) {
             // Somehow the PIN has changed, prompt to enter the old PIN again.
             activity.updateState(State.EnterOldPin);
           } else {
@@ -336,8 +328,12 @@
   public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
 
-    mPhoneAccountHandle = getIntent().getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE);
-    mConfig = new OmtpVvmCarrierConfigHelper(this, mPhoneAccountHandle);
+    mPhoneAccountHandle =
+        getIntent().getParcelableExtra(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE);
+    mPinChanger =
+        VoicemailComponent.get(this)
+            .getVoicemailClient()
+            .createPinChanger(getApplicationContext(), mPhoneAccountHandle);
     setContentView(R.layout.voicemail_change_pin);
     setTitle(R.string.change_pin_title);
 
@@ -361,42 +357,27 @@
     mHintText = (TextView) view.findViewById(R.id.hintText);
     mErrorText = (TextView) view.findViewById(R.id.errorText);
 
-    if (isDefaultOldPinSet(this, mPhoneAccountHandle)) {
-      mOldPin = getDefaultOldPin(this, mPhoneAccountHandle);
+    if (isPinScrambled(this, mPhoneAccountHandle)) {
+      mOldPin = mPinChanger.getScrambledPin();
       updateState(State.VerifyOldPin);
     } else {
       updateState(State.EnterOldPin);
     }
-  }
 
-  private void handleOmtpEvent(OmtpEvents event) {
-    mConfig.handleEvent(getVoicemailStatusEditor(), event);
-  }
-
-  private VoicemailStatus.Editor getVoicemailStatusEditor() {
-    // This activity does not have any automatic retry mechanism, errors should be written right
-    // away.
-    return VoicemailStatus.edit(this, mPhoneAccountHandle);
+    mChangePinExecutor =
+        DialerExecutorComponent.get(this)
+            .dialerExecutorFactory()
+            .createUiTaskBuilder(getFragmentManager(), "changePin", new ChangePinWorker())
+            .onSuccess(this::sendResult)
+            .onFailure((tr) -> sendResult(PinChanger.CHANGE_PIN_SYSTEM_ERROR))
+            .build();
   }
 
   /** Extracts the pin length requirement sent by the server with a STATUS SMS. */
   private void readPinLength() {
-    VisualVoicemailPreferences preferences =
-        new VisualVoicemailPreferences(this, mPhoneAccountHandle);
-    // The OMTP pin length format is {min}-{max}
-    String[] lengths = preferences.getString(OmtpConstants.TUI_PASSWORD_LENGTH, "").split("-");
-    if (lengths.length == 2) {
-      try {
-        mPinMinLength = Integer.parseInt(lengths[0]);
-        mPinMaxLength = Integer.parseInt(lengths[1]);
-      } catch (NumberFormatException e) {
-        mPinMinLength = 0;
-        mPinMaxLength = 0;
-      }
-    } else {
-      mPinMinLength = 0;
-      mPinMaxLength = 0;
-    }
+    PinSpecification pinSpecification = mPinChanger.getPinSpecification();
+    mPinMinLength = pinSpecification.minLength;
+    mPinMaxLength = pinSpecification.maxLength;
   }
 
   @Override
@@ -464,21 +445,12 @@
    * After replacing the default PIN with a random PIN, call this to store the random PIN. The
    * stored PIN will be automatically entered when the user attempts to change the PIN.
    */
-  public static void setDefaultOldPIN(
-      Context context, PhoneAccountHandle phoneAccountHandle, String pin) {
-    new VisualVoicemailPreferences(context, phoneAccountHandle)
-        .edit()
-        .putString(KEY_DEFAULT_OLD_PIN, pin)
-        .apply();
-  }
-
-  public static boolean isDefaultOldPinSet(Context context, PhoneAccountHandle phoneAccountHandle) {
-    return getDefaultOldPin(context, phoneAccountHandle) != null;
-  }
-
-  private static String getDefaultOldPin(Context context, PhoneAccountHandle phoneAccountHandle) {
-    return new VisualVoicemailPreferences(context, phoneAccountHandle)
-        .getString(KEY_DEFAULT_OLD_PIN);
+  public static boolean isPinScrambled(Context context, PhoneAccountHandle phoneAccountHandle) {
+    return VoicemailComponent.get(context)
+            .getVoicemailClient()
+            .createPinChanger(context, phoneAccountHandle)
+            .getScrambledPin()
+        != null;
   }
 
   private String getCurrentPasswordInput() {
@@ -522,24 +494,24 @@
 
   /**
    * Get the corresponding message for the {@link ChangePinResult}.<code>result</code> must not
-   * {@link OmtpConstants#CHANGE_PIN_SUCCESS}
+   * {@link PinChanger#CHANGE_PIN_SUCCESS}
    */
   private CharSequence getChangePinResultMessage(@ChangePinResult int result) {
     switch (result) {
-      case OmtpConstants.CHANGE_PIN_TOO_SHORT:
+      case PinChanger.CHANGE_PIN_TOO_SHORT:
         return getString(R.string.vm_change_pin_error_too_short);
-      case OmtpConstants.CHANGE_PIN_TOO_LONG:
+      case PinChanger.CHANGE_PIN_TOO_LONG:
         return getString(R.string.vm_change_pin_error_too_long);
-      case OmtpConstants.CHANGE_PIN_TOO_WEAK:
+      case PinChanger.CHANGE_PIN_TOO_WEAK:
         return getString(R.string.vm_change_pin_error_too_weak);
-      case OmtpConstants.CHANGE_PIN_INVALID_CHARACTER:
+      case PinChanger.CHANGE_PIN_INVALID_CHARACTER:
         return getString(R.string.vm_change_pin_error_invalid);
-      case OmtpConstants.CHANGE_PIN_MISMATCH:
+      case PinChanger.CHANGE_PIN_MISMATCH:
         return getString(R.string.vm_change_pin_error_mismatch);
-      case OmtpConstants.CHANGE_PIN_SYSTEM_ERROR:
+      case PinChanger.CHANGE_PIN_SYSTEM_ERROR:
         return getString(R.string.vm_change_pin_error_system_error);
       default:
-        VvmLog.wtf(TAG, "Unexpected ChangePinResult " + result);
+        LogUtil.e(TAG, "Unexpected ChangePinResult " + result);
         return null;
     }
   }
@@ -571,57 +543,53 @@
     mProgressDialog.setMessage(getString(R.string.vm_change_pin_progress_message));
     mProgressDialog.show();
 
-    ChangePinNetworkRequestCallback callback = new ChangePinNetworkRequestCallback(oldPin, newPin);
-    callback.requestNetwork();
+    ChangePinParams params = new ChangePinParams();
+    params.pinChanger = mPinChanger;
+    params.phoneAccountHandle = mPhoneAccountHandle;
+    params.oldPin = oldPin;
+    params.newPin = newPin;
+
+    mChangePinExecutor.executeSerial(params);
   }
 
-  private class ChangePinNetworkRequestCallback extends VvmNetworkRequestCallback {
+  private void sendResult(@ChangePinResult int result) {
+    LogUtil.i(TAG, "Change PIN result: " + result);
+    if (mProgressDialog.isShowing()
+        && !VoicemailChangePinActivity.this.isDestroyed()
+        && !VoicemailChangePinActivity.this.isFinishing()) {
+      mProgressDialog.dismiss();
+    } else {
+      LogUtil.i(TAG, "Dialog not visible, not dismissing");
+    }
+    mHandler.obtainMessage(MESSAGE_HANDLE_RESULT, result, 0).sendToTarget();
+  }
 
-    private final String mOldPin;
-    private final String mNewPin;
+  private static class ChangePinHandler extends Handler {
 
-    public ChangePinNetworkRequestCallback(String oldPin, String newPin) {
-      super(
-          mConfig, mPhoneAccountHandle, VoicemailChangePinActivity.this.getVoicemailStatusEditor());
-      mOldPin = oldPin;
-      mNewPin = newPin;
+    private final WeakReference<VoicemailChangePinActivity> activityWeakReference;
+
+    private ChangePinHandler(WeakReference<VoicemailChangePinActivity> activityWeakReference) {
+      this.activityWeakReference = activityWeakReference;
     }
 
     @Override
-    public void onAvailable(Network network) {
-      super.onAvailable(network);
-      try (ImapHelper helper =
-          new ImapHelper(
-              VoicemailChangePinActivity.this,
-              mPhoneAccountHandle,
-              network,
-              getVoicemailStatusEditor())) {
-
-        @ChangePinResult int result = helper.changePin(mOldPin, mNewPin);
-        sendResult(result);
-      } catch (InitializingException | MessagingException e) {
-        VvmLog.e(TAG, "ChangePinNetworkRequestCallback: onAvailable: ", e);
-        sendResult(OmtpConstants.CHANGE_PIN_SYSTEM_ERROR);
+    public void handleMessage(Message message) {
+      VoicemailChangePinActivity activity = activityWeakReference.get();
+      if (activity == null) {
+        return;
+      }
+      if (message.what == MESSAGE_HANDLE_RESULT) {
+        activity.mUiState.handleResult(activity, message.arg1);
       }
     }
+  }
 
+  private static class ChangePinWorker implements Worker<ChangePinParams, Integer> {
+
+    @Nullable
     @Override
-    public void onFailed(String reason) {
-      super.onFailed(reason);
-      sendResult(OmtpConstants.CHANGE_PIN_SYSTEM_ERROR);
-    }
-
-    private void sendResult(@ChangePinResult int result) {
-      VvmLog.i(TAG, "Change PIN result: " + result);
-      if (mProgressDialog.isShowing()
-          && !VoicemailChangePinActivity.this.isDestroyed()
-          && !VoicemailChangePinActivity.this.isFinishing()) {
-        mProgressDialog.dismiss();
-      } else {
-        VvmLog.i(TAG, "Dialog not visible, not dismissing");
-      }
-      mHandler.obtainMessage(MESSAGE_HANDLE_RESULT, result, 0).sendToTarget();
-      releaseNetwork();
+    public Integer doInBackground(@Nullable ChangePinParams input) throws Throwable {
+      return input.pinChanger.changePin(input.oldPin, input.newPin);
     }
   }
 }
diff --git a/java/com/android/voicemail/impl/settings/VoicemailSettingsFragment.java b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
similarity index 84%
rename from java/com/android/voicemail/impl/settings/VoicemailSettingsFragment.java
rename to java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
index 4652238..efeed08 100644
--- a/java/com/android/voicemail/impl/settings/VoicemailSettingsFragment.java
+++ b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
@@ -11,7 +11,7 @@
  * express or implied. See the License for the specific language governing permissions and
  * limitations under the License
  */
-package com.android.voicemail.impl.settings;
+package com.android.dialer.voicemail.settings;
 
 import android.annotation.TargetApi;
 import android.content.Intent;
@@ -27,15 +27,13 @@
 import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
 import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
 import com.android.dialer.notification.NotificationChannelManager;
 import com.android.voicemail.VoicemailClient;
+import com.android.voicemail.VoicemailClient.ActivationStateListener;
 import com.android.voicemail.VoicemailComponent;
-import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper;
-import com.android.voicemail.impl.R;
-import com.android.voicemail.impl.VvmLog;
-import com.android.voicemail.impl.sync.VvmAccountManager;
 
 /**
  * Fragment for voicemail settings. Requires {@link VoicemailClient#PARAM_PHONE_ACCOUNT_HANDLE} set
@@ -43,12 +41,13 @@
  */
 @TargetApi(VERSION_CODES.O)
 public class VoicemailSettingsFragment extends PreferenceFragment
-    implements Preference.OnPreferenceChangeListener, VvmAccountManager.Listener {
+    implements Preference.OnPreferenceChangeListener, ActivationStateListener {
 
   private static final String TAG = "VmSettingsActivity";
 
   @Nullable private PhoneAccountHandle phoneAccountHandle;
-  private OmtpVvmCarrierConfigHelper omtpVvmCarrierConfigHelper;
+
+  private VoicemailClient voicemailClient;
 
   private Preference voicemailNotificationPreference;
   private SwitchPreference voicemailVisualVoicemail;
@@ -63,15 +62,14 @@
 
     phoneAccountHandle =
         Assert.isNotNull(getArguments().getParcelable(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE));
-
-    omtpVvmCarrierConfigHelper = new OmtpVvmCarrierConfigHelper(getContext(), phoneAccountHandle);
+    voicemailClient = VoicemailComponent.get(getContext()).getVoicemailClient();
   }
 
   @Override
   public void onResume() {
     super.onResume();
     Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_SETTINGS_VIEWED);
-    VvmAccountManager.addListener(this);
+    voicemailClient.addActivationStateListener(this);
     PreferenceScreen preferenceScreen = getPreferenceScreen();
     if (preferenceScreen != null) {
       preferenceScreen.removeAll();
@@ -121,12 +119,11 @@
 
     voicemailChangePinPreference = findPreference(getString(R.string.voicemail_change_pin_key));
 
-    if (omtpVvmCarrierConfigHelper.isValid()) {
+    if (voicemailClient.hasCarrierSupport(getContext(), phoneAccountHandle)) {
       Assert.isNotNull(phoneAccountHandle);
       Intent changePinIntent =
           new Intent(new Intent(getContext(), VoicemailChangePinActivity.class));
-      changePinIntent.putExtra(
-          VoicemailChangePinActivity.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+      changePinIntent.putExtra(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
 
       voicemailChangePinPreference.setIntent(changePinIntent);
       voicemailChangePinPreference.setOnPreferenceClickListener(
@@ -138,7 +135,7 @@
               return false;
             }
           });
-      if (VoicemailChangePinActivity.isDefaultOldPinSet(getContext(), phoneAccountHandle)) {
+      if (VoicemailChangePinActivity.isPinScrambled(getContext(), phoneAccountHandle)) {
         voicemailChangePinPreference.setTitle(R.string.voicemail_set_pin_preference_title);
       } else {
         voicemailChangePinPreference.setTitle(R.string.voicemail_change_pin_preference_title);
@@ -147,15 +144,15 @@
 
       voicemailVisualVoicemail.setOnPreferenceChangeListener(this);
       voicemailVisualVoicemail.setChecked(
-          VisualVoicemailSettingsUtil.isEnabled(getContext(), phoneAccountHandle));
+          voicemailClient.isVoicemailEnabled(getContext(), phoneAccountHandle));
 
       autoArchiveSwitchPreference.setOnPreferenceChangeListener(this);
       autoArchiveSwitchPreference.setChecked(
-          VisualVoicemailSettingsUtil.isArchiveEnabled(getContext(), phoneAccountHandle));
+          voicemailClient.isVoicemailArchiveEnabled(getContext(), phoneAccountHandle));
 
       donateVoicemailSwitchPreference.setOnPreferenceChangeListener(this);
       donateVoicemailSwitchPreference.setChecked(
-          VisualVoicemailSettingsUtil.isVoicemailDonationEnabled(getContext(), phoneAccountHandle));
+          voicemailClient.isVoicemailDonationEnabled(getContext(), phoneAccountHandle));
       updateDonateVoicemail();
     } else {
       prefSet.removePreference(voicemailVisualVoicemail);
@@ -185,7 +182,7 @@
 
   @Override
   public void onPause() {
-    VvmAccountManager.removeListener(this);
+    voicemailClient.removeActivationStateListener(this);
     super.onPause();
   }
 
@@ -197,10 +194,10 @@
    */
   @Override
   public boolean onPreferenceChange(Preference preference, Object objValue) {
-    VvmLog.d(TAG, "onPreferenceChange: \"" + preference + "\" changed to \"" + objValue + "\"");
+    LogUtil.d(TAG, "onPreferenceChange: \"" + preference + "\" changed to \"" + objValue + "\"");
     if (preference.getKey().equals(voicemailVisualVoicemail.getKey())) {
       boolean isEnabled = (boolean) objValue;
-      VisualVoicemailSettingsUtil.setEnabled(getContext(), phoneAccountHandle, isEnabled);
+      voicemailClient.setVoicemailEnabled(getContext(), phoneAccountHandle, isEnabled);
 
       if (isEnabled) {
         Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_USER_ENABLED_IN_SETTINGS);
@@ -212,11 +209,11 @@
       updateDonateVoicemail();
     } else if (preference.getKey().equals(autoArchiveSwitchPreference.getKey())) {
       logArchiveToggle((boolean) objValue);
-      VisualVoicemailSettingsUtil.setArchiveEnabled(
+      voicemailClient.setVoicemailArchiveEnabled(
           getContext(), phoneAccountHandle, (boolean) objValue);
     } else if (preference.getKey().equals(donateVoicemailSwitchPreference.getKey())) {
       logArchiveToggle((boolean) objValue);
-      VisualVoicemailSettingsUtil.setVoicemailDonationEnabled(
+      voicemailClient.setVoicemailDonationEnabled(
           getContext(), phoneAccountHandle, (boolean) objValue);
     }
 
@@ -225,11 +222,11 @@
   }
 
   private void updateChangePin() {
-    if (!VisualVoicemailSettingsUtil.isEnabled(getContext(), phoneAccountHandle)) {
+    if (!voicemailClient.isVoicemailEnabled(getContext(), phoneAccountHandle)) {
       voicemailChangePinPreference.setSummary(
           R.string.voicemail_change_pin_preference_summary_disable);
       voicemailChangePinPreference.setEnabled(false);
-    } else if (!VvmAccountManager.isAccountActivated(getContext(), phoneAccountHandle)) {
+    } else if (!voicemailClient.isActivated(getContext(), phoneAccountHandle)) {
       voicemailChangePinPreference.setSummary(
           R.string.voicemail_change_pin_preference_summary_not_activated);
       voicemailChangePinPreference.setEnabled(false);
@@ -240,11 +237,11 @@
   }
 
   private void updateDonateVoicemail() {
-    if (!VisualVoicemailSettingsUtil.isEnabled(getContext(), phoneAccountHandle)) {
+    if (!voicemailClient.isVoicemailEnabled(getContext(), phoneAccountHandle)) {
       donateVoicemailSwitchPreference.setSummary(
           R.string.voicemail_donate_preference_summary_disable);
       donateVoicemailSwitchPreference.setEnabled(false);
-    } else if (!VvmAccountManager.isAccountActivated(getContext(), phoneAccountHandle)) {
+    } else if (!voicemailClient.isActivated(getContext(), phoneAccountHandle)) {
       donateVoicemailSwitchPreference.setSummary(
           R.string.voicemail_donate_preference_summary_not_activated);
       donateVoicemailSwitchPreference.setEnabled(false);
diff --git a/java/com/android/voicemail/impl/res/layout/voicemail_change_pin.xml b/java/com/android/dialer/voicemail/settings/res/layout/voicemail_change_pin.xml
similarity index 79%
rename from java/com/android/voicemail/impl/res/layout/voicemail_change_pin.xml
rename to java/com/android/dialer/voicemail/settings/res/layout/voicemail_change_pin.xml
index 50c9277..304bd37 100644
--- a/java/com/android/voicemail/impl/res/layout/voicemail_change_pin.xml
+++ b/java/com/android/dialer/voicemail/settings/res/layout/voicemail_change_pin.xml
@@ -1,20 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2014, 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.
-*/
+<!-- Copyright (C) 2014 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.
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/java/com/android/dialer/voicemail/settings/res/values/strings.xml b/java/com/android/dialer/voicemail/settings/res/values/strings.xml
new file mode 100644
index 0000000..4e502b4
--- /dev/null
+++ b/java/com/android/dialer/voicemail/settings/res/values/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+  <!-- Title of the "Voicemail" settings screen, with a text label identifying which SIM the settings are for. -->
+  <string name="voicemail_settings_with_label">Voicemail (<xliff:g example="Mock Carrier" id="subscriptionlabel">%s</xliff:g>)</string>
+
+  <!-- Call settings screen, setting option name -->
+  <string name="voicemail_settings_title">Voicemail</string>
+
+  <!-- DO NOT TRANSLATE. Internal key for a voicemail notification preference. -->
+  <string name="voicemail_notifications_key" translatable="false">voicemail_notification_key</string>
+
+  <!-- Voicemail notifications title. The user clicks on this preference to select
+         which sound to play and whether to vibrate when a voicemail notification is received.
+         [CHAR LIMIT=30] -->
+  <string name="voicemail_notifications_preference_title">Notifications</string>
+  <string name="voicemail_advanced_settings_key" translatable="false">voicemail_advanced_settings_key</string>
+
+  <!-- Title for advanced settings in the voicemail settings -->
+  <string name="voicemail_advanced_settings_title">Advanced Settings</string>
+
+  <!-- DO NOT TRANSLATE. Internal key for a visual voicemail preference. -->
+  <string name="voicemail_visual_voicemail_key" translatable="false">voicemail_visual_voicemail_key</string>
+
+  <!-- DO NOT TRANSLATE. Internal key for a visual voicemail archive preference. -->
+  <string name="voicemail_visual_voicemail_archive_key" translatable="false">archive_is_enabled</string>
+
+  <!-- DO NOT TRANSLATE. Internal key for a voicemail change pin preference. -->
+  <string name="voicemail_change_pin_key" translatable="false">voicemail_change_pin_key</string>
+
+  <!-- Visual voicemail on/off title [CHAR LIMIT=40] -->
+  <string name="voicemail_visual_voicemail_switch_title">Visual Voicemail</string>
+
+  <!-- Visual voicemail archive on/off title [CHAR LIMIT=40] -->
+  <string name="voicemail_visual_voicemail_auto_archive_switch_title">Extra backup and storage</string>
+
+  <!-- Voicemail change PIN dialog title [CHAR LIMIT=40] -->
+  <string name="voicemail_set_pin_preference_title">Set PIN</string>
+  <!-- Voicemail change PIN dialog title [CHAR LIMIT=40] -->
+  <string name="voicemail_change_pin_preference_title">Change PIN</string>
+
+  <string name="voicemail_change_pin_preference_summary_disable">Visual voicemail must be enabled to change PIN</string>
+  <string name="voicemail_change_pin_preference_summary_not_activated">Visual voicemail is not activated yet, please try again later</string>
+
+  <!-- Hint for the old PIN field in the change vociemail PIN dialog -->
+  <string name="vm_change_pin_old_pin">Old PIN</string>
+  <!-- Hint for the new PIN field in the change vociemail PIN dialog -->
+  <string name="vm_change_pin_new_pin">New PIN</string>
+
+  <!-- Message on the dialog when PIN changing is in progress -->
+  <string name="vm_change_pin_progress_message">Please wait.</string>
+  <!-- Error message for the voicemail PIN change if the PIN is too short -->
+  <string name="vm_change_pin_error_too_short">The new PIN is too short.</string>
+  <!-- Error message for the voicemail PIN change if the PIN is too long -->
+  <string name="vm_change_pin_error_too_long">The new PIN is too long.</string>
+  <!-- Error message for the voicemail PIN change if the PIN is too weak -->
+  <string name="vm_change_pin_error_too_weak">The new PIN is too weak. A strong password should not have continuous sequence or repeated digits.</string>
+  <!-- Error message for the voicemail PIN change if the old PIN entered doesn't match  -->
+  <string name="vm_change_pin_error_mismatch">The old PIN does not match.</string>
+  <!-- Error message for the voicemail PIN change if the new PIN contains invalid character -->
+  <string name="vm_change_pin_error_invalid">The new PIN contains invalid characters.</string>
+  <!-- Error message for the voicemail PIN change if operation has failed -->
+  <string name="vm_change_pin_error_system_error">Unable to change PIN</string>
+
+  <!-- The title for the change voicemail PIN activity -->
+  <string name="change_pin_title">Change Voicemail PIN</string>
+  <!-- The label for the continue button in change voicemail PIN activity -->
+  <string name="change_pin_continue_label">Continue</string>
+  <!-- The label for the cancel button in change voicemail PIN activity -->
+  <string name="change_pin_cancel_label">Cancel</string>
+  <!-- The label for the ok button in change voicemail PIN activity -->
+  <string name="change_pin_ok_label">Ok</string>
+  <!-- The title for the enter old pin step in change voicemail PIN activity -->
+  <string name="change_pin_enter_old_pin_header">Confirm your old PIN</string>
+  <!-- The hint for the enter old pin step in change voicemail PIN activity -->
+  <string name="change_pin_enter_old_pin_hint">Enter your voicemail PIN to continue.</string>
+  <!-- The title for the enter new pin step in change voicemail PIN activity -->
+  <string name="change_pin_enter_new_pin_header">Set a new PIN</string>
+  <!-- The hint for the enter new pin step in change voicemail PIN activity -->
+  <string name="change_pin_enter_new_pin_hint">PIN must be <xliff:g example="4" id="min">%1$d</xliff:g>-<xliff:g example="7" id="max">%2$d</xliff:g> digits.</string>
+  <!-- The title for the confirm new pin step in change voicemail PIN activity -->
+  <string name="change_pin_confirm_pin_header">Confirm your PIN</string>
+  <!-- The error message for th confirm new pin step in change voicemail PIN activity, if the pin doen't match the one previously entered -->
+  <string name="change_pin_confirm_pins_dont_match">PINs don\'t match</string>
+  <!-- The toast to show after the voicemail PIN has been successfully changed -->
+  <string name="change_pin_succeeded">Voicemail PIN updated</string>
+  <!-- The error message to show if the server reported an error while attempting to change the voicemail PIN -->
+  <string name="change_pin_system_error">Unable to set PIN</string>
+
+  <string name="voicemail_visual_voicemail_donation_key" translatable="false">donate_voicemails</string>
+
+  <!-- Title for visual voicemail setting that enables user to donate their voicemails for analysis.
+       [CHAR LIMIT=40] -->
+  <string name="voicemail_visual_voicemail_donation_switch_title">
+    Voicemail transcription analysis
+  </string>
+  <!-- Summary information for visual voicemail donation setting when visual voicemail is not enabled
+       [CHAR LIMIT=NONE] -->
+  <string name="voicemail_donate_preference_summary_disable">Visual voicemail must be enabled to donate voicemails</string>
+  <!-- Summary information for visual voicemail donation setting when visual voicemail is not activated
+       [CHAR LIMIT=NONE] -->
+  <string name="voicemail_donate_preference_summary_not_activated">Visual voicemail is not activated yet, please try again later</string>
+  <!-- Summary information for visual voicemail donation setting [CHAR LIMIT=NONE] -->
+  <string name="voicemail_donate_preference_summary_info">Let Google review your voicemail messages to improve transcription quality</string>
+
+</resources>
diff --git a/java/com/android/voicemail/impl/res/xml/voicemail_settings.xml b/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml
similarity index 100%
rename from java/com/android/voicemail/impl/res/xml/voicemail_settings.xml
rename to java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml
diff --git a/java/com/android/voicemail/PinChanger.java b/java/com/android/voicemail/PinChanger.java
new file mode 100644
index 0000000..f58c96b
--- /dev/null
+++ b/java/com/android/voicemail/PinChanger.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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.voicemail;
+
+import android.support.annotation.IntDef;
+import android.support.annotation.WorkerThread;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Interface to change the PIN used to access the mailbox by calling. */
+public interface PinChanger {
+
+  /** Results from {@link #changePin(String, String)} */
+  @Retention(RetentionPolicy.SOURCE)
+  @IntDef(
+    value = {
+      CHANGE_PIN_SUCCESS,
+      CHANGE_PIN_TOO_SHORT,
+      CHANGE_PIN_TOO_LONG,
+      CHANGE_PIN_TOO_WEAK,
+      CHANGE_PIN_MISMATCH,
+      CHANGE_PIN_INVALID_CHARACTER,
+      CHANGE_PIN_SYSTEM_ERROR
+    }
+  )
+  @interface ChangePinResult {}
+
+  int CHANGE_PIN_SUCCESS = 0;
+  int CHANGE_PIN_TOO_SHORT = 1;
+  int CHANGE_PIN_TOO_LONG = 2;
+  int CHANGE_PIN_TOO_WEAK = 3;
+  int CHANGE_PIN_MISMATCH = 4;
+  int CHANGE_PIN_INVALID_CHARACTER = 5;
+  int CHANGE_PIN_SYSTEM_ERROR = 6;
+
+  @WorkerThread
+  @ChangePinResult
+  int changePin(String oldPin, String newPin);
+
+  /**
+   * Set the scrambled PIN if it is auto generated during provisioning. Set to {@code null} to
+   * clear.
+   */
+  void setScrambledPin(String pin);
+
+  String getScrambledPin();
+
+  /** Format requirements for the PIN. */
+  class PinSpecification {
+    public int minLength;
+    public int maxLength;
+  }
+
+  PinSpecification getPinSpecification();
+}
diff --git a/java/com/android/voicemail/VoicemailClient.java b/java/com/android/voicemail/VoicemailClient.java
index 717362e..d033369 100644
--- a/java/com/android/voicemail/VoicemailClient.java
+++ b/java/com/android/voicemail/VoicemailClient.java
@@ -17,7 +17,6 @@
 package com.android.voicemail;
 
 import android.content.Context;
-import android.content.Intent;
 import android.os.PersistableBundle;
 import android.provider.VoicemailContract.Voicemails;
 import android.support.annotation.MainThread;
@@ -67,6 +66,12 @@
   String VOICEMAIL_SECRET_CODE = "886266344";
 
   /**
+   * Whether visual voicemail is supported by the carrier for the {@code phoneAccountHandle}. This
+   * is purely based on the MCCMNC, and a single account might still be disabled by the carrier.
+   */
+  boolean hasCarrierSupport(Context context, PhoneAccountHandle phoneAccountHandle);
+
+  /**
    * Whether the visual voicemail service is enabled for the {@code phoneAccountHandle}. "Enable"
    * means the user "wants" to have this service on, and does not mean the service is actually
    * functional(For example, the service is blocked on the carrier side. The service will be
@@ -104,14 +109,6 @@
   void appendOmtpVoicemailStatusSelectionClause(
       Context context, StringBuilder where, List<String> selectionArgs);
 
-  /**
-   * @return the class name of the {@link android.preference.PreferenceFragment} for voicemail
-   *     settings, or {@code null} if dialer cannot control voicemail settings. Always return {@code
-   *     null} before OC.
-   */
-  @Nullable
-  String getSettingsFragment();
-
   boolean isVoicemailArchiveEnabled(Context context, PhoneAccountHandle phoneAccountHandle);
 
   /**
@@ -134,11 +131,8 @@
   /** @return if the voicemail donation setting has been enabled by the user. */
   boolean isVoicemailDonationEnabled(Context context, PhoneAccountHandle account);
 
-  /**
-   * @return an intent that will launch the activity to change the voicemail PIN. The PIN is used
-   *     when calling into the mailbox.
-   */
-  Intent getSetPinIntent(Context context, PhoneAccountHandle phoneAccountHandle);
+  void setVoicemailDonationEnabled(
+      Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled);
 
   /**
    * Whether the client is activated and handling visual voicemail for the {@code
@@ -165,5 +159,20 @@
   @MainThread
   void onShutdown(@NonNull Context context);
 
+  /** Listener for changes in {@link #isActivated(Context, PhoneAccountHandle)} */
+  interface ActivationStateListener {
+    @MainThread
+    void onActivationStateChanged(PhoneAccountHandle phoneAccountHandle, boolean isActivated);
+  }
+
+  @MainThread
+  void addActivationStateListener(ActivationStateListener listener);
+
+  @MainThread
+  void removeActivationStateListener(ActivationStateListener listener);
+
+  /** Provides interface to change the PIN used to access the mailbox by calling. */
+  PinChanger createPinChanger(Context context, PhoneAccountHandle phoneAccountHandle);
+
   void onTosAccepted(Context context);
 }
diff --git a/java/com/android/voicemail/impl/OmtpConstants.java b/java/com/android/voicemail/impl/OmtpConstants.java
index 97da2a8..d94e361 100644
--- a/java/com/android/voicemail/impl/OmtpConstants.java
+++ b/java/com/android/voicemail/impl/OmtpConstants.java
@@ -13,11 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License
  */
-package com.android.voicemail.impl;
 
-import android.support.annotation.IntDef;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+package com.android.voicemail.impl;
 
 /**
  * Wrapper class to hold relevant OMTP constants as defined in the OMTP spec.
@@ -212,28 +209,6 @@
   public static final String RESPONSE_CHANGE_PIN_INVALID_CHARACTER =
       "password contains invalid characters";
 
-  @Retention(RetentionPolicy.SOURCE)
-  @IntDef(
-    value = {
-      CHANGE_PIN_SUCCESS,
-      CHANGE_PIN_TOO_SHORT,
-      CHANGE_PIN_TOO_LONG,
-      CHANGE_PIN_TOO_WEAK,
-      CHANGE_PIN_MISMATCH,
-      CHANGE_PIN_INVALID_CHARACTER,
-      CHANGE_PIN_SYSTEM_ERROR
-    }
-  )
-  public @interface ChangePinResult {}
-
-  public static final int CHANGE_PIN_SUCCESS = 0;
-  public static final int CHANGE_PIN_TOO_SHORT = 1;
-  public static final int CHANGE_PIN_TOO_LONG = 2;
-  public static final int CHANGE_PIN_TOO_WEAK = 3;
-  public static final int CHANGE_PIN_MISMATCH = 4;
-  public static final int CHANGE_PIN_INVALID_CHARACTER = 5;
-  public static final int CHANGE_PIN_SYSTEM_ERROR = 6;
-
   public static String getClientType() {
     String manufacturer =
         truncate(
diff --git a/java/com/android/voicemail/impl/PinChangerImpl.java b/java/com/android/voicemail/impl/PinChangerImpl.java
new file mode 100644
index 0000000..8735a26
--- /dev/null
+++ b/java/com/android/voicemail/impl/PinChangerImpl.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 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.voicemail.impl;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.net.Network;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import com.android.dialer.common.Assert;
+import com.android.voicemail.PinChanger;
+import com.android.voicemail.impl.imap.ImapHelper;
+import com.android.voicemail.impl.imap.ImapHelper.InitializingException;
+import com.android.voicemail.impl.mail.MessagingException;
+import com.android.voicemail.impl.sync.VvmNetworkRequest;
+import com.android.voicemail.impl.sync.VvmNetworkRequest.NetworkWrapper;
+import com.android.voicemail.impl.sync.VvmNetworkRequest.RequestFailedException;
+
+@TargetApi(VERSION_CODES.O)
+class PinChangerImpl implements PinChanger {
+
+  private final Context context;
+  private final PhoneAccountHandle phoneAccountHandle;
+
+  private static final String KEY_SCRAMBLED_PIN = "default_old_pin"; // legacy name, DO NOT CHANGE
+
+  PinChangerImpl(Context context, PhoneAccountHandle phoneAccountHandle) {
+    this.context = context;
+    this.phoneAccountHandle = phoneAccountHandle;
+  }
+
+  @WorkerThread
+  @Override
+  @ChangePinResult
+  public int changePin(String oldPin, String newPin) {
+    Assert.isWorkerThread();
+    OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(context, phoneAccountHandle);
+    VoicemailStatus.Editor status = VoicemailStatus.edit(context, phoneAccountHandle);
+    try (NetworkWrapper networkWrapper =
+        VvmNetworkRequest.getNetwork(config, phoneAccountHandle, status)) {
+      Network network = networkWrapper.get();
+      try (ImapHelper helper = new ImapHelper(context, phoneAccountHandle, network, status)) {
+        return helper.changePin(oldPin, newPin);
+      } catch (InitializingException | MessagingException e) {
+        VvmLog.e(
+            "VoicemailClientImpl.changePin", "ChangePinNetworkRequestCallback: onAvailable: " + e);
+        return PinChanger.CHANGE_PIN_SYSTEM_ERROR;
+      }
+
+    } catch (RequestFailedException e) {
+      return PinChanger.CHANGE_PIN_SYSTEM_ERROR;
+    }
+  }
+
+  @Override
+  public void setScrambledPin(String pin) {
+    new VisualVoicemailPreferences(context, phoneAccountHandle)
+        .edit()
+        .putString(KEY_SCRAMBLED_PIN, pin)
+        .apply();
+    if (pin == null) {
+      new OmtpVvmCarrierConfigHelper(context, phoneAccountHandle)
+          .handleEvent(
+              VoicemailStatus.edit(context, phoneAccountHandle), OmtpEvents.CONFIG_PIN_SET);
+    }
+  }
+
+  @Override
+  public String getScrambledPin() {
+    return new VisualVoicemailPreferences(context, phoneAccountHandle).getString(KEY_SCRAMBLED_PIN);
+  }
+
+  @Override
+  public PinSpecification getPinSpecification() {
+    PinSpecification result = new PinSpecification();
+    VisualVoicemailPreferences preferences =
+        new VisualVoicemailPreferences(context, phoneAccountHandle);
+    // The OMTP pin length format is {min}-{max}
+    String[] lengths = preferences.getString(OmtpConstants.TUI_PASSWORD_LENGTH, "").split("-");
+    if (lengths.length == 2) {
+      try {
+        result.minLength = Integer.parseInt(lengths[0]);
+        result.maxLength = Integer.parseInt(lengths[1]);
+      } catch (NumberFormatException e) {
+        // do nothing, return default value;
+      }
+    }
+    return result;
+  }
+}
diff --git a/java/com/android/voicemail/impl/PreOMigrationHandler.java b/java/com/android/voicemail/impl/PreOMigrationHandler.java
index 6dc2dee..3ec5e08 100644
--- a/java/com/android/voicemail/impl/PreOMigrationHandler.java
+++ b/java/com/android/voicemail/impl/PreOMigrationHandler.java
@@ -22,8 +22,8 @@
 import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import com.android.voicemail.VoicemailComponent;
 import com.android.voicemail.impl.settings.VisualVoicemailSettingsUtil;
-import com.android.voicemail.impl.settings.VoicemailChangePinActivity;
 import java.lang.reflect.Method;
 
 /** Handles migration of data from the visual voicemail client in telephony before O. */
@@ -95,7 +95,10 @@
       String scrambledPin = legacySettings.getString(EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING);
       if (!TextUtils.isEmpty(scrambledPin)) {
         VvmLog.i("PreOMigrationHandler.migrateSettings", "migrating scrambled PIN");
-        VoicemailChangePinActivity.setDefaultOldPIN(context, phoneAccountHandle, scrambledPin);
+        VoicemailComponent.get(context)
+            .getVoicemailClient()
+            .createPinChanger(context, phoneAccountHandle)
+            .setScrambledPin(scrambledPin);
       }
     }
   }
diff --git a/java/com/android/voicemail/impl/VoicemailClientImpl.java b/java/com/android/voicemail/impl/VoicemailClientImpl.java
index ff1a18d..14fa1d8 100644
--- a/java/com/android/voicemail/impl/VoicemailClientImpl.java
+++ b/java/com/android/voicemail/impl/VoicemailClientImpl.java
@@ -22,19 +22,17 @@
 import android.provider.VoicemailContract.Voicemails;
 import android.support.annotation.MainThread;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.support.v4.os.BuildCompat;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.configprovider.ConfigProviderBindings;
+import com.android.voicemail.PinChanger;
 import com.android.voicemail.VisualVoicemailTypeExtensions;
 import com.android.voicemail.VoicemailClient;
 import com.android.voicemail.impl.configui.VoicemailSecretCodeActivity;
 import com.android.voicemail.impl.settings.VisualVoicemailSettingsUtil;
-import com.android.voicemail.impl.settings.VoicemailChangePinActivity;
-import com.android.voicemail.impl.settings.VoicemailSettingsFragment;
 import com.android.voicemail.impl.sync.VvmAccountManager;
 import com.android.voicemail.impl.transcribe.TranscriptionBackfillService;
 import com.android.voicemail.impl.transcribe.TranscriptionConfigProvider;
@@ -75,6 +73,11 @@
   }
 
   @Override
+  public boolean hasCarrierSupport(Context context, PhoneAccountHandle phoneAccountHandle) {
+    return new OmtpVvmCarrierConfigHelper(context, phoneAccountHandle).isValid();
+  }
+
+  @Override
   public boolean isVoicemailEnabled(Context context, PhoneAccountHandle phoneAccountHandle) {
     return VisualVoicemailSettingsUtil.isEnabled(context, phoneAccountHandle);
   }
@@ -85,12 +88,6 @@
     VisualVoicemailSettingsUtil.setEnabled(context, phoneAccountHandle, enabled);
   }
 
-  @Nullable
-  @Override
-  public String getSettingsFragment() {
-    return VoicemailSettingsFragment.class.getName();
-  }
-
   @Override
   public boolean isVoicemailArchiveEnabled(Context context, PhoneAccountHandle phoneAccountHandle) {
     return VisualVoicemailSettingsUtil.isArchiveEnabled(context, phoneAccountHandle);
@@ -145,10 +142,9 @@
   }
 
   @Override
-  public Intent getSetPinIntent(Context context, PhoneAccountHandle phoneAccountHandle) {
-    Intent intent = new Intent(context, VoicemailChangePinActivity.class);
-    intent.putExtra(VoicemailChangePinActivity.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
-    return intent;
+  public void setVoicemailDonationEnabled(
+      Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled) {
+    VisualVoicemailSettingsUtil.setVoicemailDonationEnabled(context, phoneAccountHandle, enabled);
   }
 
   @Override
@@ -181,6 +177,21 @@
     OmtpService.onShutdown(context);
   }
 
+  @Override
+  public void addActivationStateListener(ActivationStateListener listener) {
+    VvmAccountManager.addListener(listener);
+  }
+
+  @Override
+  public void removeActivationStateListener(ActivationStateListener listener) {
+    VvmAccountManager.removeListener(listener);
+  }
+
+  @Override
+  public PinChanger createPinChanger(Context context, PhoneAccountHandle phoneAccountHandle) {
+    return new PinChangerImpl(context, phoneAccountHandle);
+  }
+
   @TargetApi(VERSION_CODES.O)
   @Override
   public void appendOmtpVoicemailSelectionClause(
diff --git a/java/com/android/voicemail/impl/imap/ImapHelper.java b/java/com/android/voicemail/impl/imap/ImapHelper.java
index 94abe9b..a0e86dc 100644
--- a/java/com/android/voicemail/impl/imap/ImapHelper.java
+++ b/java/com/android/voicemail/impl/imap/ImapHelper.java
@@ -22,8 +22,9 @@
 import android.support.annotation.Nullable;
 import android.telecom.PhoneAccountHandle;
 import android.util.Base64;
+import com.android.voicemail.PinChanger;
+import com.android.voicemail.PinChanger.ChangePinResult;
 import com.android.voicemail.impl.OmtpConstants;
-import com.android.voicemail.impl.OmtpConstants.ChangePinResult;
 import com.android.voicemail.impl.OmtpEvents;
 import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper;
 import com.android.voicemail.impl.VisualVoicemailPreferences;
@@ -393,7 +394,7 @@
       return getChangePinResultFromImapResponse(connection.readResponse());
     } catch (IOException ioe) {
       VvmLog.e(TAG, "changePin: ", ioe);
-      return OmtpConstants.CHANGE_PIN_SYSTEM_ERROR;
+      return PinChanger.CHANGE_PIN_SYSTEM_ERROR;
     } finally {
       connection.destroyResponses();
     }
@@ -434,24 +435,24 @@
       String message = response.getStringOrEmpty(1).getString();
       LogUtils.d(TAG, "change PIN failed: " + message);
       if (OmtpConstants.RESPONSE_CHANGE_PIN_TOO_SHORT.equals(message)) {
-        return OmtpConstants.CHANGE_PIN_TOO_SHORT;
+        return PinChanger.CHANGE_PIN_TOO_SHORT;
       }
       if (OmtpConstants.RESPONSE_CHANGE_PIN_TOO_LONG.equals(message)) {
-        return OmtpConstants.CHANGE_PIN_TOO_LONG;
+        return PinChanger.CHANGE_PIN_TOO_LONG;
       }
       if (OmtpConstants.RESPONSE_CHANGE_PIN_TOO_WEAK.equals(message)) {
-        return OmtpConstants.CHANGE_PIN_TOO_WEAK;
+        return PinChanger.CHANGE_PIN_TOO_WEAK;
       }
       if (OmtpConstants.RESPONSE_CHANGE_PIN_MISMATCH.equals(message)) {
-        return OmtpConstants.CHANGE_PIN_MISMATCH;
+        return PinChanger.CHANGE_PIN_MISMATCH;
       }
       if (OmtpConstants.RESPONSE_CHANGE_PIN_INVALID_CHARACTER.equals(message)) {
-        return OmtpConstants.CHANGE_PIN_INVALID_CHARACTER;
+        return PinChanger.CHANGE_PIN_INVALID_CHARACTER;
       }
-      return OmtpConstants.CHANGE_PIN_SYSTEM_ERROR;
+      return PinChanger.CHANGE_PIN_SYSTEM_ERROR;
     }
     LogUtils.d(TAG, "change PIN succeeded");
-    return OmtpConstants.CHANGE_PIN_SUCCESS;
+    return PinChanger.CHANGE_PIN_SUCCESS;
   }
 
   public void updateQuota() {
diff --git a/java/com/android/voicemail/impl/protocol/Vvm3EventHandler.java b/java/com/android/voicemail/impl/protocol/Vvm3EventHandler.java
index 24f530f..74ba8dd 100644
--- a/java/com/android/voicemail/impl/protocol/Vvm3EventHandler.java
+++ b/java/com/android/voicemail/impl/protocol/Vvm3EventHandler.java
@@ -20,13 +20,13 @@
 import android.provider.VoicemailContract.Status;
 import android.support.annotation.IntDef;
 import android.telecom.PhoneAccountHandle;
+import com.android.voicemail.VoicemailComponent;
 import com.android.voicemail.impl.DefaultOmtpEventHandler;
 import com.android.voicemail.impl.OmtpEvents;
 import com.android.voicemail.impl.OmtpEvents.Type;
 import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper;
 import com.android.voicemail.impl.VoicemailStatus;
 import com.android.voicemail.impl.VvmLog;
-import com.android.voicemail.impl.settings.VoicemailChangePinActivity;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -302,6 +302,10 @@
       VvmLog.e(TAG, "status editor has null phone account handle");
       return false;
     }
-    return VoicemailChangePinActivity.isDefaultOldPinSet(context, phoneAccountHandle);
+    return VoicemailComponent.get(context)
+            .getVoicemailClient()
+            .createPinChanger(context, phoneAccountHandle)
+            .getScrambledPin()
+        != null;
   }
 }
diff --git a/java/com/android/voicemail/impl/protocol/Vvm3Protocol.java b/java/com/android/voicemail/impl/protocol/Vvm3Protocol.java
index 5454bac..782386d 100644
--- a/java/com/android/voicemail/impl/protocol/Vvm3Protocol.java
+++ b/java/com/android/voicemail/impl/protocol/Vvm3Protocol.java
@@ -26,6 +26,8 @@
 import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 import com.android.dialer.logging.DialerImpression;
+import com.android.voicemail.PinChanger;
+import com.android.voicemail.VoicemailComponent;
 import com.android.voicemail.impl.ActivationTask;
 import com.android.voicemail.impl.OmtpConstants;
 import com.android.voicemail.impl.OmtpEvents;
@@ -37,7 +39,6 @@
 import com.android.voicemail.impl.imap.ImapHelper.InitializingException;
 import com.android.voicemail.impl.mail.MessagingException;
 import com.android.voicemail.impl.settings.VisualVoicemailSettingsUtil;
-import com.android.voicemail.impl.settings.VoicemailChangePinActivity;
 import com.android.voicemail.impl.sms.OmtpMessageSender;
 import com.android.voicemail.impl.sms.StatusMessage;
 import com.android.voicemail.impl.sms.Vvm3MessageSender;
@@ -68,7 +69,7 @@
   private static final String IMAP_CHANGE_VM_LANG_FORMAT = "CHANGE_VM_LANG Lang=%1$s";
   private static final String IMAP_CLOSE_NUT = "CLOSE_NUT";
 
-  private static final String ISO639_Spanish = "es";
+  private static final String ISO639_SPANISH = "es";
 
   /**
    * For VVM3, if the STATUS SMS returns {@link StatusMessage#getProvisioningStatus()} of {@link
@@ -217,7 +218,7 @@
         // VVM3 has inconsistent error language code to OMTP. Just issue a raw command
         // here.
         // TODO(a bug): use LocaleList
-        if (Locale.getDefault().getLanguage().equals(new Locale(ISO639_Spanish).getLanguage())) {
+        if (Locale.getDefault().getLanguage().equals(new Locale(ISO639_SPANISH).getLanguage())) {
           // Spanish
           helper.changeVoicemailTuiLanguage(VVM3_VM_LANGUAGE_SPANISH_STANDARD_NO_GUEST_PROMPTS);
         } else {
@@ -257,14 +258,19 @@
       return false;
     }
 
-    if (VoicemailChangePinActivity.isDefaultOldPinSet(context, phoneAccountHandle)) {
+    PinChanger pinChanger =
+        VoicemailComponent.get(context)
+            .getVoicemailClient()
+            .createPinChanger(context, phoneAccountHandle);
+
+    if (pinChanger.getScrambledPin() != null) {
       // The pin was already set
       VvmLog.i(TAG, "PIN already set");
       return true;
     }
     String newPin = generatePin(getMinimumPinLength(context, phoneAccountHandle));
-    if (helper.changePin(defaultPin, newPin) == OmtpConstants.CHANGE_PIN_SUCCESS) {
-      VoicemailChangePinActivity.setDefaultOldPIN(context, phoneAccountHandle, newPin);
+    if (helper.changePin(defaultPin, newPin) == PinChanger.CHANGE_PIN_SUCCESS) {
+      pinChanger.setScrambledPin(newPin);
       helper.handleEvent(OmtpEvents.CONFIG_DEFAULT_PIN_REPLACED);
     }
     VvmLog.i(TAG, "new user: PIN set");
diff --git a/java/com/android/voicemail/impl/res/values/strings.xml b/java/com/android/voicemail/impl/res/values/strings.xml
index c70306d..d1186c2 100644
--- a/java/com/android/voicemail/impl/res/values/strings.xml
+++ b/java/com/android/voicemail/impl/res/values/strings.xml
@@ -15,112 +15,9 @@
 -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-  <!-- Title of the "Voicemail" settings screen, with a text label identifying which SIM the settings are for. -->
-  <string name="voicemail_settings_with_label">Voicemail (<xliff:g example="Mock Carrier" id="subscriptionlabel">%s</xliff:g>)</string>
-
-  <!-- Call settings screen, setting option name -->
-  <string name="voicemail_settings_title">Voicemail</string>
-
-  <!-- DO NOT TRANSLATE. Internal key for a voicemail notification preference. -->
-  <string name="voicemail_notifications_key" translatable="false">voicemail_notification_key</string>
-
-  <!-- Voicemail notifications title. The user clicks on this preference to select
-         which sound to play and whether to vibrate when a voicemail notification is received.
-         [CHAR LIMIT=30] -->
-  <string name="voicemail_notifications_preference_title">Notifications</string>
-  <string name="voicemail_advanced_settings_key" translatable="false">voicemail_advanced_settings_key</string>
-
-  <!-- Title for advanced settings in the voicemail settings -->
-  <string name="voicemail_advanced_settings_title">Advanced Settings</string>
-
-  <!-- DO NOT TRANSLATE. Internal key for a visual voicemail preference. -->
-    <string name="voicemail_visual_voicemail_key" translatable="false">
-        voicemail_visual_voicemail_key
-    </string>
-  <!-- DO NOT TRANSLATE. Internal key for a visual voicemail archive preference. -->
-    <string name="voicemail_visual_voicemail_archive_key" translatable="false">
-        archive_is_enabled
-    </string>
-    <string name="voicemail_visual_voicemail_donation_key" translatable="false">
-        donate_voicemails
-    </string>
-  <!-- DO NOT TRANSLATE. Internal key for a voicemail change pin preference. -->
-  <string name="voicemail_change_pin_key" translatable="false">voicemail_change_pin_key</string>
-
-  <!-- Visual voicemail on/off title [CHAR LIMIT=40] -->
-  <string name="voicemail_visual_voicemail_switch_title">Visual Voicemail</string>
-
-  <!-- Visual voicemail archive on/off title [CHAR LIMIT=40] -->
-  <string name="voicemail_visual_voicemail_auto_archive_switch_title">
-    Extra backup and storage
-  </string>
-
-  <!-- Title for visual voicemail setting that enables user to donate their voicemails for analysis.
-       [CHAR LIMIT=40] -->
-  <string name="voicemail_visual_voicemail_donation_switch_title">
-    Voicemail transcription analysis
-  </string>
-  <!-- Summary information for visual voicemail donation setting when visual voicemail is not enabled
-       [CHAR LIMIT=NONE] -->
-  <string name="voicemail_donate_preference_summary_disable">Visual voicemail must be enabled to donate voicemails</string>
-  <!-- Summary information for visual voicemail donation setting when visual voicemail is not activated
-       [CHAR LIMIT=NONE] -->
-  <string name="voicemail_donate_preference_summary_not_activated">Visual voicemail is not activated yet, please try again later</string>
-  <!-- Summary information for visual voicemail donation setting [CHAR LIMIT=NONE] -->
-  <string name="voicemail_donate_preference_summary_info">Let Google review your voicemail messages to improve transcription quality</string>
-
-  <!-- Voicemail change PIN dialog title [CHAR LIMIT=40] -->
-  <string name="voicemail_set_pin_preference_title">Set PIN</string>
-  <!-- Voicemail change PIN dialog title [CHAR LIMIT=40] -->
-  <string name="voicemail_change_pin_preference_title">Change PIN</string>
-
-  <string name="voicemail_change_pin_preference_summary_disable">Visual voicemail must be enabled to change PIN</string>
-  <string name="voicemail_change_pin_preference_summary_not_activated">Visual voicemail is not activated yet, please try again later</string>
-
-  <!-- Message on the dialog when PIN changing is in progress -->
-  <string name="vm_change_pin_progress_message">Please wait.</string>
-  <!-- Error message for the voicemail PIN change if the PIN is too short -->
-  <string name="vm_change_pin_error_too_short">The new PIN is too short.</string>
-  <!-- Error message for the voicemail PIN change if the PIN is too long -->
-  <string name="vm_change_pin_error_too_long">The new PIN is too long.</string>
-  <!-- Error message for the voicemail PIN change if the PIN is too weak -->
-  <string name="vm_change_pin_error_too_weak">The new PIN is too weak. A strong password should not have continuous sequence or repeated digits.</string>
-  <!-- Error message for the voicemail PIN change if the old PIN entered doesn't match  -->
-  <string name="vm_change_pin_error_mismatch">The old PIN does not match.</string>
-  <!-- Error message for the voicemail PIN change if the new PIN contains invalid character -->
-  <string name="vm_change_pin_error_invalid">The new PIN contains invalid characters.</string>
-  <!-- Error message for the voicemail PIN change if operation has failed -->
-  <string name="vm_change_pin_error_system_error">Unable to change PIN</string>
   <!-- Message to replace the transcription if a visual voicemail message is not supported-->
   <string name="vvm_unsupported_message_format">Unsupported message type, call <xliff:g example="*86" id="number">%s</xliff:g> to listen.</string>
 
-  <!-- The title for the change voicemail PIN activity -->
-  <string name="change_pin_title">Change Voicemail PIN</string>
-  <!-- The label for the continue button in change voicemail PIN activity -->
-  <string name="change_pin_continue_label">Continue</string>
-  <!-- The label for the cancel button in change voicemail PIN activity -->
-  <string name="change_pin_cancel_label">Cancel</string>
-  <!-- The label for the ok button in change voicemail PIN activity -->
-  <string name="change_pin_ok_label">Ok</string>
-  <!-- The title for the enter old pin step in change voicemail PIN activity -->
-  <string name="change_pin_enter_old_pin_header">Confirm your old PIN</string>
-  <!-- The hint for the enter old pin step in change voicemail PIN activity -->
-  <string name="change_pin_enter_old_pin_hint">Enter your voicemail PIN to continue.</string>
-  <!-- The title for the enter new pin step in change voicemail PIN activity -->
-  <string name="change_pin_enter_new_pin_header">Set a new PIN</string>
-  <!-- The hint for the enter new pin step in change voicemail PIN activity -->
-  <string name="change_pin_enter_new_pin_hint">PIN must be <xliff:g example="4" id="min">%1$d</xliff:g>-<xliff:g example="7" id="max">%2$d</xliff:g> digits.</string>
-  <!-- The title for the confirm new pin step in change voicemail PIN activity -->
-  <string name="change_pin_confirm_pin_header">Confirm your PIN</string>
-  <!-- The error message for th confirm new pin step in change voicemail PIN activity, if the pin doen't match the one previously entered -->
-  <string name="change_pin_confirm_pins_dont_match">PINs don\'t match</string>
-  <!-- The toast to show after the voicemail PIN has been successfully changed -->
-  <string name="change_pin_succeeded">Voicemail PIN updated</string>
-  <!-- The error message to show if the server reported an error while attempting to change the voicemail PIN -->
-  <string name="change_pin_system_error">Unable to set PIN</string>
-
   <string name="vvm_config_override_load_current_key" translatable="false">vvm_config_override_load_current</string>
   <string name="vvm_config_override_enabled_key" translatable="false">vvm_config_override_enabled</string>
-
 </resources>
diff --git a/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java b/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java
index 6694a5d..61d7619 100644
--- a/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java
+++ b/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java
@@ -21,7 +21,6 @@
 import com.android.dialer.common.Assert;
 import com.android.voicemail.VoicemailComponent;
 import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper;
-import com.android.voicemail.impl.R;
 import com.android.voicemail.impl.VisualVoicemailPreferences;
 import com.android.voicemail.impl.VvmLog;
 import com.android.voicemail.impl.sync.VvmAccountManager;
@@ -30,6 +29,8 @@
 public class VisualVoicemailSettingsUtil {
 
   @VisibleForTesting public static final String IS_ENABLED_KEY = "is_enabled";
+  private static final String ARCHIVE_ENABLED_KEY = "archive_is_enabled";
+  private static final String DONATE_VOICEMAILS_KEY = "donate_voicemails";
 
   public static void setEnabled(
       Context context, PhoneAccountHandle phoneAccount, boolean isEnabled) {
@@ -53,7 +54,7 @@
         VoicemailComponent.get(context).getVoicemailClient().isVoicemailArchiveAvailable(context));
     new VisualVoicemailPreferences(context, phoneAccount)
         .edit()
-        .putBoolean(context.getString(R.string.voicemail_visual_voicemail_archive_key), isEnabled)
+        .putBoolean(ARCHIVE_ENABLED_KEY, isEnabled)
         .apply();
   }
 
@@ -65,7 +66,7 @@
             .isVoicemailTranscriptionAvailable(context));
     new VisualVoicemailPreferences(context, phoneAccount)
         .edit()
-        .putBoolean(context.getString(R.string.voicemail_visual_voicemail_donation_key), isEnabled)
+        .putBoolean(DONATE_VOICEMAILS_KEY, isEnabled)
         .apply();
   }
 
@@ -87,8 +88,7 @@
     Assert.isNotNull(phoneAccount);
 
     VisualVoicemailPreferences prefs = new VisualVoicemailPreferences(context, phoneAccount);
-    return prefs.getBoolean(
-        context.getString(R.string.voicemail_visual_voicemail_archive_key), false);
+    return prefs.getBoolean(ARCHIVE_ENABLED_KEY, false);
   }
 
   public static boolean isVoicemailDonationEnabled(
@@ -96,8 +96,7 @@
     Assert.isNotNull(phoneAccount);
 
     VisualVoicemailPreferences prefs = new VisualVoicemailPreferences(context, phoneAccount);
-    return prefs.getBoolean(
-        context.getString(R.string.voicemail_visual_voicemail_donation_key), false);
+    return prefs.getBoolean(DONATE_VOICEMAILS_KEY, false);
   }
 
   /**
diff --git a/java/com/android/voicemail/impl/settings/VoicemailRingtonePreference.java b/java/com/android/voicemail/impl/settings/VoicemailRingtonePreference.java
deleted file mode 100644
index 22c729c..0000000
--- a/java/com/android/voicemail/impl/settings/VoicemailRingtonePreference.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2017 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.voicemail.impl.settings;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Build.VERSION_CODES;
-import android.os.Handler;
-import android.os.Message;
-import android.preference.RingtonePreference;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.util.AttributeSet;
-import com.android.dialer.common.Assert;
-import com.android.dialer.util.SettingsUtil;
-
-/**
- * Looks up the voicemail ringtone's name asynchronously and updates the preference's summary when
- * it is created or updated.
- */
-@TargetApi(VERSION_CODES.O)
-public class VoicemailRingtonePreference extends RingtonePreference {
-
-  /** Callback when the ringtone name has been fetched. */
-  public interface VoicemailRingtoneNameChangeListener {
-    void onVoicemailRingtoneNameChanged(CharSequence name);
-  }
-
-  private static final int MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY = 1;
-
-  private PhoneAccountHandle phoneAccountHandle;
-  private final TelephonyManager telephonyManager;
-
-  private VoicemailRingtoneNameChangeListener mVoicemailRingtoneNameChangeListener;
-  private Runnable mVoicemailRingtoneLookupRunnable;
-  private final Handler mVoicemailRingtoneLookupComplete =
-      new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-          switch (msg.what) {
-            case MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY:
-              if (mVoicemailRingtoneNameChangeListener != null) {
-                mVoicemailRingtoneNameChangeListener.onVoicemailRingtoneNameChanged(
-                    (CharSequence) msg.obj);
-              }
-              setSummary((CharSequence) msg.obj);
-              break;
-            default:
-              Assert.fail();
-          }
-        }
-      };
-
-  public VoicemailRingtonePreference(Context context, AttributeSet attrs) {
-    super(context, attrs);
-    telephonyManager = context.getSystemService(TelephonyManager.class);
-  }
-
-  public void init(PhoneAccountHandle phoneAccountHandle, CharSequence oldRingtoneName) {
-    this.phoneAccountHandle = phoneAccountHandle;
-    setSummary(oldRingtoneName);
-    mVoicemailRingtoneLookupRunnable =
-        new Runnable() {
-          @Override
-          public void run() {
-            SettingsUtil.getRingtoneName(
-                getContext(),
-                mVoicemailRingtoneLookupComplete,
-                telephonyManager.getVoicemailRingtoneUri(phoneAccountHandle),
-                MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY);
-          }
-        };
-
-    updateRingtoneName();
-  }
-
-  public void setVoicemailRingtoneNameChangeListener(VoicemailRingtoneNameChangeListener l) {
-    mVoicemailRingtoneNameChangeListener = l;
-  }
-
-  @Override
-  protected Uri onRestoreRingtone() {
-    return telephonyManager.getVoicemailRingtoneUri(phoneAccountHandle);
-  }
-
-  @Override
-  protected void onSaveRingtone(Uri ringtoneUri) {
-    telephonyManager.setVoicemailRingtoneUri(phoneAccountHandle, ringtoneUri);
-    updateRingtoneName();
-  }
-
-  private void updateRingtoneName() {
-    new Thread(mVoicemailRingtoneLookupRunnable).start();
-  }
-}
diff --git a/java/com/android/voicemail/impl/sync/VvmAccountManager.java b/java/com/android/voicemail/impl/sync/VvmAccountManager.java
index f458b2a..e7f2eef 100644
--- a/java/com/android/voicemail/impl/sync/VvmAccountManager.java
+++ b/java/com/android/voicemail/impl/sync/VvmAccountManager.java
@@ -29,6 +29,7 @@
 import com.android.dialer.common.PerAccountSharedPreferences;
 import com.android.dialer.common.concurrent.ThreadUtil;
 import com.android.dialer.storage.StorageComponent;
+import com.android.voicemail.VoicemailClient.ActivationStateListener;
 import com.android.voicemail.impl.OmtpConstants;
 import com.android.voicemail.impl.VisualVoicemailPreferences;
 import com.android.voicemail.impl.VoicemailStatus;
@@ -50,15 +51,9 @@
 public class VvmAccountManager {
   public static final String TAG = "VvmAccountManager";
 
-  /** Listener for activation state changes. Will be called on the main thread. */
-  public interface Listener {
-    @MainThread
-    void onActivationStateChanged(PhoneAccountHandle phoneAccountHandle, boolean isActivated);
-  }
-
   @VisibleForTesting static final String IS_ACCOUNT_ACTIVATED = "is_account_activated";
 
-  private static Set<Listener> listeners = new ArraySet<>();
+  private static final Set<ActivationStateListener> listeners = new ArraySet<>();
 
   public static void addAccount(
       Context context, PhoneAccountHandle phoneAccountHandle, StatusMessage statusMessage) {
@@ -69,7 +64,7 @@
 
     ThreadUtil.postOnUiThread(
         () -> {
-          for (Listener listener : listeners) {
+          for (ActivationStateListener listener : listeners) {
             listener.onActivationStateChanged(phoneAccountHandle, true);
           }
         });
@@ -86,7 +81,7 @@
         .apply();
     ThreadUtil.postOnUiThread(
         () -> {
-          for (Listener listener : listeners) {
+          for (ActivationStateListener listener : listeners) {
             listener.onActivationStateChanged(phoneAccount, false);
           }
         });
@@ -113,13 +108,13 @@
   }
 
   @MainThread
-  public static void addListener(Listener listener) {
+  public static void addListener(ActivationStateListener listener) {
     Assert.isMainThread();
     listeners.add(listener);
   }
 
   @MainThread
-  public static void removeListener(Listener listener) {
+  public static void removeListener(ActivationStateListener listener) {
     Assert.isMainThread();
     listeners.remove(listener);
   }
diff --git a/java/com/android/voicemail/stub/StubVoicemailClient.java b/java/com/android/voicemail/stub/StubVoicemailClient.java
index 4b8ed9a..0fe533e 100644
--- a/java/com/android/voicemail/stub/StubVoicemailClient.java
+++ b/java/com/android/voicemail/stub/StubVoicemailClient.java
@@ -17,12 +17,12 @@
 package com.android.voicemail.stub;
 
 import android.content.Context;
-import android.content.Intent;
 import android.os.PersistableBundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
+import com.android.dialer.common.Assert;
+import com.android.voicemail.PinChanger;
 import com.android.voicemail.VoicemailClient;
 import java.util.List;
 import javax.inject.Inject;
@@ -57,11 +57,6 @@
       Context context, StringBuilder where, List<String> selectionArgs) {}
 
   @Override
-  public String getSettingsFragment() {
-    return null;
-  }
-
-  @Override
   public boolean isVoicemailArchiveEnabled(Context context, PhoneAccountHandle phoneAccountHandle) {
     return false;
   }
@@ -86,11 +81,8 @@
   }
 
   @Override
-  public Intent getSetPinIntent(Context context, PhoneAccountHandle phoneAccountHandle) {
-    Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL);
-    intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
-    return intent;
-  }
+  public void setVoicemailDonationEnabled(
+      Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled) {}
 
   @Override
   public boolean isActivated(Context context, PhoneAccountHandle phoneAccountHandle) {
@@ -113,5 +105,25 @@
   public void onShutdown(@NonNull Context context) {}
 
   @Override
+  public void addActivationStateListener(ActivationStateListener listener) {
+    throw Assert.createAssertionFailException("should never be called on stub.");
+  }
+
+  @Override
+  public void removeActivationStateListener(ActivationStateListener listener) {
+    throw Assert.createAssertionFailException("should never be called on stub.");
+  }
+
+  @Override
+  public boolean hasCarrierSupport(Context context, PhoneAccountHandle phoneAccountHandle) {
+    return false;
+  }
+
+  @Override
+  public PinChanger createPinChanger(Context context, PhoneAccountHandle phoneAccountHandle) {
+    throw Assert.createAssertionFailException("should never be called on stub.");
+  }
+
+  @Override
   public void onTosAccepted(Context context) {}
 }