internal change

Bug: 62423454
Test: manual and updated unit test
PiperOrigin-RevId: 173731907
Change-Id: Ic73600197b1c4fa6ac0937a8c38b048cd8faded8
diff --git a/java/com/android/voicemail/VoicemailClient.java b/java/com/android/voicemail/VoicemailClient.java
index d52a67a..717362e 100644
--- a/java/com/android/voicemail/VoicemailClient.java
+++ b/java/com/android/voicemail/VoicemailClient.java
@@ -125,6 +125,16 @@
       Context context, PhoneAccountHandle phoneAccountHandle, boolean value);
 
   /**
+   * @return if the voicemail transcription feature is available on the current device. This depends
+   *     on whether the server side flag is turned on for the feature, and if the OS meets the
+   *     requirement for this feature.
+   */
+  boolean isVoicemailTranscriptionAvailable(Context context);
+
+  /** @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.
    */
diff --git a/java/com/android/voicemail/impl/VoicemailClientImpl.java b/java/com/android/voicemail/impl/VoicemailClientImpl.java
index 9bb14f2..ff1a18d 100644
--- a/java/com/android/voicemail/impl/VoicemailClientImpl.java
+++ b/java/com/android/voicemail/impl/VoicemailClientImpl.java
@@ -37,6 +37,7 @@
 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;
 import java.util.List;
 import javax.inject.Inject;
 
@@ -120,6 +121,30 @@
   }
 
   @Override
+  public boolean isVoicemailTranscriptionAvailable(Context context) {
+    if (!BuildCompat.isAtLeastO()) {
+      LogUtil.i(
+          "VoicemailClientImpl.isVoicemailTranscriptionAvailable", "not running on O or later");
+      return false;
+    }
+
+    TranscriptionConfigProvider provider = new TranscriptionConfigProvider(context);
+    if (!provider.isVoicemailTranscriptionEnabled()) {
+      LogUtil.i(
+          "VoicemailClientImpl.isVoicemailTranscriptionAvailable", "feature disabled by config");
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public boolean isVoicemailDonationEnabled(Context context, PhoneAccountHandle account) {
+    return isVoicemailTranscriptionAvailable(context)
+        && VisualVoicemailSettingsUtil.isVoicemailDonationEnabled(context, account);
+  }
+
+  @Override
   public Intent getSetPinIntent(Context context, PhoneAccountHandle phoneAccountHandle) {
     Intent intent = new Intent(context, VoicemailChangePinActivity.class);
     intent.putExtra(VoicemailChangePinActivity.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
diff --git a/java/com/android/voicemail/impl/fetch/VoicemailFetchedCallback.java b/java/com/android/voicemail/impl/fetch/VoicemailFetchedCallback.java
index 3e82540..e8e14be 100644
--- a/java/com/android/voicemail/impl/fetch/VoicemailFetchedCallback.java
+++ b/java/com/android/voicemail/impl/fetch/VoicemailFetchedCallback.java
@@ -97,7 +97,8 @@
     if (updateVoicemail(values)) {
       ThreadUtil.postOnUiThread(
           () -> {
-            if (!TranscriptionService.scheduleNewVoicemailTranscriptionJob(mContext, mUri, true)) {
+            if (!TranscriptionService.scheduleNewVoicemailTranscriptionJob(
+                mContext, mUri, mPhoneAccountHandle, true)) {
               VvmLog.w(TAG, String.format("Failed to schedule transcription for %s", mUri));
             }
           });
diff --git a/java/com/android/voicemail/impl/res/values/strings.xml b/java/com/android/voicemail/impl/res/values/strings.xml
index 66c0e62..c70306d 100644
--- a/java/com/android/voicemail/impl/res/values/strings.xml
+++ b/java/com/android/voicemail/impl/res/values/strings.xml
@@ -42,6 +42,9 @@
     <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>
 
@@ -53,6 +56,20 @@
     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] -->
diff --git a/java/com/android/voicemail/impl/res/xml/voicemail_settings.xml b/java/com/android/voicemail/impl/res/xml/voicemail_settings.xml
index 5051090..9b0391a 100644
--- a/java/com/android/voicemail/impl/res/xml/voicemail_settings.xml
+++ b/java/com/android/voicemail/impl/res/xml/voicemail_settings.xml
@@ -29,6 +29,12 @@
     android:key="@string/voicemail_visual_voicemail_archive_key"
     android:dependency="@string/voicemail_visual_voicemail_key"
     android:title="@string/voicemail_visual_voicemail_auto_archive_switch_title"/>"
+
+  <SwitchPreference
+    android:key="@string/voicemail_visual_voicemail_donation_key"
+    android:dependency="@string/voicemail_visual_voicemail_key"
+    android:title="@string/voicemail_visual_voicemail_donation_switch_title"/>"
+
   <Preference
     android:key="@string/voicemail_change_pin_key"
     android:title="@string/voicemail_change_pin_preference_title"/>
diff --git a/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java b/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java
index ae526d1..6694a5d 100644
--- a/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java
+++ b/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java
@@ -57,6 +57,18 @@
         .apply();
   }
 
+  public static void setVoicemailDonationEnabled(
+      Context context, PhoneAccountHandle phoneAccount, boolean isEnabled) {
+    Assert.checkArgument(
+        VoicemailComponent.get(context)
+            .getVoicemailClient()
+            .isVoicemailTranscriptionAvailable(context));
+    new VisualVoicemailPreferences(context, phoneAccount)
+        .edit()
+        .putBoolean(context.getString(R.string.voicemail_visual_voicemail_donation_key), isEnabled)
+        .apply();
+  }
+
   public static boolean isEnabled(Context context, PhoneAccountHandle phoneAccount) {
     if (phoneAccount == null) {
       return false;
@@ -79,6 +91,15 @@
         context.getString(R.string.voicemail_visual_voicemail_archive_key), false);
   }
 
+  public static boolean isVoicemailDonationEnabled(
+      Context context, PhoneAccountHandle phoneAccount) {
+    Assert.isNotNull(phoneAccount);
+
+    VisualVoicemailPreferences prefs = new VisualVoicemailPreferences(context, phoneAccount);
+    return prefs.getBoolean(
+        context.getString(R.string.voicemail_visual_voicemail_donation_key), false);
+  }
+
   /**
    * Whether the client enabled status is explicitly set by user or by default(Whether carrier VVM
    * app is installed). This is used to determine whether to disable the client when the carrier VVM
diff --git a/java/com/android/voicemail/impl/settings/VoicemailSettingsFragment.java b/java/com/android/voicemail/impl/settings/VoicemailSettingsFragment.java
index e2ea725..4652238 100644
--- a/java/com/android/voicemail/impl/settings/VoicemailSettingsFragment.java
+++ b/java/com/android/voicemail/impl/settings/VoicemailSettingsFragment.java
@@ -53,6 +53,7 @@
   private Preference voicemailNotificationPreference;
   private SwitchPreference voicemailVisualVoicemail;
   private SwitchPreference autoArchiveSwitchPreference;
+  private SwitchPreference donateVoicemailSwitchPreference;
   private Preference voicemailChangePinPreference;
   private PreferenceScreen advancedSettings;
 
@@ -102,12 +103,22 @@
         (SwitchPreference)
             findPreference(getString(R.string.voicemail_visual_voicemail_archive_key));
 
+    donateVoicemailSwitchPreference =
+        (SwitchPreference)
+            findPreference(getString(R.string.voicemail_visual_voicemail_donation_key));
+
     if (!VoicemailComponent.get(getContext())
         .getVoicemailClient()
         .isVoicemailArchiveAvailable(getContext())) {
       getPreferenceScreen().removePreference(autoArchiveSwitchPreference);
     }
 
+    if (!VoicemailComponent.get(getContext())
+        .getVoicemailClient()
+        .isVoicemailDonationEnabled(getContext(), phoneAccountHandle)) {
+      getPreferenceScreen().removePreference(donateVoicemailSwitchPreference);
+    }
+
     voicemailChangePinPreference = findPreference(getString(R.string.voicemail_change_pin_key));
 
     if (omtpVvmCarrierConfigHelper.isValid()) {
@@ -141,9 +152,15 @@
       autoArchiveSwitchPreference.setOnPreferenceChangeListener(this);
       autoArchiveSwitchPreference.setChecked(
           VisualVoicemailSettingsUtil.isArchiveEnabled(getContext(), phoneAccountHandle));
+
+      donateVoicemailSwitchPreference.setOnPreferenceChangeListener(this);
+      donateVoicemailSwitchPreference.setChecked(
+          VisualVoicemailSettingsUtil.isVoicemailDonationEnabled(getContext(), phoneAccountHandle));
+      updateDonateVoicemail();
     } else {
       prefSet.removePreference(voicemailVisualVoicemail);
       prefSet.removePreference(autoArchiveSwitchPreference);
+      prefSet.removePreference(donateVoicemailSwitchPreference);
       prefSet.removePreference(voicemailChangePinPreference);
     }
 
@@ -192,10 +209,15 @@
       }
 
       updateChangePin();
+      updateDonateVoicemail();
     } else if (preference.getKey().equals(autoArchiveSwitchPreference.getKey())) {
       logArchiveToggle((boolean) objValue);
       VisualVoicemailSettingsUtil.setArchiveEnabled(
           getContext(), phoneAccountHandle, (boolean) objValue);
+    } else if (preference.getKey().equals(donateVoicemailSwitchPreference.getKey())) {
+      logArchiveToggle((boolean) objValue);
+      VisualVoicemailSettingsUtil.setVoicemailDonationEnabled(
+          getContext(), phoneAccountHandle, (boolean) objValue);
     }
 
     // Always let the preference setting proceed.
@@ -217,6 +239,21 @@
     }
   }
 
+  private void updateDonateVoicemail() {
+    if (!VisualVoicemailSettingsUtil.isEnabled(getContext(), phoneAccountHandle)) {
+      donateVoicemailSwitchPreference.setSummary(
+          R.string.voicemail_donate_preference_summary_disable);
+      donateVoicemailSwitchPreference.setEnabled(false);
+    } else if (!VvmAccountManager.isAccountActivated(getContext(), phoneAccountHandle)) {
+      donateVoicemailSwitchPreference.setSummary(
+          R.string.voicemail_donate_preference_summary_not_activated);
+      donateVoicemailSwitchPreference.setEnabled(false);
+    } else {
+      donateVoicemailSwitchPreference.setSummary(R.string.voicemail_donate_preference_summary_info);
+      donateVoicemailSwitchPreference.setEnabled(true);
+    }
+  }
+
   private void logArchiveToggle(boolean userTurnedOn) {
     if (userTurnedOn) {
       Logger.get(getContext())
@@ -231,6 +268,7 @@
   public void onActivationStateChanged(PhoneAccountHandle phoneAccountHandle, boolean isActivated) {
     if (this.phoneAccountHandle.equals(phoneAccountHandle)) {
       updateChangePin();
+      updateDonateVoicemail();
     }
   }
 
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java
index f3c6e64..f7f6439 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java
@@ -72,7 +72,7 @@
     for (Uri uri : untranscribed) {
       ThreadUtil.postOnUiThread(
           () -> {
-            TranscriptionService.scheduleNewVoicemailTranscriptionJob(this, uri, false);
+            TranscriptionService.scheduleNewVoicemailTranscriptionJob(this, uri, null, false);
           });
     }
   }
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java b/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java
index 4bb9a26..5da4503 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java
@@ -75,6 +75,11 @@
         .getLong("voicemail_transcription_get_transcript_poll_interval_millis", 1000L);
   }
 
+  public boolean isVoicemailDonationEnabled() {
+    return ConfigProviderBindings.get(context)
+        .getBoolean("voicemail_transcription_donation_enabled", false);
+  }
+
   @Override
   public String toString() {
     return String.format(
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
index b733928..79e1a01 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
@@ -25,8 +25,10 @@
 import android.content.Intent;
 import android.net.Uri;
 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;
 import com.android.dialer.common.LogUtil;
@@ -44,6 +46,7 @@
  */
 public class TranscriptionService extends JobService {
   @VisibleForTesting static final String EXTRA_VOICEMAIL_URI = "extra_voicemail_uri";
+  @VisibleForTesting static final String EXTRA_ACCOUNT_HANDLE = "extra_account_handle";
 
   private ExecutorService executorService;
   private JobParameters jobParameters;
@@ -58,10 +61,14 @@
   }
 
   // Schedule a task to transcribe the indicated voicemail, return true if transcription task was
-  // scheduled.
+  // scheduled. If the PhoneAccountHandle is null then the voicemail will not be considered for
+  // donation.
   @MainThread
   public static boolean scheduleNewVoicemailTranscriptionJob(
-      Context context, Uri voicemailUri, boolean highPriority) {
+      Context context,
+      Uri voicemailUri,
+      @Nullable PhoneAccountHandle account,
+      boolean highPriority) {
     Assert.isMainThread();
     if (BuildCompat.isAtLeastO()) {
       LogUtil.i(
@@ -80,7 +87,7 @@
         builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
       }
       JobScheduler scheduler = context.getSystemService(JobScheduler.class);
-      JobWorkItem workItem = makeWorkItem(voicemailUri);
+      JobWorkItem workItem = makeWorkItem(voicemailUri, account);
       return scheduler.enqueue(builder.build(), workItem) == JobScheduler.RESULT_SUCCESS;
     } else {
       LogUtil.i("TranscriptionService.scheduleNewVoicemailTranscriptionJob", "not supported");
@@ -193,6 +200,10 @@
     return workItem.getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI);
   }
 
+  static PhoneAccountHandle getPhoneAccountHandle(JobWorkItem workItem) {
+    return workItem.getIntent().getParcelableExtra(EXTRA_ACCOUNT_HANDLE);
+  }
+
   private ExecutorService getExecutorService() {
     if (executorService == null) {
       // The common use case is transcribing a single voicemail so just use a single thread executor
@@ -219,9 +230,12 @@
     }
   }
 
-  private static JobWorkItem makeWorkItem(Uri voicemailUri) {
+  private static JobWorkItem makeWorkItem(Uri voicemailUri, PhoneAccountHandle account) {
     Intent intent = new Intent();
     intent.putExtra(EXTRA_VOICEMAIL_URI, voicemailUri);
+    if (account != null) {
+      intent.putExtra(EXTRA_ACCOUNT_HANDLE, account);
+    }
     return new JobWorkItem(intent);
   }
 
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java
index a93c651..f3b1d58 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java
@@ -20,6 +20,8 @@
 import android.content.Context;
 import android.net.Uri;
 import android.support.annotation.MainThread;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 import android.util.Pair;
 import com.android.dialer.common.Assert;
@@ -62,6 +64,7 @@
   private final JobWorkItem workItem;
   private final TranscriptionClientFactory clientFactory;
   private final Uri voicemailUri;
+  protected final PhoneAccountHandle phoneAccountHandle;
   private final TranscriptionDbHelper databaseHelper;
   protected final TranscriptionConfigProvider configProvider;
   protected ByteString audioData;
@@ -86,6 +89,7 @@
     this.workItem = workItem;
     this.clientFactory = clientFactory;
     this.voicemailUri = TranscriptionService.getVoicemailUri(workItem);
+    this.phoneAccountHandle = TranscriptionService.getPhoneAccountHandle(workItem);
     this.configProvider = configProvider;
     databaseHelper = new TranscriptionDbHelper(context, voicemailUri);
   }
@@ -240,14 +244,24 @@
       return false;
     }
 
-    if (audioData.startsWith(ByteString.copyFromUtf8(AMR_PREFIX))) {
-      encoding = AudioFormat.AMR_NB_8KHZ;
-    } else {
+    encoding = getAudioFormat(audioData);
+    if (encoding == AudioFormat.AUDIO_FORMAT_UNSPECIFIED) {
       VvmLog.i(TAG, "Transcriber.readAndValidateAudioFile, unknown encoding");
-      encoding = AudioFormat.AUDIO_FORMAT_UNSPECIFIED;
       return false;
     }
 
     return true;
   }
+
+  private static AudioFormat getAudioFormat(ByteString audioData) {
+    return audioData != null && audioData.startsWith(ByteString.copyFromUtf8(AMR_PREFIX))
+        ? AudioFormat.AMR_NB_8KHZ
+        : AudioFormat.AUDIO_FORMAT_UNSPECIFIED;
+  }
+
+  @VisibleForTesting
+  void setAudioDataForTesting(ByteString audioData) {
+    this.audioData = audioData;
+    encoding = getAudioFormat(audioData);
+  }
 }
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java
index e757280..f946607 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java
@@ -21,11 +21,13 @@
 import com.android.dialer.common.Assert;
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
+import com.android.voicemail.VoicemailComponent;
 import com.android.voicemail.impl.VvmLog;
 import com.android.voicemail.impl.transcribe.TranscriptionService.JobCallback;
 import com.android.voicemail.impl.transcribe.grpc.GetTranscriptResponseAsync;
 import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory;
 import com.android.voicemail.impl.transcribe.grpc.TranscriptionResponseAsync;
+import com.google.internal.communications.voicemailtranscription.v1.DonationPreference;
 import com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest;
 import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest;
 import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus;
@@ -119,13 +121,22 @@
     return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY);
   }
 
-  private TranscribeVoicemailAsyncRequest getUploadRequest() {
+  TranscribeVoicemailAsyncRequest getUploadRequest() {
     return TranscribeVoicemailAsyncRequest.newBuilder()
         .setVoicemailData(audioData)
         .setAudioFormat(encoding)
+        .setDonationPreference(
+            isDonationEnabled() ? DonationPreference.DONATE : DonationPreference.DO_NOT_DONATE)
         .build();
   }
 
+  private boolean isDonationEnabled() {
+    return phoneAccountHandle != null
+        && VoicemailComponent.get(context)
+            .getVoicemailClient()
+            .isVoicemailDonationEnabled(context, phoneAccountHandle);
+  }
+
   private GetTranscriptRequest getGetTranscriptRequest(TranscriptionResponseAsync uploadResponse) {
     Assert.checkArgument(uploadResponse.getTranscriptionId() != null);
     return GetTranscriptRequest.newBuilder()
diff --git a/java/com/android/voicemail/stub/StubVoicemailClient.java b/java/com/android/voicemail/stub/StubVoicemailClient.java
index c2c7a6d..4b8ed9a 100644
--- a/java/com/android/voicemail/stub/StubVoicemailClient.java
+++ b/java/com/android/voicemail/stub/StubVoicemailClient.java
@@ -76,6 +76,16 @@
       Context context, PhoneAccountHandle phoneAccountHandle, boolean value) {}
 
   @Override
+  public boolean isVoicemailTranscriptionAvailable(Context context) {
+    return false;
+  }
+
+  @Override
+  public boolean isVoicemailDonationEnabled(Context context, PhoneAccountHandle account) {
+    return false;
+  }
+
+  @Override
   public Intent getSetPinIntent(Context context, PhoneAccountHandle phoneAccountHandle) {
     Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL);
     intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);