• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 package com.android.voicemail.impl.transcribe;
17 
18 import android.app.job.JobWorkItem;
19 import android.content.Context;
20 import android.support.annotation.VisibleForTesting;
21 import android.util.Pair;
22 import com.android.dialer.logging.DialerImpression;
23 import com.android.voicemail.VoicemailComponent;
24 import com.android.voicemail.impl.VvmLog;
25 import com.android.voicemail.impl.transcribe.TranscriptionService.JobCallback;
26 import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory;
27 import com.android.voicemail.impl.transcribe.grpc.TranscriptionResponseAsync;
28 import com.google.internal.communications.voicemailtranscription.v1.DonationPreference;
29 import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest;
30 import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus;
31 
32 /**
33  * Background task to get a voicemail transcription using the asynchronous API. The async API works
34  * as follows:
35  *
36  * <ol>
37  *   <li>client uploads voicemail data to the server
38  *   <li>server responds with a transcription-id and an estimated transcription wait time
39  *   <li>client waits appropriate amount of time then begins polling for the result
40  * </ol>
41  *
42  * This implementation blocks until the response or an error is received, even though it is using
43  * the asynchronous server API.
44  */
45 public class TranscriptionTaskAsync extends TranscriptionTask {
46   private static final String TAG = "TranscriptionTaskAsync";
47 
TranscriptionTaskAsync( Context context, JobCallback callback, JobWorkItem workItem, TranscriptionClientFactory clientFactory, TranscriptionConfigProvider configProvider)48   public TranscriptionTaskAsync(
49       Context context,
50       JobCallback callback,
51       JobWorkItem workItem,
52       TranscriptionClientFactory clientFactory,
53       TranscriptionConfigProvider configProvider) {
54     super(context, callback, workItem, clientFactory, configProvider);
55   }
56 
57   @Override
getTranscription()58   protected Pair<String, TranscriptionStatus> getTranscription() {
59     VvmLog.i(TAG, "getTranscription");
60 
61     if (GetTranscriptReceiver.hasPendingAlarm(context)) {
62       // Don't start a transcription while another is still active
63       VvmLog.i(
64           TAG,
65           "getTranscription, pending transcription, postponing transcription of: " + voicemailUri);
66       return new Pair<>(null, null);
67     }
68 
69     TranscribeVoicemailAsyncRequest uploadRequest = getUploadRequest();
70     VvmLog.i(
71         TAG,
72         "getTranscription, uploading voicemail: "
73             + voicemailUri
74             + ", id: "
75             + uploadRequest.getTranscriptionId());
76     TranscriptionResponseAsync uploadResponse =
77         (TranscriptionResponseAsync)
78             sendRequest((client) -> client.sendUploadRequest(uploadRequest));
79 
80     if (cancelled) {
81       VvmLog.i(TAG, "getTranscription, cancelled.");
82       return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY);
83     } else if (uploadResponse == null) {
84       VvmLog.i(TAG, "getTranscription, failed to upload voicemail.");
85       return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY);
86     } else if (uploadResponse.getTranscriptionId() == null) {
87       VvmLog.i(TAG, "getTranscription, upload error: " + uploadResponse.status);
88       return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY);
89     } else {
90       VvmLog.i(TAG, "getTranscription, begin polling for: " + uploadResponse.getTranscriptionId());
91       GetTranscriptReceiver.beginPolling(
92           context,
93           voicemailUri,
94           uploadResponse.getTranscriptionId(),
95           uploadResponse.getEstimatedWaitMillis(),
96           configProvider,
97           phoneAccountHandle);
98       // This indicates that the result is not available yet
99       return new Pair<>(null, null);
100     }
101   }
102 
103   @Override
getRequestSentImpression()104   protected DialerImpression.Type getRequestSentImpression() {
105     return DialerImpression.Type.VVM_TRANSCRIPTION_REQUEST_SENT_ASYNC;
106   }
107 
108   @VisibleForTesting
getUploadRequest()109   TranscribeVoicemailAsyncRequest getUploadRequest() {
110     TranscribeVoicemailAsyncRequest.Builder builder =
111         TranscribeVoicemailAsyncRequest.newBuilder()
112             .setVoicemailData(audioData)
113             .setAudioFormat(encoding)
114             .setDonationPreference(
115                 isDonationEnabled() ? DonationPreference.DONATE : DonationPreference.DO_NOT_DONATE);
116     // Generate the transcript id locally if configured to do so, or if voicemail donation is
117     // available (because rating donating voicemails requires locally generated voicemail ids).
118     if (configProvider.useClientGeneratedVoicemailIds()
119         || configProvider.isVoicemailDonationAvailable()) {
120       // The server currently can't handle repeated transcription id's so if we add the Uri to the
121       // fingerprint (which contains the voicemail id) which is different each time a voicemail is
122       // downloaded.  If this becomes a problem then it should be possible to change the server
123       // behavior to allow id's to be re-used, a bug
124       String salt = voicemailUri.toString();
125       builder.setTranscriptionId(TranscriptionUtils.getFingerprintFor(audioData, salt));
126     }
127     return builder.build();
128   }
129 
isDonationEnabled()130   private boolean isDonationEnabled() {
131     return phoneAccountHandle != null
132         && VoicemailComponent.get(context)
133             .getVoicemailClient()
134             .isVoicemailDonationEnabled(context, phoneAccountHandle);
135   }
136 }
137