Merge changes Iebf5ab67,I69df1f28

* changes:
  Use OnPreDrawListener to adjust key layouts on the dialpad.
  Don't transcribe voicemail unless carrier allows OTT transcription
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java b/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java
index 1092175..3e43213 100644
--- a/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java
@@ -347,7 +347,12 @@
           .putInt(PREF_DIALER_TOS_VERSION_ACCEPTED_KEY, CURRENT_DIALER_TOS_VERSION)
           .apply();
     }
-    VoicemailComponent.get(context).getVoicemailClient().onTosAccepted(context);
+
+    PhoneAccountHandle handle =
+        new PhoneAccountHandle(
+            ComponentName.unflattenFromString(status.phoneAccountComponentName),
+            status.phoneAccountId);
+    VoicemailComponent.get(context).getVoicemailClient().onTosAccepted(context, handle);
   }
 
   private boolean hasAcknowledgedFeatures() {
diff --git a/java/com/android/dialer/dialpadview/DialpadView.java b/java/com/android/dialer/dialpadview/DialpadView.java
index 73abd12..4cbf42f 100644
--- a/java/com/android/dialer/dialpadview/DialpadView.java
+++ b/java/com/android/dialer/dialpadview/DialpadView.java
@@ -33,7 +33,9 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.ViewPropertyAnimator;
+import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.EditText;
 import android.widget.ImageButton;
@@ -76,6 +78,7 @@
 
   private final AttributeSet mAttributeSet;
   private final ColorStateList mRippleColor;
+  private final OnPreDrawListenerForKeyLayoutAdjust mOnPreDrawListenerForKeyLayoutAdjust;
   private final String[] mPrimaryLettersMapping;
   private final String[] mSecondaryLettersMapping;
   private final boolean mIsRtl; // whether the dialpad is shown in a right-to-left locale
@@ -112,6 +115,21 @@
 
     mPrimaryLettersMapping = DialpadCharMappings.getDefaultKeyToCharsMap();
     mSecondaryLettersMapping = DialpadCharMappings.getKeyToCharsMap(context);
+
+    mOnPreDrawListenerForKeyLayoutAdjust = new OnPreDrawListenerForKeyLayoutAdjust();
+  }
+
+  @Override
+  protected void onAttachedToWindow() {
+    super.onAttachedToWindow();
+    getViewTreeObserver().removeOnPreDrawListener(mOnPreDrawListenerForKeyLayoutAdjust);
+    getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListenerForKeyLayoutAdjust);
+  }
+
+  @Override
+  protected void onDetachedFromWindow() {
+    super.onDetachedFromWindow();
+    getViewTreeObserver().removeOnPreDrawListener(mOnPreDrawListenerForKeyLayoutAdjust);
   }
 
   @Override
@@ -221,89 +239,6 @@
     zero.setLongHoverContentDescription(resources.getText(R.string.description_image_button_plus));
   }
 
-  @Override
-  protected void onLayout(boolean changed, int l, int t, int r, int b) {
-    super.onLayout(changed, l, t, r, b);
-
-    if (changed) {
-      if (isLandscapeMode()) {
-        adjustKeyWidths();
-      } else {
-        adjustDigitKeyHeights();
-      }
-    }
-  }
-
-  /**
-   * Make the heights of all digit keys the same.
-   *
-   * <p>When the device is in portrait mode, we first find the maximum height among digit key
-   * layouts. Then for each key, we adjust the height of the layout containing letters/the voice
-   * mail icon to ensure the height of each digit key is the same.
-   *
-   * <p>This method should be called after the sizes of related layouts have been calculated by the
-   * framework.
-   */
-  private void adjustDigitKeyHeights() {
-    Assert.checkState(!isLandscapeMode());
-
-    int maxHeight = 0;
-    for (int i = 0; i <= 9; i++) {
-      DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]);
-      LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
-      maxHeight = Math.max(maxHeight, keyLayout.getHeight());
-    }
-
-    for (int i = 0; i <= 9; i++) {
-      DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]);
-      LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
-
-      DialpadTextView numberView =
-          (DialpadTextView) keyLayout.findViewById(R.id.dialpad_key_number);
-      MarginLayoutParams numberViewLayoutParams = (MarginLayoutParams) numberView.getLayoutParams();
-
-      LinearLayout iconOrLettersLayout =
-          (LinearLayout) keyLayout.findViewById(R.id.dialpad_key_icon_or_letters_layout);
-      iconOrLettersLayout.setLayoutParams(
-          new LayoutParams(
-              LayoutParams.WRAP_CONTENT /* width */,
-              maxHeight
-                  - numberView.getHeight()
-                  - numberViewLayoutParams.topMargin
-                  - numberViewLayoutParams.bottomMargin /* height */));
-    }
-  }
-
-  /**
-   * Make the widths of all keys the same.
-   *
-   * <p>When the device is in landscape mode, we first find the maximum width among key layouts.
-   * Then we adjust the width of each layout's horizontal placeholder so that each key has the same
-   * width.
-   *
-   * <p>This method should be called after the sizes of related layouts have been calculated by the
-   * framework.
-   */
-  private void adjustKeyWidths() {
-    Assert.checkState(isLandscapeMode());
-
-    int maxWidth = 0;
-    for (int buttonId : BUTTON_IDS) {
-      DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(buttonId);
-      LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
-      maxWidth = Math.max(maxWidth, keyLayout.getWidth());
-    }
-
-    for (int buttonId : BUTTON_IDS) {
-      DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(buttonId);
-      LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
-      View horizontalPlaceholder = keyLayout.findViewById(R.id.dialpad_key_horizontal_placeholder);
-      horizontalPlaceholder.setLayoutParams(
-          new LayoutParams(
-              maxWidth - keyLayout.getWidth() /* width */, LayoutParams.MATCH_PARENT /* height */));
-    }
-  }
-
   private Drawable getDrawableCompat(Context context, int id) {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
       return context.getDrawable(id);
@@ -559,4 +494,181 @@
   private boolean isLandscapeMode() {
     return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
   }
+
+  /**
+   * An {@link OnPreDrawListener} that adjusts the height/width of each key layout so that they can
+   * be properly aligned.
+   *
+   * <p>When the device is in portrait mode, the layout height for key "1" can be different from
+   * those of other <b>digit</b> keys due to the voicemail icon. Adjustments are needed to ensure
+   * the layouts for all <b>digit</b> keys are of the same height. Key "*" and key "#" are excluded
+   * because their styles are different from other keys'.
+   *
+   * <p>When the device is in landscape mode, keys can have different layout widths due to the
+   * icon/characters associated with them. Adjustments are needed to ensure the layouts for all keys
+   * are of the same width.
+   *
+   * <p>Note that adjustments can only be made after the layouts are measured, which is why the
+   * logic lives in an {@link OnPreDrawListener} that is invoked when the view tree is about to be
+   * drawn.
+   */
+  private class OnPreDrawListenerForKeyLayoutAdjust implements OnPreDrawListener {
+
+    /**
+     * This method is invoked when the view tree is about to be drawn. At this point, all views in
+     * the tree have been measured and given a frame.
+     *
+     * <p>If the keys have been adjusted, we instruct the current drawing pass to proceed by
+     * returning true. Otherwise, adjustments will be made and the current drawing pass will be
+     * cancelled by returning false.
+     *
+     * <p>It is imperative to schedule another layout pass of the view tree after adjustments are
+     * made so that {@link #onPreDraw()} can be invoked again to check the layouts and proceed with
+     * the drawing pass.
+     */
+    @Override
+    public boolean onPreDraw() {
+      if (!shouldAdjustKeySizes()) {
+        return true; // Return true to proceed with the current drawing pass.
+      }
+
+      adjustKeySizes();
+      return false; // Return false to cancel the current drawing pass.
+    }
+
+    private boolean shouldAdjustKeySizes() {
+      return isLandscapeMode() ? shouldAdjustKeyWidths() : shouldAdjustDigitKeyHeights();
+    }
+
+    /**
+     * Return true if not all key layouts have the same width. This method must be called when the
+     * device is in landscape mode.
+     */
+    private boolean shouldAdjustKeyWidths() {
+      Assert.checkState(isLandscapeMode());
+
+      DialpadKeyButton dialpadKeyButton = (DialpadKeyButton) findViewById(BUTTON_IDS[0]);
+      LinearLayout keyLayout =
+          (LinearLayout) dialpadKeyButton.findViewById(R.id.dialpad_key_layout);
+      final int width = keyLayout.getWidth();
+
+      for (int i = 1; i < BUTTON_IDS.length; i++) {
+        dialpadKeyButton = (DialpadKeyButton) findViewById(BUTTON_IDS[i]);
+        keyLayout = (LinearLayout) dialpadKeyButton.findViewById(R.id.dialpad_key_layout);
+        if (width != keyLayout.getWidth()) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    /**
+     * Return true if not all <b>digit</b> key layouts have the same height. This method must be
+     * called when the device is in portrait mode.
+     */
+    private boolean shouldAdjustDigitKeyHeights() {
+      Assert.checkState(!isLandscapeMode());
+
+      DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[0]);
+      LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
+      final int height = keyLayout.getHeight();
+
+      // BUTTON_IDS[i] is the resource ID for button i when 0 <= i && i <= 9.
+      // For example, BUTTON_IDS[3] is the resource ID for button "3" on the dialpad.
+      for (int i = 1; i <= 9; i++) {
+        dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]);
+        keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
+        if (height != keyLayout.getHeight()) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    private void adjustKeySizes() {
+      if (isLandscapeMode()) {
+        adjustKeyWidths();
+      } else {
+        adjustDigitKeyHeights();
+      }
+    }
+
+    /**
+     * Make the heights of all <b>digit</b> keys the same.
+     *
+     * <p>When the device is in portrait mode, we first find the maximum height among digit key
+     * layouts. Then for each key, we adjust the height of the layout containing letters/the
+     * voicemail icon to ensure the height of each digit key is the same.
+     *
+     * <p>A layout pass will be scheduled in this method by {@link
+     * LinearLayout#setLayoutParams(ViewGroup.LayoutParams)}.
+     */
+    private void adjustDigitKeyHeights() {
+      Assert.checkState(!isLandscapeMode());
+
+      int maxHeight = 0;
+
+      // BUTTON_IDS[i] is the resource ID for button i when 0 <= i && i <= 9.
+      // For example, BUTTON_IDS[3] is the resource ID for button "3" on the dialpad.
+      for (int i = 0; i <= 9; i++) {
+        DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]);
+        LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
+        maxHeight = Math.max(maxHeight, keyLayout.getHeight());
+      }
+
+      for (int i = 0; i <= 9; i++) {
+        DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]);
+        LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
+
+        DialpadTextView numberView =
+            (DialpadTextView) keyLayout.findViewById(R.id.dialpad_key_number);
+        MarginLayoutParams numberViewLayoutParams =
+            (MarginLayoutParams) numberView.getLayoutParams();
+
+        LinearLayout iconOrLettersLayout =
+            (LinearLayout) keyLayout.findViewById(R.id.dialpad_key_icon_or_letters_layout);
+        iconOrLettersLayout.setLayoutParams(
+            new LayoutParams(
+                LayoutParams.WRAP_CONTENT /* width */,
+                maxHeight
+                    - numberView.getHeight()
+                    - numberViewLayoutParams.topMargin
+                    - numberViewLayoutParams.bottomMargin /* height */));
+      }
+    }
+
+    /**
+     * Make the widths of all keys the same.
+     *
+     * <p>When the device is in landscape mode, we first find the maximum width among key layouts.
+     * Then we adjust the width of each layout's horizontal placeholder so that each key has the
+     * same width.
+     *
+     * <p>A layout pass will be scheduled in this method by {@link
+     * View#setLayoutParams(ViewGroup.LayoutParams)}.
+     */
+    private void adjustKeyWidths() {
+      Assert.checkState(isLandscapeMode());
+
+      int maxWidth = 0;
+      for (int buttonId : BUTTON_IDS) {
+        DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(buttonId);
+        LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
+        maxWidth = Math.max(maxWidth, keyLayout.getWidth());
+      }
+
+      for (int buttonId : BUTTON_IDS) {
+        DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(buttonId);
+        LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout);
+        View horizontalPlaceholder =
+            keyLayout.findViewById(R.id.dialpad_key_horizontal_placeholder);
+        horizontalPlaceholder.setLayoutParams(
+            new LayoutParams(
+                maxWidth - keyLayout.getWidth() /* width */,
+                LayoutParams.MATCH_PARENT /* height */));
+      }
+    }
+  }
 }
diff --git a/java/com/android/voicemail/CarrierConfigKeys.java b/java/com/android/voicemail/CarrierConfigKeys.java
new file mode 100644
index 0000000..609221d
--- /dev/null
+++ b/java/com/android/voicemail/CarrierConfigKeys.java
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+/**
+ * Keys used to lookup carrier specific configuration strings. See {@code
+ * VoicemailClient.getCarrierConfigString}
+ */
+public interface CarrierConfigKeys {
+
+  /**
+   * Carrier config key whose value will be 'true' for carriers that allow over the top voicemail
+   * transcription.
+   */
+  String VVM_CARRIER_ALLOWS_OTT_TRANSCRIPTION_STRING =
+      "vvm_carrier_allows_ott_transcription_string";
+}
diff --git a/java/com/android/voicemail/VoicemailClient.java b/java/com/android/voicemail/VoicemailClient.java
index 050286a..db5d745 100644
--- a/java/com/android/voicemail/VoicemailClient.java
+++ b/java/com/android/voicemail/VoicemailClient.java
@@ -177,5 +177,12 @@
   /** Provides interface to change the PIN used to access the mailbox by calling. */
   PinChanger createPinChanger(Context context, PhoneAccountHandle phoneAccountHandle);
 
-  void onTosAccepted(Context context);
+  void onTosAccepted(Context context, PhoneAccountHandle phoneAccountHandle);
+
+  /**
+   * @return arbitrary carrier configuration String value associate with the indicated key. See
+   *     {@code CarrierConfigKeys.java}
+   */
+  @Nullable
+  String getCarrierConfigString(Context context, PhoneAccountHandle phoneAccountHandle, String key);
 }
diff --git a/java/com/android/voicemail/impl/VoicemailClientImpl.java b/java/com/android/voicemail/impl/VoicemailClientImpl.java
index 2add59e..3305438 100644
--- a/java/com/android/voicemail/impl/VoicemailClientImpl.java
+++ b/java/com/android/voicemail/impl/VoicemailClientImpl.java
@@ -22,6 +22,7 @@
 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;
@@ -285,8 +286,15 @@
   }
 
   @Override
-  public void onTosAccepted(Context context) {
+  public void onTosAccepted(Context context, PhoneAccountHandle account) {
     LogUtil.i("VoicemailClientImpl.onTosAccepted", "try backfilling voicemail transcriptions");
-    TranscriptionBackfillService.scheduleTask(context);
+    TranscriptionBackfillService.scheduleTask(context, account);
+  }
+
+  @Override
+  @Nullable
+  public String getCarrierConfigString(Context context, PhoneAccountHandle account, String key) {
+    OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context, account);
+    return helper.isValid() ? helper.getString(key) : null;
   }
 }
diff --git a/java/com/android/voicemail/impl/res/xml/vvm_config.xml b/java/com/android/voicemail/impl/res/xml/vvm_config.xml
index 1e5190a..499bae2 100644
--- a/java/com/android/voicemail/impl/res/xml/vvm_config.xml
+++ b/java/com/android/voicemail/impl/res/xml/vvm_config.xml
@@ -104,6 +104,7 @@
       <!-- a bug -->
       <item value="AUTH=DIGEST-MD5"/>
     </string-array>
+    <string name="vvm_carrier_allows_ott_transcription_string">true</string>
   </pbundle_as_map>
 
   <pbundle_as_map>
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java
index f7f6439..8161e71 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java
@@ -25,6 +25,7 @@
 import android.support.annotation.WorkerThread;
 import android.support.v4.app.JobIntentService;
 import android.support.v4.os.BuildCompat;
+import android.telecom.PhoneAccountHandle;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.ThreadUtil;
 import com.android.dialer.constants.ScheduledJobIds;
@@ -38,7 +39,7 @@
 public class TranscriptionBackfillService extends JobIntentService {
 
   /** Schedule a task to scan the database for untranscribed voicemails */
-  public static boolean scheduleTask(Context context) {
+  public static boolean scheduleTask(Context context, PhoneAccountHandle account) {
     if (BuildCompat.isAtLeastO()) {
       LogUtil.enterBlock("TranscriptionBackfillService.transcribeOldVoicemails");
       ComponentName componentName = new ComponentName(context, TranscriptionBackfillService.class);
@@ -46,15 +47,17 @@
           new JobInfo.Builder(ScheduledJobIds.VVM_TRANSCRIPTION_BACKFILL_JOB, componentName)
               .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
       JobScheduler scheduler = context.getSystemService(JobScheduler.class);
-      return scheduler.enqueue(builder.build(), makeWorkItem()) == JobScheduler.RESULT_SUCCESS;
+      return scheduler.enqueue(builder.build(), makeWorkItem(account))
+          == JobScheduler.RESULT_SUCCESS;
     } else {
       LogUtil.i("TranscriptionBackfillService.transcribeOldVoicemails", "not supported");
       return false;
     }
   }
 
-  private static JobWorkItem makeWorkItem() {
+  private static JobWorkItem makeWorkItem(PhoneAccountHandle account) {
     Intent intent = new Intent();
+    intent.putExtra(TranscriptionService.EXTRA_ACCOUNT_HANDLE, account);
     return new JobWorkItem(intent);
   }
 
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
index 79e1a01..33c9676 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
@@ -24,10 +24,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
 import android.support.annotation.MainThread;
-import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
-import android.support.v4.os.BuildCompat;
 import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 import com.android.dialer.common.Assert;
@@ -35,6 +34,9 @@
 import com.android.dialer.constants.ScheduledJobIds;
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
+import com.android.voicemail.CarrierConfigKeys;
+import com.android.voicemail.VoicemailClient;
+import com.android.voicemail.VoicemailComponent;
 import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -65,34 +67,46 @@
   // donation.
   @MainThread
   public static boolean scheduleNewVoicemailTranscriptionJob(
-      Context context,
-      Uri voicemailUri,
-      @Nullable PhoneAccountHandle account,
-      boolean highPriority) {
+      Context context, Uri voicemailUri, PhoneAccountHandle account, boolean highPriority) {
     Assert.isMainThread();
-    if (BuildCompat.isAtLeastO()) {
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
       LogUtil.i(
-          "TranscriptionService.scheduleNewVoicemailTranscriptionJob", "scheduling transcription");
-      Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_VOICEMAIL_RECEIVED);
-
-      ComponentName componentName = new ComponentName(context, TranscriptionService.class);
-      JobInfo.Builder builder =
-          new JobInfo.Builder(ScheduledJobIds.VVM_TRANSCRIPTION_JOB, componentName);
-      if (highPriority) {
-        builder
-            .setMinimumLatency(0)
-            .setOverrideDeadline(0)
-            .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
-      } else {
-        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
-      }
-      JobScheduler scheduler = context.getSystemService(JobScheduler.class);
-      JobWorkItem workItem = makeWorkItem(voicemailUri, account);
-      return scheduler.enqueue(builder.build(), workItem) == JobScheduler.RESULT_SUCCESS;
-    } else {
-      LogUtil.i("TranscriptionService.scheduleNewVoicemailTranscriptionJob", "not supported");
+          "TranscriptionService.scheduleNewVoicemailTranscriptionJob", "not supported by sdk");
       return false;
     }
+    if (!carrierAllowsOttTranscription(context, account)) {
+      LogUtil.i(
+          "TranscriptionService.scheduleNewVoicemailTranscriptionJob",
+          "carrier doesn't allow transcription");
+      return false;
+    }
+
+    LogUtil.i(
+        "TranscriptionService.scheduleNewVoicemailTranscriptionJob", "scheduling transcription");
+    Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_VOICEMAIL_RECEIVED);
+
+    ComponentName componentName = new ComponentName(context, TranscriptionService.class);
+    JobInfo.Builder builder =
+        new JobInfo.Builder(ScheduledJobIds.VVM_TRANSCRIPTION_JOB, componentName);
+    if (highPriority) {
+      builder
+          .setMinimumLatency(0)
+          .setOverrideDeadline(0)
+          .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+    } else {
+      builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
+    }
+    JobScheduler scheduler = context.getSystemService(JobScheduler.class);
+    JobWorkItem workItem = makeWorkItem(voicemailUri, account);
+    return scheduler.enqueue(builder.build(), workItem) == JobScheduler.RESULT_SUCCESS;
+  }
+
+  private static boolean carrierAllowsOttTranscription(
+      Context context, PhoneAccountHandle account) {
+    VoicemailClient client = VoicemailComponent.get(context).getVoicemailClient();
+    return Boolean.parseBoolean(
+        client.getCarrierConfigString(
+            context, account, CarrierConfigKeys.VVM_CARRIER_ALLOWS_OTT_TRANSCRIPTION_STRING));
   }
 
   // Cancel all transcription tasks
diff --git a/java/com/android/voicemail/stub/StubVoicemailClient.java b/java/com/android/voicemail/stub/StubVoicemailClient.java
index fe06324..cfbb3ec 100644
--- a/java/com/android/voicemail/stub/StubVoicemailClient.java
+++ b/java/com/android/voicemail/stub/StubVoicemailClient.java
@@ -130,5 +130,11 @@
   }
 
   @Override
-  public void onTosAccepted(Context context) {}
+  public void onTosAccepted(Context context, PhoneAccountHandle account) {}
+
+  @Override
+  @Nullable
+  public String getCarrierConfigString(Context context, PhoneAccountHandle account, String key) {
+    return null;
+  }
 }