release-request-369362fc-cc6c-4cb1-95c0-90b5ac7c52cb-for-git_oc-mr1-release-4288633 snap-temp-L81700000095141745

Change-Id: I605c3b28a5ac328edc2328d759e18ffb10035cda
diff --git a/Android.mk b/Android.mk
index beccd86..17823e7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -189,6 +189,13 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, $(SRC_DIRS))
 LOCAL_SRC_FILES := $(filter-out $(EXCLUDE_FILES),$(LOCAL_SRC_FILES))
 LOCAL_SRC_FILES += $(call all-proto-files-under, $(SRC_DIRS))
+
+# Backup Library
+BACKUP_LIB_SRC_DIR := ../../../external/libbackup/src/com/google/android/libraries/backup
+EXCLUDE_BACKUP_LIB_SRCS := $(call all-java-files-under, $(BACKUP_LIB_SRC_DIR)/shadow)
+LOCAL_SRC_FILES += $(call all-java-files-under, $(BACKUP_LIB_SRC_DIR))
+LOCAL_SRC_FILES := $(filter-out $(EXCLUDE_BACKUP_LIB_SRCS),$(LOCAL_SRC_FILES))
+
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)
 
 LOCAL_RESOURCE_DIR := \
@@ -365,6 +372,8 @@
 DIALER_MANIFEST_FILES :=
 PROCESSOR_LIBRARIES_TARGET :=
 PROCESSOR_JARS :=
+BACKUP_LIB_SRC_DIR :=
+EXCLUDE_BACKUP_LIB_SRCS :=
 
 # Create references to prebuilt libraries.
 include $(CLEAR_VARS)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5420ec0..a384919 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -103,17 +103,12 @@
   <uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS"/>
 
   <application
-    android:backupAgent='com.android.dialer.backup.DialerBackupAgent'
-    android:fullBackupOnly="true"
-    android:restoreAnyVersion="true"
     android:hardwareAccelerated="true"
     android:icon="@mipmap/ic_launcher_phone"
     android:label="@string/applicationLabel"
     android:name="com.android.dialer.binary.aosp.AospDialerApplication"
     android:supportsRtl="true"
     android:usesCleartextTraffic="false">
-
-
   </application>
 
 </manifest>
diff --git a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
index a4a2ba2..99c49b7 100644
--- a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
+++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
@@ -605,13 +605,15 @@
       callButtonView.setVisibility(View.VISIBLE);
     }
 
-    if (CallUtil.isVideoEnabled(mContext)
+    // We need to check if we are showing the Lightbringer primary button. If we are, then we should
+    // show the "Call" button here regardless of IMS availability.
+    if (showLightbringerPrimaryButton()) {
+      callButtonView.setVisibility(View.VISIBLE);
+      videoCallButtonView.setVisibility(View.GONE);
+    } else if (CallUtil.isVideoEnabled(mContext)
         && (hasPlacedCarrierVideoCall() || canSupportCarrierVideoCall())) {
       videoCallButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number));
       videoCallButtonView.setVisibility(View.VISIBLE);
-    } else if (showLightbringerPrimaryButton()) {
-      callButtonView.setVisibility(View.VISIBLE);
-      videoCallButtonView.setVisibility(View.GONE);
     } else if (lightbringerReady) {
       videoCallButtonView.setTag(IntentProvider.getLightbringerIntentProvider(number));
       videoCallButtonView.setVisibility(View.VISIBLE);
diff --git a/java/com/android/dialer/backup/AndroidManifest.xml b/java/com/android/dialer/backup/AndroidManifest.xml
index 1cbbe53..84992c0 100644
--- a/java/com/android/dialer/backup/AndroidManifest.xml
+++ b/java/com/android/dialer/backup/AndroidManifest.xml
@@ -1,5 +1,5 @@
 <!--
- ~ Copyright (C) 2016 The Android Open Source Project
+ ~ 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.
@@ -13,14 +13,13 @@
  ~ 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.backup">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.dialer.backup">
 
-  <application
-    android:backupAgent="com.android.dialer.backup.DialerBackupAgent"
-    android:fullBackupOnly="true"
-    android:restoreAnyVersion="true"
-    />
 
-</manifest>
+  <!-- Android backup service key -->
+  <!-- https://developer.android.com/google/backup/signup.html -->
+  <meta-data
+      android:name="com.google.android.backup.api_key"
+      android:value="AEdPqrEAAAAIn3-Y3JKit1mrzfvcdbVhUiJn2ICtKfhGYLy0Bg"/>
+</manifest>
\ No newline at end of file
diff --git a/java/com/android/dialer/backup/DialerBackupAgent.java b/java/com/android/dialer/backup/DialerBackupAgent.java
deleted file mode 100644
index a0baf39..0000000
--- a/java/com/android/dialer/backup/DialerBackupAgent.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dialer.backup;
-
-import android.annotation.TargetApi;
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.app.backup.FullBackupDataOutput;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build.VERSION_CODES;
-import android.os.ParcelFileDescriptor;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.provider.VoicemailContract;
-import android.provider.VoicemailContract.Voicemails;
-import android.telecom.PhoneAccountHandle;
-import android.util.Pair;
-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.telecom.TelecomUtil;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * The Dialer backup agent to backup voicemails, and files under files, shared prefs and databases
- */
-public class DialerBackupAgent extends BackupAgent {
-  // File names suffix for backup/restore.
-  private static final String VOICEMAIL_BACKUP_FILE_SUFFIX = "_voicemail_backup.proto";
-  // File name formats for backup. It looks like 000000_voicemail_backup.proto, 0000001...
-  private static final String VOICEMAIL_BACKUP_FILE_FORMAT = "%06d" + VOICEMAIL_BACKUP_FILE_SUFFIX;
-  // Order by Date entries from database. We start backup from the newest.
-  private static final String ORDER_BY_DATE = "date DESC";
-  // Voicemail Uri Column
-  public static final String VOICEMAIL_URI = "voicemail_uri";
-  // Voicemail packages to backup
-  public static final String VOICEMAIL_SOURCE_PACKAGE = "com.google.android.dialer";
-
-  private long voicemailsBackedupSoFar = 0;
-  private long sizeOfVoicemailsBackedupSoFar = 0;
-  private boolean maxVoicemailBackupReached = false;
-
-  /**
-   * onBackup is used for Key/Value backup. Since we are using Dolly/Android Auto backup, we do not
-   * need to implement this method and Dolly should not be calling this. Instead Dolly will be
-   * calling onFullBackup.
-   */
-  @Override
-  public void onBackup(
-      ParcelFileDescriptor parcelFileDescriptor,
-      BackupDataOutput backupDataOutput,
-      ParcelFileDescriptor parcelFileDescriptor1)
-      throws IOException {
-    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_BACKUP);
-    Assert.fail("Android Backup should not call DialerBackupAgent.onBackup");
-  }
-
-  /**
-   * onRestore is used for Key/Value restore. Since we are using Dolly/Android Auto backup/restore,
-   * we need to implement this method only for backwards compatibility. Dolly should be calling
-   * onFileRestore during its restore.
-   */
-  @Override
-  public void onRestore(
-      BackupDataInput backupDataInput, int i, ParcelFileDescriptor parcelFileDescriptor)
-      throws IOException {
-    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE);
-  }
-
-  @TargetApi(VERSION_CODES.M)
-  @Override
-  public void onFullBackup(FullBackupDataOutput data) throws IOException {
-    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_FULL_BACKUP);
-    LogUtil.i("DialerBackupAgent.onFullBackup", "performing dialer backup");
-
-    List<PhoneAccountHandle> phoneAccountsToArchive =
-        DialerBackupUtils.getPhoneAccountsToArchive(this);
-
-    if (!maxVoicemailBackupReached && !phoneAccountsToArchive.isEmpty()) {
-      voicemailsBackedupSoFar = 0;
-      sizeOfVoicemailsBackedupSoFar = 0;
-
-      LogUtil.i("DialerBackupAgent.onFullBackup", "autoBackup is enabled");
-      ContentResolver contentResolver = getContentResolver();
-      int limit = 1000;
-
-      Uri uri =
-          TelecomUtil.getCallLogUri(this)
-              .buildUpon()
-              .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit))
-              .build();
-
-      LogUtil.i("DialerBackupAgent.onFullBackup", "backing up from: " + uri);
-
-      try (Cursor cursor =
-          contentResolver.query(
-              uri,
-              null,
-              String.format(
-                  "(%s = ? AND deleted = 0 AND  %s = ? AND ?)",
-                  Calls.TYPE, Voicemails.SOURCE_PACKAGE),
-              new String[] {
-                Integer.toString(CallLog.Calls.VOICEMAIL_TYPE),
-                VOICEMAIL_SOURCE_PACKAGE,
-                DialerBackupUtils.getPhoneAccountClause(phoneAccountsToArchive)
-              },
-              ORDER_BY_DATE,
-              null)) {
-
-        if (cursor == null) {
-          LogUtil.i("DialerBackupAgent.onFullBackup", "cursor was null");
-          return;
-        }
-
-        LogUtil.i("DialerBackupAgent.onFullBackup", "cursor count: " + cursor.getCount());
-        if (cursor.moveToFirst()) {
-          int fileNum = 0;
-          do {
-            backupRow(
-                data, cursor, String.format(Locale.US, VOICEMAIL_BACKUP_FILE_FORMAT, fileNum++));
-          } while (cursor.moveToNext() && !maxVoicemailBackupReached);
-        } else {
-          LogUtil.i("DialerBackupAgent.onFullBackup", "cursor.moveToFirst failed");
-        }
-      }
-    }
-    LogUtil.i(
-        "DialerBackupAgent.onFullBackup",
-        "vm files backed up: %d, vm size backed up:%d, "
-            + "max vm backup reached:%b, phone accounts to archive: %d",
-        voicemailsBackedupSoFar,
-        sizeOfVoicemailsBackedupSoFar,
-        maxVoicemailBackupReached,
-        phoneAccountsToArchive.size());
-    super.onFullBackup(data);
-    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_FULL_BACKED_UP);
-  }
-
-  private void backupRow(FullBackupDataOutput data, Cursor cursor, String fileName)
-      throws IOException {
-
-    VoicemailInfo cursorRowInProto =
-        DialerBackupUtils.convertVoicemailCursorRowToProto(cursor, getContentResolver());
-
-    File file = new File(getFilesDir(), fileName);
-    DialerBackupUtils.writeProtoToFile(file, cursorRowInProto);
-
-    if (sizeOfVoicemailsBackedupSoFar + file.length()
-        > DialerBackupUtils.maxVoicemailSizeToBackup) {
-      Logger.get(this).logImpression(DialerImpression.Type.BACKUP_MAX_VM_BACKUP_REACHED);
-      maxVoicemailBackupReached = true;
-      file.delete();
-      return;
-    }
-
-    backupFile(file, data);
-  }
-
-  // TODO: Write to FullBackupDataOutput directly (b/33849960)
-  private void backupFile(File file, FullBackupDataOutput data) throws IOException {
-    try {
-      super.fullBackupFile(file, data);
-      sizeOfVoicemailsBackedupSoFar = sizeOfVoicemailsBackedupSoFar + file.length();
-      voicemailsBackedupSoFar++;
-      Logger.get(this).logImpression(DialerImpression.Type.BACKUP_VOICEMAIL_BACKED_UP);
-      LogUtil.i("DialerBackupAgent.backupFile", "file backed up:" + file.getAbsolutePath());
-    } finally {
-      file.delete();
-    }
-  }
-
-  // Being tracked in b/33839952
-  @Override
-  public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
-    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_QUOTA_EXCEEDED);
-    LogUtil.i("DialerBackupAgent.onQuotaExceeded", "does nothing");
-  }
-
-  @TargetApi(VERSION_CODES.M)
-  @Override
-  public void onRestoreFile(
-      ParcelFileDescriptor data, long size, File destination, int type, long mode, long mtime)
-      throws IOException {
-    LogUtil.i("DialerBackupAgent.onRestoreFile", "size:" + size + " destination: " + destination);
-
-    String fileName = destination.getName();
-    LogUtil.i("DialerBackupAgent.onRestoreFile", "file name: " + fileName);
-
-    if (fileName.endsWith(VOICEMAIL_BACKUP_FILE_SUFFIX)) {
-      if (DialerBackupUtils.canRestoreVoicemails(getContentResolver(), this)) {
-        try {
-          super.onRestoreFile(data, size, destination, type, mode, mtime);
-          restoreVoicemail(destination);
-          destination.delete();
-        } catch (IOException e) {
-          Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_IO_EXCEPTION);
-          LogUtil.e(
-              "DialerBackupAgent.onRestoreFile", "could not restore voicemail - IOException: ", e);
-        }
-      } else {
-        LogUtil.i("DialerBackupAgent.onRestoreFile", "build does not support restoring voicemails");
-      }
-
-    } else {
-      super.onRestoreFile(data, size, destination, type, mode, mtime);
-      LogUtil.i("DialerBackupAgent.onRestoreFile", "restored: " + fileName);
-      Logger.get(this).logImpression(DialerImpression.Type.BACKUP_RESTORED_FILE);
-    }
-  }
-
-  @Override
-  public void onRestoreFinished() {
-    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_FINISHED);
-    LogUtil.i("DialerBackupAgent.onRestoreFinished", "do nothing");
-  }
-
-  @TargetApi(VERSION_CODES.M)
-  private void restoreVoicemail(File file) throws IOException {
-    Pair<ContentValues, byte[]> pair =
-        DialerBackupUtils.convertVoicemailProtoFileToContentValueAndAudioBytes(
-            file, getApplicationContext());
-
-    if (pair == null) {
-      LogUtil.i("DialerBackupAgent.restoreVoicemail", "not restoring VM due to duplicate");
-      Logger.get(this)
-          .logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_VM_DUPLICATE_NOT_RESTORING);
-      return;
-    }
-
-    // TODO: Uniquely identify backup agent as the creator of this voicemail b/34084298
-    try (OutputStream restoreStream =
-        getContentResolver()
-            .openOutputStream(
-                getContentResolver()
-                    .insert(VoicemailContract.Voicemails.CONTENT_URI, pair.first))) {
-      DialerBackupUtils.copyAudioBytesToContentUri(pair.second, restoreStream);
-      Logger.get(this).logImpression(DialerImpression.Type.BACKUP_RESTORED_VOICEMAIL);
-    }
-  }
-}
diff --git a/java/com/android/dialer/backup/DialerBackupUtils.java b/java/com/android/dialer/backup/DialerBackupUtils.java
deleted file mode 100644
index fe714f6..0000000
--- a/java/com/android/dialer/backup/DialerBackupUtils.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dialer.backup;
-
-import android.annotation.TargetApi;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build.VERSION_CODES;
-import android.provider.VoicemailContract;
-import android.provider.VoicemailContract.Voicemails;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.util.Pair;
-import com.android.dialer.common.Assert;
-import com.android.dialer.common.LogUtil;
-import com.android.dialer.configprovider.ConfigProviderBindings;
-import com.android.voicemail.VoicemailComponent;
-import com.google.common.io.ByteStreams;
-import com.google.common.io.Files;
-import com.google.protobuf.ByteString;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Helper functions for DialerBackupAgent */
-public class DialerBackupUtils {
-  // Backup voicemails up to 20MB
-  static long maxVoicemailSizeToBackup = 20000000L;
-  static final String RESTORED_COLUMN = "restored";
-
-  private DialerBackupUtils() {}
-
-  public static void copyAudioBytesToContentUri(
-      @NonNull byte[] audioBytesArray, @NonNull OutputStream restoreStream) throws IOException {
-    LogUtil.i("DialerBackupUtils.copyStream", "audioByteArray length: " + audioBytesArray.length);
-
-    ByteArrayInputStream decodedStream = new ByteArrayInputStream(audioBytesArray);
-    LogUtil.i(
-        "DialerBackupUtils.copyStream", "decodedStream.available: " + decodedStream.available());
-
-    ByteStreams.copy(decodedStream, restoreStream);
-  }
-
-  public static @Nullable ByteString audioStreamToByteString(@NonNull InputStream stream)
-      throws IOException {
-    if (stream.available() > 0) {
-      return ByteString.readFrom(stream);
-    } else {
-      LogUtil.i("DialerBackupUtils.audioStreamToByteArray", "no audio stream to backup");
-    }
-    return ByteString.EMPTY;
-  }
-
-  public static void writeProtoToFile(@NonNull File file, @NonNull VoicemailInfo voicemailInfo)
-      throws IOException {
-    LogUtil.i(
-        "DialerBackupUtils.writeProtoToFile",
-        "backup " + voicemailInfo + " to " + file.getAbsolutePath());
-
-    byte[] bytes = voicemailInfo.toByteArray();
-    Files.write(bytes, file);
-  }
-
-  /** Only restore voicemails that have the restored column in calllog (NMR2+ builds) */
-  @TargetApi(VERSION_CODES.M)
-  public static boolean canRestoreVoicemails(ContentResolver contentResolver, Context context) {
-    try (Cursor cursor = contentResolver.query(Voicemails.CONTENT_URI, null, null, null, null)) {
-      // Restored column only exists in NMR2 and above builds.
-      if (cursor.getColumnIndex(RESTORED_COLUMN) != -1) {
-        LogUtil.i("DialerBackupUtils.canRestoreVoicemails", "Build supports restore");
-        return true;
-      } else {
-        LogUtil.i("DialerBackupUtils.canRestoreVoicemails", "Build does not support restore");
-        return false;
-      }
-    }
-  }
-
-  public static VoicemailInfo protoFileToVoicemailInfo(@NonNull File file) throws IOException {
-    byte[] byteArray = Files.toByteArray(file);
-    return VoicemailInfo.parseFrom(byteArray);
-  }
-
-  @TargetApi(VERSION_CODES.M)
-  public static VoicemailInfo convertVoicemailCursorRowToProto(
-      @NonNull Cursor cursor, @NonNull ContentResolver contentResolver) throws IOException {
-
-    VoicemailInfo.Builder voicemailInfo = VoicemailInfo.newBuilder();
-
-    for (int i = 0; i < cursor.getColumnCount(); ++i) {
-      String name = cursor.getColumnName(i);
-      String value = cursor.getString(i);
-
-      LogUtil.i(
-          "DialerBackupUtils.convertVoicemailCursorRowToProto",
-          "column index: %d, column name: %s, column value: %s",
-          i,
-          name,
-          value);
-
-      switch (name) {
-        case Voicemails.DATE:
-          voicemailInfo.setDate(value);
-          break;
-        case Voicemails.DELETED:
-          voicemailInfo.setDeleted(value);
-          break;
-        case Voicemails.DIRTY:
-          voicemailInfo.setDirty(value);
-          break;
-        case Voicemails.DIR_TYPE:
-          voicemailInfo.setDirType(value);
-          break;
-        case Voicemails.DURATION:
-          voicemailInfo.setDuration(value);
-          break;
-        case Voicemails.HAS_CONTENT:
-          voicemailInfo.setHasContent(value);
-          break;
-        case Voicemails.IS_READ:
-          voicemailInfo.setIsRead(value);
-          break;
-        case Voicemails.ITEM_TYPE:
-          voicemailInfo.setItemType(value);
-          break;
-        case Voicemails.LAST_MODIFIED:
-          voicemailInfo.setLastModified(value);
-          break;
-        case Voicemails.MIME_TYPE:
-          voicemailInfo.setMimeType(value);
-          break;
-        case Voicemails.NUMBER:
-          voicemailInfo.setNumber(value);
-          break;
-        case Voicemails.PHONE_ACCOUNT_COMPONENT_NAME:
-          voicemailInfo.setPhoneAccountComponentName(value);
-          break;
-        case Voicemails.PHONE_ACCOUNT_ID:
-          voicemailInfo.setPhoneAccountId(value);
-          break;
-        case Voicemails.SOURCE_DATA:
-          voicemailInfo.setSourceData(value);
-          break;
-        case Voicemails.SOURCE_PACKAGE:
-          voicemailInfo.setSourcePackage(value);
-          break;
-        case Voicemails.TRANSCRIPTION:
-          voicemailInfo.setTranscription(value);
-          break;
-        case DialerBackupAgent.VOICEMAIL_URI:
-          try (InputStream audioStream = contentResolver.openInputStream(Uri.parse(value))) {
-            voicemailInfo.setEncodedVoicemailKey(audioStreamToByteString(audioStream));
-          }
-          break;
-        default:
-          LogUtil.i(
-              "DialerBackupUtils.convertVoicemailCursorRowToProto",
-              "Not backing up column: %s, with value: %s",
-              name,
-              value);
-          break;
-      }
-    }
-    return voicemailInfo.build();
-  }
-
-  public static Pair<ContentValues, byte[]> convertVoicemailProtoFileToContentValueAndAudioBytes(
-      @NonNull File file, Context context) throws IOException {
-
-    VoicemailInfo voicemailInfo = DialerBackupUtils.protoFileToVoicemailInfo(file);
-    LogUtil.i(
-        "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio",
-        "file name: "
-            + file.getName()
-            + " voicemailInfo size: "
-            + voicemailInfo.getSerializedSize());
-
-    if (isDuplicate(context, voicemailInfo)) {
-      LogUtil.i(
-          "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio",
-          "voicemail already exists");
-      return null;
-    } else {
-      ContentValues contentValues = new ContentValues();
-
-      if (voicemailInfo.hasDate()) {
-        contentValues.put(Voicemails.DATE, voicemailInfo.getDate());
-      }
-      if (voicemailInfo.hasDeleted()) {
-        contentValues.put(Voicemails.DELETED, voicemailInfo.getDeleted());
-      }
-      if (!voicemailInfo.hasDirty()) {
-        contentValues.put(Voicemails.DIRTY, voicemailInfo.getDirty());
-      }
-      if (!voicemailInfo.hasDuration()) {
-        contentValues.put(Voicemails.DURATION, voicemailInfo.getDuration());
-      }
-      if (!voicemailInfo.hasIsRead()) {
-        contentValues.put(Voicemails.IS_READ, voicemailInfo.getIsRead());
-      }
-      if (!voicemailInfo.hasLastModified()) {
-        contentValues.put(Voicemails.LAST_MODIFIED, voicemailInfo.getLastModified());
-      }
-      if (!voicemailInfo.hasMimeType()) {
-        contentValues.put(Voicemails.MIME_TYPE, voicemailInfo.getMimeType());
-      }
-      if (!voicemailInfo.hasNumber()) {
-        contentValues.put(Voicemails.NUMBER, voicemailInfo.getNumber());
-      }
-      if (!voicemailInfo.hasPhoneAccountComponentName()) {
-        contentValues.put(
-            Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, voicemailInfo.getPhoneAccountComponentName());
-      }
-      if (!voicemailInfo.hasPhoneAccountId()) {
-        contentValues.put(Voicemails.PHONE_ACCOUNT_ID, voicemailInfo.getPhoneAccountId());
-      }
-      if (!voicemailInfo.hasSourceData()) {
-        contentValues.put(Voicemails.SOURCE_DATA, voicemailInfo.getSourceData());
-      }
-      if (!voicemailInfo.hasSourcePackage()) {
-        contentValues.put(Voicemails.SOURCE_PACKAGE, voicemailInfo.getSourcePackage());
-      }
-      if (!voicemailInfo.hasTranscription()) {
-        contentValues.put(Voicemails.TRANSCRIPTION, voicemailInfo.getTranscription());
-      }
-      contentValues.put(VoicemailContract.Voicemails.HAS_CONTENT, 1);
-      contentValues.put(RESTORED_COLUMN, "1");
-      contentValues.put(Voicemails.SOURCE_PACKAGE, getSourcePackage(context, voicemailInfo));
-
-      LogUtil.i(
-          "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio",
-          "cv: " + contentValues);
-
-      return Pair.create(contentValues, voicemailInfo.getEncodedVoicemailKey().toByteArray());
-    }
-  }
-
-  /**
-   * We should be using the system package name as the source package if there is no endless VM/VM
-   * archive present on the device. This is to separate pre-O (no endless VM) and O+ (endless VM)
-   * devices. This ensures that the source of truth for VMs is the VM server when endless VM is not
-   * enabled, and when endless VM/archived VMs is present, the source of truth for VMs is the device
-   * itself.
-   */
-  private static String getSourcePackage(Context context, VoicemailInfo voicemailInfo) {
-    if (ConfigProviderBindings.get(context)
-        .getBoolean("voicemail_restore_force_system_source_package", false)) {
-      LogUtil.i("DialerBackupUtils.getSourcePackage", "forcing system source package");
-      return "com.android.phone";
-    }
-    if (ConfigProviderBindings.get(context)
-        .getBoolean("voicemail_restore_check_archive_for_source_package", true)) {
-      if ("1".equals(voicemailInfo.getArchived())) {
-        LogUtil.i(
-            "DialerBackupUtils.getSourcePackage",
-            "voicemail was archived, using app source package");
-        // Using our app's source package will prevent the archived voicemail from being deleted by
-        // the system when it syncs with the voicemail server. In most cases the user will not see
-        // duplicate voicemails because this voicemail was archived and likely deleted from the
-        // voicemail server.
-        return context.getPackageName();
-      } else {
-        // Use the system source package. This means that if the voicemail is not present on the
-        // voicemail server then the system will delete it when it syncs.
-        LogUtil.i(
-            "DialerBackupUtils.getSourcePackage",
-            "voicemail was not archived, using system source package");
-        return "com.android.phone";
-      }
-    }
-    // Use our app's source package. This means that if the system syncs voicemail from the server
-    // the user could potentially get duplicate voicemails.
-    LogUtil.i("DialerBackupUtils.getSourcePackage", "defaulting to using app source package");
-    return context.getPackageName();
-  }
-
-  @TargetApi(VERSION_CODES.M)
-  private static boolean isDuplicate(Context context, VoicemailInfo voicemailInfo) {
-    // This checks for VM that might already exist, and doesn't restore them
-    try (Cursor cursor =
-        context
-            .getContentResolver()
-            .query(
-                VoicemailContract.Voicemails.CONTENT_URI,
-                null,
-                String.format(
-                    "(%s = ? AND %s = ? AND %s = ?)",
-                    Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION),
-                new String[] {
-                  voicemailInfo.getNumber(), voicemailInfo.getDate(), voicemailInfo.getDuration()
-                },
-                null,
-                null)) {
-      if (cursor.moveToFirst()
-          && ConfigProviderBindings.get(context)
-              .getBoolean("enable_vm_restore_no_duplicate", true)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  public static String getPhoneAccountClause(List<PhoneAccountHandle> phoneAccountsToArchive) {
-    Assert.checkArgument(!phoneAccountsToArchive.isEmpty());
-    StringBuilder whereQuery = new StringBuilder();
-
-    whereQuery.append("(");
-
-    for (int i = 0; i < phoneAccountsToArchive.size(); i++) {
-      whereQuery.append(
-          Voicemails.PHONE_ACCOUNT_ID + " = " + phoneAccountsToArchive.get(i).getId());
-
-      if (phoneAccountsToArchive.size() > 1 && i < phoneAccountsToArchive.size() - 1) {
-        whereQuery.append(" OR ");
-      }
-    }
-    whereQuery.append(")");
-    return whereQuery.toString();
-  }
-
-  public static List<PhoneAccountHandle> getPhoneAccountsToArchive(Context context) {
-    List<PhoneAccountHandle> phoneAccountsToBackUp = new ArrayList<>();
-
-    for (PhoneAccountHandle handle :
-        context.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts()) {
-
-      if (VoicemailComponent.get(context)
-          .getVoicemailClient()
-          .isVoicemailArchiveEnabled(context, handle)) {
-        phoneAccountsToBackUp.add(handle);
-        LogUtil.i(
-            "DialerBackupUtils.getPhoneAccountsToArchive", "enabled for: " + handle.toString());
-      } else {
-        LogUtil.i(
-            "DialerBackupUtils.getPhoneAccountsToArchive", "not enabled for: " + handle.toString());
-      }
-    }
-    return phoneAccountsToBackUp;
-  }
-}
diff --git a/java/com/android/dialer/backup/DialerPersistentBackupAgent.java b/java/com/android/dialer/backup/DialerPersistentBackupAgent.java
new file mode 100644
index 0000000..085c343
--- /dev/null
+++ b/java/com/android/dialer/backup/DialerPersistentBackupAgent.java
@@ -0,0 +1,84 @@
+/*
+ * 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.dialer.backup;
+
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.util.ArrayMap;
+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.google.android.libraries.backup.BackupKeyPredicate;
+import com.google.android.libraries.backup.BackupKeyPredicates;
+import com.google.android.libraries.backup.PersistentBackupAgentHelper;
+import java.io.IOException;
+import java.util.Map;
+
+/** Implementation of Key/Value Backup that powers Dialer's backup and restore. */
+public class DialerPersistentBackupAgent extends PersistentBackupAgentHelper {
+
+  private static final String DEFAULT_SHARED_PREFS_NAME = "com.google.android.dialer_preferences";
+
+  @NonNull private final String sharedPrefsName;
+
+  @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+  DialerPersistentBackupAgent(@NonNull String sharedPreferenceName) {
+    this.sharedPrefsName = Assert.isNotNull(sharedPreferenceName);
+    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_KEY_VALUE_BACKUP_AGENT_CONSTRUCTOR);
+  }
+
+  public DialerPersistentBackupAgent() {
+    this(DEFAULT_SHARED_PREFS_NAME);
+  }
+
+  @Override
+  public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor stateFile)
+      throws IOException {
+    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_KEY_VALUE_ON_RESTORE);
+    LogUtil.i("DialerPersistentBackupAgent.onRestore", "restore from version: " + appVersionCode);
+    super.onRestore(data, appVersionCode, stateFile);
+  }
+
+  @Override
+  public void onBackup(
+      ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)
+      throws IOException {
+    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_KEY_VALUE_ON_BACKUP);
+    LogUtil.i("DialerPersistentBackupAgent.onBackup", "onBackup being performed");
+    super.onBackup(oldState, data, newState);
+  }
+
+  @Override
+  public Map<String, BackupKeyPredicate> getBackupSpecification() {
+    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_KEY_VALUE_GET_BACKUP_SPECIFICATION);
+    LogUtil.i(
+        "DialerPersistentBackupAgent.getBackupSpecification",
+        "file being backed up: " + sharedPrefsName);
+    Map<String, BackupKeyPredicate> backupSpecification = new ArrayMap<>();
+    backupSpecification.put(sharedPrefsName, BackupKeyPredicates.alwaysTrue());
+    return backupSpecification;
+  }
+
+  @Override
+  public void onRestoreFinished() {
+    Logger.get(this).logImpression(DialerImpression.Type.BACKUP_KEY_VALUE_ON_RESTORE_FINISHED);
+    super.onRestoreFinished();
+  }
+}
diff --git a/java/com/android/dialer/backup/proto/voicemail_info.proto b/java/com/android/dialer/backup/proto/voicemail_info.proto
deleted file mode 100644
index 7497683..0000000
--- a/java/com/android/dialer/backup/proto/voicemail_info.proto
+++ /dev/null
@@ -1,31 +0,0 @@
-syntax = "proto2";
-
-option java_package = "com.android.dialer.backup";
-option java_multiple_files = true;
-option optimize_for = LITE_RUNTIME;
-
-
-package com.android.dialer.backup;
-
-// Next id: 20
-message VoicemailInfo {
-  optional string date = 1;
-  optional string deleted = 2;
-  optional string dirty = 3;
-  optional string dir_type = 4;
-  optional string duration = 5;
-  optional string has_content = 6;
-  optional string is_read = 7;
-  optional string item_type = 8;
-  optional string last_modified = 9;
-  optional string mime_type = 10;
-  optional string number = 11;
-  optional string phone_account_component_name = 12;
-  optional string phone_account_id = 13;
-  optional string source_data = 14;
-  optional string source_package = 15;
-  optional string transcription = 16;
-  optional string voicemail_uri = 17;
-  optional bytes encoded_voicemail_key = 18;
-  optional string archived = 19;
-}
diff --git a/java/com/android/dialer/contactsfragment/ContactsFragment.java b/java/com/android/dialer/contactsfragment/ContactsFragment.java
index 41fa8f9..50c9fe8 100644
--- a/java/com/android/dialer/contactsfragment/ContactsFragment.java
+++ b/java/com/android/dialer/contactsfragment/ContactsFragment.java
@@ -122,8 +122,10 @@
       emptyContentView.setDescription(R.string.all_contacts_empty);
       emptyContentView.setActionLabel(R.string.all_contacts_empty_add_contact_action);
       emptyContentView.setVisibility(View.VISIBLE);
+      recyclerView.setVisibility(View.GONE);
     } else {
       emptyContentView.setVisibility(View.GONE);
+      recyclerView.setVisibility(View.VISIBLE);
       adapter = new ContactsAdapter(getContext(), cursor);
       manager =
           new LinearLayoutManager(getContext()) {
diff --git a/java/com/android/dialer/logging/dialer_impression.proto b/java/com/android/dialer/logging/dialer_impression.proto
index 2f89a3a..c185ad4 100644
--- a/java/com/android/dialer/logging/dialer_impression.proto
+++ b/java/com/android/dialer/logging/dialer_impression.proto
@@ -249,24 +249,24 @@
     VOICEMAIL_CONFIGURATION_STATE_CORRUPTION_DETECTED_FROM_NOTIFICATION = 1080;
 
     // Android Auto Backup and Restore (Dolly)
-    BACKUP_ON_BACKUP = 1081;
-    BACKUP_ON_FULL_BACKUP = 1082;
-    BACKUP_ON_BACKUP_DISABLED = 1083;
-    BACKUP_VOICEMAIL_BACKED_UP = 1084;
-    BACKUP_FULL_BACKED_UP = 1085;
-    BACKUP_ON_BACKUP_JSON_EXCEPTION = 1086;
+    BACKUP_ON_BACKUP = 1081 [deprecated = true];
+    BACKUP_ON_FULL_BACKUP = 1082 [deprecated = true];
+    BACKUP_ON_BACKUP_DISABLED = 1083 [deprecated = true];
+    BACKUP_VOICEMAIL_BACKED_UP = 1084 [deprecated = true];
+    BACKUP_FULL_BACKED_UP = 1085 [deprecated = true];
+    BACKUP_ON_BACKUP_JSON_EXCEPTION = 1086 [deprecated = true];
 
-    BACKUP_ON_QUOTA_EXCEEDED = 1087;
+    BACKUP_ON_QUOTA_EXCEEDED = 1087 [deprecated = true];
 
-    BACKUP_ON_RESTORE = 1088;
-    BACKUP_RESTORED_FILE = 1089;
-    BACKUP_RESTORED_VOICEMAIL = 1090;
-    BACKUP_ON_RESTORE_FINISHED = 1091;
-    BACKUP_ON_RESTORE_DISABLED = 1092;
-    BACKUP_ON_RESTORE_JSON_EXCEPTION = 1093;
-    BACKUP_ON_RESTORE_IO_EXCEPTION = 1094;
+    BACKUP_ON_RESTORE = 1088 [deprecated = true];
+    BACKUP_RESTORED_FILE = 1089 [deprecated = true];
+    BACKUP_RESTORED_VOICEMAIL = 1090 [deprecated = true];
+    BACKUP_ON_RESTORE_FINISHED = 1091 [deprecated = true];
+    BACKUP_ON_RESTORE_DISABLED = 1092 [deprecated = true];
+    BACKUP_ON_RESTORE_JSON_EXCEPTION = 1093 [deprecated = true];
+    BACKUP_ON_RESTORE_IO_EXCEPTION = 1094 [deprecated = true];
 
-    BACKUP_MAX_VM_BACKUP_REACHED = 1095;
+    BACKUP_MAX_VM_BACKUP_REACHED = 1095 [deprecated = true];
 
     EVENT_ANSWER_HINT_ACTIVATED = 1096;
     EVENT_ANSWER_HINT_DEACTIVATED = 1097;
@@ -280,7 +280,7 @@
     INCOMING_VIDEO_CALL = 1102;
     USER_PARTICIPATED_IN_A_VIDEO_CALL = 1103;
 
-    BACKUP_ON_RESTORE_VM_DUPLICATE_NOT_RESTORING = 1104;
+    BACKUP_ON_RESTORE_VM_DUPLICATE_NOT_RESTORING = 1104 [deprecated = true];
 
     // User tapped the 'Share and call' button to start the call composer
     CALL_LOG_SHARE_AND_CALL = 1105;
@@ -455,5 +455,12 @@
 
     // In in call UI
     UPGRADE_TO_VIDEO_CALL_BUTTON_SHOWN = 1236;
+
+    // Dialer Key/Value Backup and Restore
+    BACKUP_KEY_VALUE_ON_BACKUP = 1239;
+    BACKUP_KEY_VALUE_ON_RESTORE = 1240;
+    BACKUP_KEY_VALUE_ON_RESTORE_FINISHED = 1241;
+    BACKUP_KEY_VALUE_GET_BACKUP_SPECIFICATION = 1242;
+    BACKUP_KEY_VALUE_BACKUP_AGENT_CONSTRUCTOR = 1243;
   }
 }
diff --git a/java/com/android/incallui/incall/impl/ButtonChooser.java b/java/com/android/incallui/incall/impl/ButtonChooser.java
index 55b82f0..095a8be 100644
--- a/java/com/android/incallui/incall/impl/ButtonChooser.java
+++ b/java/com/android/incallui/incall/impl/ButtonChooser.java
@@ -18,6 +18,7 @@
 
 import android.support.annotation.NonNull;
 import com.android.dialer.common.Assert;
+import com.android.incallui.incall.impl.MappedButtonConfig.MappingInfo;
 import com.android.incallui.incall.protocol.InCallButtonIds;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -103,12 +104,29 @@
       if (placedButtons.size() >= numUiButtons) {
         return;
       }
+
       // If the conflict button is allowed but disabled, don't place it since it probably will
       // move when it's enabled.
       if (!allowedButtons.contains(conflict) || disabledButtons.contains(conflict)) {
         continue;
       }
+
+      if (isMutuallyExclusiveButtonAvailable(
+          config.lookupMappingInfo(conflict).getMutuallyExclusiveButton(), allowedButtons)) {
+        continue;
+      }
       placedButtons.add(conflict);
     }
   }
+
+  private boolean isMutuallyExclusiveButtonAvailable(
+      int mutuallyExclusiveButton, @NonNull Set<Integer> allowedButtons) {
+    if (mutuallyExclusiveButton == MappingInfo.NO_MUTUALLY_EXCLUSIVE_BUTTON_SET) {
+      return false;
+    }
+    if (allowedButtons.contains(mutuallyExclusiveButton)) {
+      return true;
+    }
+    return false;
+  }
 }
diff --git a/java/com/android/incallui/incall/impl/ButtonChooserFactory.java b/java/com/android/incallui/incall/impl/ButtonChooserFactory.java
index 99364e2..0f4a95d 100644
--- a/java/com/android/incallui/incall/impl/ButtonChooserFactory.java
+++ b/java/com/android/incallui/incall/impl/ButtonChooserFactory.java
@@ -74,6 +74,12 @@
     mapping.put(
         InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, MappingInfo.builder(4).setSlotOrder(10).build());
     mapping.put(InCallButtonIds.BUTTON_SWAP, MappingInfo.builder(5).setSlotOrder(0).build());
+    mapping.put(
+        InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY,
+        MappingInfo.builder(5)
+            .setSlotOrder(Integer.MAX_VALUE)
+            .setMutuallyExclusiveButton(InCallButtonIds.BUTTON_SWAP)
+            .build());
 
     return new ButtonChooser(new MappedButtonConfig(mapping));
   }
diff --git a/java/com/android/incallui/incall/impl/MappedButtonConfig.java b/java/com/android/incallui/incall/impl/MappedButtonConfig.java
index 7229837..67c4137 100644
--- a/java/com/android/incallui/incall/impl/MappedButtonConfig.java
+++ b/java/com/android/incallui/incall/impl/MappedButtonConfig.java
@@ -141,7 +141,7 @@
   }
 
   @NonNull
-  private MappingInfo lookupMappingInfo(@InCallButtonIds int button) {
+  public MappingInfo lookupMappingInfo(@InCallButtonIds int button) {
     MappingInfo info = mapping.get(button);
     if (info == null) {
       throw new IllegalArgumentException(
@@ -154,6 +154,8 @@
   @AutoValue
   abstract static class MappingInfo {
 
+    public static final int NO_MUTUALLY_EXCLUSIVE_BUTTON_SET = -1;
+
     /** The Ui slot into which a given button desires to be placed. */
     public abstract int getSlot();
 
@@ -171,11 +173,20 @@
      */
     public abstract int getConflictOrder();
 
+    /**
+     * Returns an integer representing a button for which the given button conflicts. Defaults to
+     * {@link NO_MUTUALLY_EXCLUSIVE_BUTTON_SET}.
+     *
+     * <p>If the mutually exclusive button is chosen, the associated button should never be chosen.
+     */
+    public abstract @InCallButtonIds int getMutuallyExclusiveButton();
+
     static Builder builder(int slot) {
       return new AutoValue_MappedButtonConfig_MappingInfo.Builder()
           .setSlot(slot)
           .setSlotOrder(Integer.MAX_VALUE)
-          .setConflictOrder(Integer.MAX_VALUE);
+          .setConflictOrder(Integer.MAX_VALUE)
+          .setMutuallyExclusiveButton(NO_MUTUALLY_EXCLUSIVE_BUTTON_SET);
     }
 
     /** Class used to build instances of {@link MappingInfo}. */
@@ -187,6 +198,8 @@
 
       public abstract Builder setConflictOrder(int conflictOrder);
 
+      public abstract Builder setMutuallyExclusiveButton(@InCallButtonIds int button);
+
       public abstract MappingInfo build();
     }
   }
diff --git a/java/com/android/incallui/video/impl/VideoCallFragment.java b/java/com/android/incallui/video/impl/VideoCallFragment.java
index 203710e..ab31f76 100644
--- a/java/com/android/incallui/video/impl/VideoCallFragment.java
+++ b/java/com/android/incallui/video/impl/VideoCallFragment.java
@@ -21,7 +21,6 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Matrix;
 import android.graphics.Outline;
 import android.graphics.Point;
 import android.graphics.drawable.Animatable;
@@ -284,7 +283,7 @@
               int oldRight,
               int oldBottom) {
             LogUtil.i("VideoCallFragment.onLayoutChange", "previewTextureView layout changed");
-            fixPreviewRotation();
+            updatePreviewVideoScaling();
             updatePreviewOffView();
           }
         });
@@ -689,11 +688,13 @@
   @Override
   public void onLocalVideoDimensionsChanged() {
     LogUtil.i("VideoCallFragment.onLocalVideoDimensionsChanged", null);
+    updatePreviewVideoScaling();
   }
 
   @Override
   public void onLocalVideoOrientationChanged() {
     LogUtil.i("VideoCallFragment.onLocalVideoOrientationChanged", null);
+    updatePreviewVideoScaling();
   }
 
   /** Called when the remote video's dimensions change. */
@@ -961,15 +962,31 @@
     // Do nothing
   }
 
-  private void fixPreviewRotation() {
-    int rotationDegrees = getRotationDegrees();
-    if (rotationDegrees == 90 || rotationDegrees == 270) {
-      int viewWidth = previewTextureView.getWidth();
-      int viewHeight = previewTextureView.getHeight();
-      Matrix transform = new Matrix();
-      // Multiplying by -1 prevents the image from being upside down in landscape mode.
-      transform.postRotate(rotationDegrees * -1.0f, viewWidth / 2.0f, viewHeight / 2.0f);
-      previewTextureView.setTransform(transform);
+  private void updatePreviewVideoScaling() {
+    if (previewTextureView.getWidth() == 0 || previewTextureView.getHeight() == 0) {
+      LogUtil.i("VideoCallFragment.updatePreviewVideoScaling", "view layout hasn't finished yet");
+      return;
+    }
+    VideoSurfaceTexture localVideoSurfaceTexture =
+        videoCallScreenDelegate.getLocalVideoSurfaceTexture();
+    Point cameraDimensions = localVideoSurfaceTexture.getSurfaceDimensions();
+    if (cameraDimensions == null) {
+      LogUtil.i(
+          "VideoCallFragment.updatePreviewVideoScaling", "camera dimensions haven't been set");
+      return;
+    }
+    if (isLandscape()) {
+      VideoSurfaceBindings.scaleVideoAndFillView(
+          previewTextureView,
+          cameraDimensions.x,
+          cameraDimensions.y,
+          videoCallScreenDelegate.getDeviceOrientation());
+    } else {
+      VideoSurfaceBindings.scaleVideoAndFillView(
+          previewTextureView,
+          cameraDimensions.y,
+          cameraDimensions.x,
+          videoCallScreenDelegate.getDeviceOrientation());
     }
   }
 
@@ -1006,22 +1023,6 @@
     return rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270;
   }
 
-  private int getRotationDegrees() {
-    int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
-    switch (rotation) {
-      case Surface.ROTATION_0:
-        return 0;
-      case Surface.ROTATION_90:
-        return 90;
-      case Surface.ROTATION_180:
-        return 180;
-      case Surface.ROTATION_270:
-        return 270;
-      default:
-        throw Assert.createAssertionFailException("unsupported rotation: " + rotation);
-    }
-  }
-
   private void enterGreenScreenMode() {
     LogUtil.i("VideoCallFragment.enterGreenScreenMode", null);
     RelativeLayout.LayoutParams params =
diff --git a/java/com/android/incallui/videosurface/impl/VideoSurfaceTextureImpl.java b/java/com/android/incallui/videosurface/impl/VideoSurfaceTextureImpl.java
index 1af7dff..8cac402 100644
--- a/java/com/android/incallui/videosurface/impl/VideoSurfaceTextureImpl.java
+++ b/java/com/android/incallui/videosurface/impl/VideoSurfaceTextureImpl.java
@@ -67,6 +67,9 @@
         "VideoSurfaceTextureImpl.setSurfaceDimensions",
         "surfaceDimensions: " + surfaceDimensions + " " + toString());
     this.surfaceDimensions = surfaceDimensions;
+    if (surfaceDimensions != null && savedSurfaceTexture != null) {
+      savedSurfaceTexture.setDefaultBufferSize(surfaceDimensions.x, surfaceDimensions.y);
+    }
   }
 
   @Override
diff --git a/java/com/android/voicemail/impl/AndroidManifest.xml b/java/com/android/voicemail/impl/AndroidManifest.xml
index 49d93f5..db5369e 100644
--- a/java/com/android/voicemail/impl/AndroidManifest.xml
+++ b/java/com/android/voicemail/impl/AndroidManifest.xml
@@ -18,7 +18,6 @@
   package="com.android.voicemail.impl">
 
   <application
-    android:allowBackup="false"
     android:supportsRtl="true"
     android:usesCleartextTraffic="true"
     android:defaultToDeviceProtectedStorage="true"