summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--java/com/android/dialer/logging/dialer_impression.proto12
-rw-r--r--java/com/android/incallui/autoresizetext/AutoResizeTextView.java11
-rw-r--r--java/com/android/voicemail/impl/VoicemailTranscriptionServiceGrpc.java138
-rw-r--r--java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java32
-rw-r--r--java/com/android/voicemail/impl/transcribe/TranscriptionService.java13
-rw-r--r--java/com/android/voicemail/impl/transcribe/TranscriptionTask.java135
-rw-r--r--java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java123
-rw-r--r--java/com/android/voicemail/impl/transcribe/TranscriptionTaskSync.java69
-rw-r--r--java/com/android/voicemail/impl/transcribe/grpc/GetTranscriptResponseAsync.java102
-rw-r--r--java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java44
-rw-r--r--java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClientFactory.java4
-rw-r--r--java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponse.java53
-rw-r--r--java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseAsync.java53
-rw-r--r--java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseSync.java43
-rw-r--r--java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto88
15 files changed, 832 insertions, 88 deletions
diff --git a/java/com/android/dialer/logging/dialer_impression.proto b/java/com/android/dialer/logging/dialer_impression.proto
index de38ff359..b02c82b57 100644
--- a/java/com/android/dialer/logging/dialer_impression.proto
+++ b/java/com/android/dialer/logging/dialer_impression.proto
@@ -504,5 +504,17 @@ message DialerImpression {
// notifications is very high and the system may suppress future
// notifications.
HIGH_GLOBAL_NOTIFICATION_COUNT_REACHED = 1249;
+
+ // More impressions for interactions with the voicemail transcription server
+ VVM_TRANSCRIPTION_REQUEST_SENT_ASYNC = 1250;
+ VVM_TRANSCRIPTION_VOICEMAIL_RECEIVED = 1251;
+ VVM_TRANSCRIPTION_VOICEMAIL_FORMAT_NOT_SUPPORTED = 1252;
+ VVM_TRANSCRIPTION_VOICEMAIL_INVALID_DATA = 1253;
+ VVM_TRANSCRIPTION_VOICEMAIL_UPLOAD_FAILED = 1254;
+ VVM_TRANSCRIPTION_RESPONSE_LANGUAGE_NOT_SUPPORTED = 1255;
+ VVM_TRANSCRIPTION_RESPONSE_NO_SPEECH_DETECTED = 1256;
+ VVM_TRANSCRIPTION_RESPONSE_EXPIRED = 1257;
+ VVM_TRANSCRIPTION_RESPONSE_TOO_MANY_ERRORS = 1258;
+ VVM_TRANSCRIPTION_POLLING_TIMEOUT = 1259;
}
}
diff --git a/java/com/android/incallui/autoresizetext/AutoResizeTextView.java b/java/com/android/incallui/autoresizetext/AutoResizeTextView.java
index 8fd0b4868..5a22b93dc 100644
--- a/java/com/android/incallui/autoresizetext/AutoResizeTextView.java
+++ b/java/com/android/incallui/autoresizetext/AutoResizeTextView.java
@@ -226,10 +226,13 @@ public class AutoResizeTextView extends TextView {
} else {
// If multiline, lay the text out, then check the number of lines, the layout's height,
// and each line's width.
- StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, maxWidth)
- .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
- .setUseLineSpacingFromFallbacks(true)
- .build();
+ StaticLayout layout = new StaticLayout(text,
+ textPaint,
+ maxWidth,
+ Alignment.ALIGN_NORMAL,
+ getLineSpacingMultiplier(),
+ getLineSpacingExtra(),
+ true);
// Return false if we need more than maxLines. The text is obviously too big in this case.
if (maxLines != NO_LINE_LIMIT && layout.getLineCount() > maxLines) {
diff --git a/java/com/android/voicemail/impl/VoicemailTranscriptionServiceGrpc.java b/java/com/android/voicemail/impl/VoicemailTranscriptionServiceGrpc.java
index 448c69356..8fcbf3b97 100644
--- a/java/com/android/voicemail/impl/VoicemailTranscriptionServiceGrpc.java
+++ b/java/com/android/voicemail/impl/VoicemailTranscriptionServiceGrpc.java
@@ -55,6 +55,24 @@ public class VoicemailTranscriptionServiceGrpc {
"google.internal.communications.voicemailtranscription.v1.VoicemailTranscriptionService", "TranscribeVoicemail"),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailRequest.getDefaultInstance()),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailResponse.getDefaultInstance()));
+ @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/1901")
+ public static final io.grpc.MethodDescriptor<com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest,
+ com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse> METHOD_TRANSCRIBE_VOICEMAIL_ASYNC =
+ io.grpc.MethodDescriptor.create(
+ io.grpc.MethodDescriptor.MethodType.UNARY,
+ generateFullMethodName(
+ "google.internal.communications.voicemailtranscription.v1.VoicemailTranscriptionService", "TranscribeVoicemailAsync"),
+ io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest.getDefaultInstance()),
+ io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse.getDefaultInstance()));
+ @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/1901")
+ public static final io.grpc.MethodDescriptor<com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest,
+ com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse> METHOD_GET_TRANSCRIPT =
+ io.grpc.MethodDescriptor.create(
+ io.grpc.MethodDescriptor.MethodType.UNARY,
+ generateFullMethodName(
+ "google.internal.communications.voicemailtranscription.v1.VoicemailTranscriptionService", "GetTranscript"),
+ io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest.getDefaultInstance()),
+ io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse.getDefaultInstance()));
/**
* Creates a new async stub that supports all call types for the service
@@ -96,6 +114,28 @@ public class VoicemailTranscriptionServiceGrpc {
asyncUnimplementedUnaryCall(METHOD_TRANSCRIBE_VOICEMAIL, responseObserver);
}
+ /**
+ * <pre>
+ * Schedules a transcription of the given voicemail. The transcript can be
+ * retrieved using the returned ID.
+ * </pre>
+ */
+ public void transcribeVoicemailAsync(com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest request,
+ io.grpc.stub.StreamObserver<com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse> responseObserver) {
+ asyncUnimplementedUnaryCall(METHOD_TRANSCRIBE_VOICEMAIL_ASYNC, responseObserver);
+ }
+
+ /**
+ * <pre>
+ * Returns the transcript corresponding to the given ID, which was returned
+ * by TranscribeVoicemailAsync.
+ * </pre>
+ */
+ public void getTranscript(com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest request,
+ io.grpc.stub.StreamObserver<com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse> responseObserver) {
+ asyncUnimplementedUnaryCall(METHOD_GET_TRANSCRIPT, responseObserver);
+ }
+
@java.lang.Override public io.grpc.ServerServiceDefinition bindService() {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
.addMethod(
@@ -105,6 +145,20 @@ public class VoicemailTranscriptionServiceGrpc {
com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailRequest,
com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailResponse>(
this, METHODID_TRANSCRIBE_VOICEMAIL)))
+ .addMethod(
+ METHOD_TRANSCRIBE_VOICEMAIL_ASYNC,
+ asyncUnaryCall(
+ new MethodHandlers<
+ com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest,
+ com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse>(
+ this, METHODID_TRANSCRIBE_VOICEMAIL_ASYNC)))
+ .addMethod(
+ METHOD_GET_TRANSCRIPT,
+ asyncUnaryCall(
+ new MethodHandlers<
+ com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest,
+ com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse>(
+ this, METHODID_GET_TRANSCRIPT)))
.build();
}
}
@@ -140,6 +194,30 @@ public class VoicemailTranscriptionServiceGrpc {
asyncUnaryCall(
getChannel().newCall(METHOD_TRANSCRIBE_VOICEMAIL, getCallOptions()), request, responseObserver);
}
+
+ /**
+ * <pre>
+ * Schedules a transcription of the given voicemail. The transcript can be
+ * retrieved using the returned ID.
+ * </pre>
+ */
+ public void transcribeVoicemailAsync(com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest request,
+ io.grpc.stub.StreamObserver<com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse> responseObserver) {
+ asyncUnaryCall(
+ getChannel().newCall(METHOD_TRANSCRIBE_VOICEMAIL_ASYNC, getCallOptions()), request, responseObserver);
+ }
+
+ /**
+ * <pre>
+ * Returns the transcript corresponding to the given ID, which was returned
+ * by TranscribeVoicemailAsync.
+ * </pre>
+ */
+ public void getTranscript(com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest request,
+ io.grpc.stub.StreamObserver<com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse> responseObserver) {
+ asyncUnaryCall(
+ getChannel().newCall(METHOD_GET_TRANSCRIPT, getCallOptions()), request, responseObserver);
+ }
}
/**
@@ -172,6 +250,28 @@ public class VoicemailTranscriptionServiceGrpc {
return blockingUnaryCall(
getChannel(), METHOD_TRANSCRIBE_VOICEMAIL, getCallOptions(), request);
}
+
+ /**
+ * <pre>
+ * Schedules a transcription of the given voicemail. The transcript can be
+ * retrieved using the returned ID.
+ * </pre>
+ */
+ public com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse transcribeVoicemailAsync(com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest request) {
+ return blockingUnaryCall(
+ getChannel(), METHOD_TRANSCRIBE_VOICEMAIL_ASYNC, getCallOptions(), request);
+ }
+
+ /**
+ * <pre>
+ * Returns the transcript corresponding to the given ID, which was returned
+ * by TranscribeVoicemailAsync.
+ * </pre>
+ */
+ public com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse getTranscript(com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest request) {
+ return blockingUnaryCall(
+ getChannel(), METHOD_GET_TRANSCRIPT, getCallOptions(), request);
+ }
}
/**
@@ -205,9 +305,35 @@ public class VoicemailTranscriptionServiceGrpc {
return futureUnaryCall(
getChannel().newCall(METHOD_TRANSCRIBE_VOICEMAIL, getCallOptions()), request);
}
+
+ /**
+ * <pre>
+ * Schedules a transcription of the given voicemail. The transcript can be
+ * retrieved using the returned ID.
+ * </pre>
+ */
+ public com.google.common.util.concurrent.ListenableFuture<com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse> transcribeVoicemailAsync(
+ com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest request) {
+ return futureUnaryCall(
+ getChannel().newCall(METHOD_TRANSCRIBE_VOICEMAIL_ASYNC, getCallOptions()), request);
+ }
+
+ /**
+ * <pre>
+ * Returns the transcript corresponding to the given ID, which was returned
+ * by TranscribeVoicemailAsync.
+ * </pre>
+ */
+ public com.google.common.util.concurrent.ListenableFuture<com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse> getTranscript(
+ com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest request) {
+ return futureUnaryCall(
+ getChannel().newCall(METHOD_GET_TRANSCRIPT, getCallOptions()), request);
+ }
}
private static final int METHODID_TRANSCRIBE_VOICEMAIL = 0;
+ private static final int METHODID_TRANSCRIBE_VOICEMAIL_ASYNC = 1;
+ private static final int METHODID_GET_TRANSCRIPT = 2;
private static class MethodHandlers<Req, Resp> implements
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
@@ -230,6 +356,14 @@ public class VoicemailTranscriptionServiceGrpc {
serviceImpl.transcribeVoicemail((com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailRequest) request,
(io.grpc.stub.StreamObserver<com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailResponse>) responseObserver);
break;
+ case METHODID_TRANSCRIBE_VOICEMAIL_ASYNC:
+ serviceImpl.transcribeVoicemailAsync((com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest) request,
+ (io.grpc.stub.StreamObserver<com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse>) responseObserver);
+ break;
+ case METHODID_GET_TRANSCRIPT:
+ serviceImpl.getTranscript((com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest) request,
+ (io.grpc.stub.StreamObserver<com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse>) responseObserver);
+ break;
default:
throw new AssertionError();
}
@@ -248,7 +382,9 @@ public class VoicemailTranscriptionServiceGrpc {
public static io.grpc.ServiceDescriptor getServiceDescriptor() {
return new io.grpc.ServiceDescriptor(SERVICE_NAME,
- METHOD_TRANSCRIBE_VOICEMAIL);
+ METHOD_TRANSCRIBE_VOICEMAIL,
+ METHOD_TRANSCRIBE_VOICEMAIL_ASYNC,
+ METHOD_GET_TRANSCRIPT);
}
}
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java b/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java
index 0c83615b4..83f04dab8 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java
@@ -53,10 +53,38 @@ public class TranscriptionConfigProvider {
.getBoolean("voicemail_transcription_server_use_plaintext", false);
}
+ public boolean shouldUseSyncApi() {
+ return ConfigProviderBindings.get(context)
+ .getBoolean("voicemail_transcription_server_use_sync_api", false);
+ }
+
+ public long getMaxTranscriptionRetries() {
+ return ConfigProviderBindings.get(context)
+ .getLong("voicemail_transcription_max_transcription_retries", 2L);
+ }
+
+ public long getMaxGetTranscriptPolls() {
+ return ConfigProviderBindings.get(context)
+ .getLong("voicemail_transcription_max_get_transcript_polls", 20L);
+ }
+
+ public long getGetTranscriptPollIntervalMillis() {
+ return ConfigProviderBindings.get(context)
+ .getLong("voicemail_transcription_get_transcript_poll_interval_millis", 1000L);
+ }
+
@Override
public String toString() {
return String.format(
- "{ address: %s, api key: %s, auth token: %s, plaintext: %b }",
- getServerAddress(), getApiKey(), getAuthToken(), shouldUsePlaintext());
+ "{ address: %s, api key: %s, auth token: %s, plaintext: %b, sync: %b, retries: %d, polls:"
+ + " %d, poll ms: %d }",
+ getServerAddress(),
+ getApiKey(),
+ getAuthToken(),
+ shouldUsePlaintext(),
+ shouldUseSyncApi(),
+ getMaxTranscriptionRetries(),
+ getMaxGetTranscriptPolls(),
+ getGetTranscriptPollIntervalMillis());
}
}
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
index 9a781e6c2..6933a3a66 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java
@@ -31,6 +31,8 @@ import android.text.TextUtils;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.constants.ScheduledJobIds;
+import com.android.dialer.logging.DialerImpression;
+import com.android.dialer.logging.Logger;
import com.android.dialer.strictmode.DialerStrictMode;
import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory;
import java.util.concurrent.ExecutorService;
@@ -60,6 +62,8 @@ public class TranscriptionService extends JobService {
Assert.isMainThread();
if (BuildCompat.isAtLeastO()) {
LogUtil.i("TranscriptionService.transcribeVoicemail", "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)
@@ -148,8 +152,13 @@ public class TranscriptionService extends JobService {
Assert.isMainThread();
JobWorkItem workItem = jobParameters.dequeueWork();
if (workItem != null) {
- getExecutorService()
- .execute(new TranscriptionTask(this, new Callback(), workItem, getClientFactory()));
+ TranscriptionTask task =
+ configProvider.shouldUseSyncApi()
+ ? new TranscriptionTaskSync(
+ this, new Callback(), workItem, getClientFactory(), configProvider)
+ : new TranscriptionTaskAsync(
+ this, new Callback(), workItem, getClientFactory(), configProvider);
+ getExecutorService().execute(task);
return true;
} else {
return false;
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java
index a14b6df91..61a5ee8de 100644
--- a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java
@@ -20,6 +20,7 @@ import android.app.job.JobWorkItem;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
+import android.util.Pair;
import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
@@ -27,10 +28,10 @@ import com.android.voicemail.impl.VvmLog;
import com.android.voicemail.impl.transcribe.TranscriptionService.JobCallback;
import com.android.voicemail.impl.transcribe.grpc.TranscriptionClient;
import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory;
+import com.android.voicemail.impl.transcribe.grpc.TranscriptionResponse;
import com.google.internal.communications.voicemailtranscription.v1.AudioFormat;
-import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailRequest;
+import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus;
import com.google.protobuf.ByteString;
-import io.grpc.Status;
import java.io.IOException;
import java.io.InputStream;
@@ -41,7 +42,7 @@ import java.io.InputStream;
* This task performs the following steps:
* 1. Update the transcription-state in the database to 'in-progress'
* 2. Create grpc client and transcription request
- * 3. Make synchronous grpc transcription request to backend server
+ * 3. Make synchronous or asynchronous grpc transcription request to backend server
* 3a. On response
* Update the database with transcription (if successful) and new transcription-state
* 3b. On network error
@@ -50,7 +51,7 @@ import java.io.InputStream;
* 4. Notify the callback that the work item is complete
* </pre>
*/
-public class TranscriptionTask implements Runnable {
+public abstract class TranscriptionTask implements Runnable {
private static final String TAG = "TranscriptionTask";
private final Context context;
@@ -59,22 +60,29 @@ public class TranscriptionTask implements Runnable {
private final TranscriptionClientFactory clientFactory;
private final Uri voicemailUri;
private final TranscriptionDbHelper databaseHelper;
- private ByteString audioData;
- private AudioFormat encoding;
+ protected final TranscriptionConfigProvider configProvider;
+ protected ByteString audioData;
+ protected AudioFormat encoding;
- private static final int MAX_RETRIES = 2;
static final String AMR_PREFIX = "#!AMR\n";
- public TranscriptionTask(
+ /** Functional interface for sending requests to the transcription server */
+ public interface Request {
+ TranscriptionResponse getResponse(TranscriptionClient client);
+ }
+
+ TranscriptionTask(
Context context,
JobCallback callback,
JobWorkItem workItem,
- TranscriptionClientFactory clientFactory) {
+ TranscriptionClientFactory clientFactory,
+ TranscriptionConfigProvider configProvider) {
this.context = context;
this.callback = callback;
this.workItem = workItem;
this.clientFactory = clientFactory;
this.voicemailUri = getVoicemailUri(workItem);
+ this.configProvider = configProvider;
databaseHelper = new TranscriptionDbHelper(context, voicemailUri);
}
@@ -85,6 +93,13 @@ public class TranscriptionTask implements Runnable {
updateTranscriptionState(VoicemailCompat.TRANSCRIPTION_IN_PROGRESS);
transcribeVoicemail();
} else {
+ if (AudioFormat.AUDIO_FORMAT_UNSPECIFIED.equals(encoding)) {
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_VOICEMAIL_FORMAT_NOT_SUPPORTED);
+ } else {
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_VOICEMAIL_INVALID_DATA);
+ }
updateTranscriptionState(VoicemailCompat.TRANSCRIPTION_FAILED);
}
ThreadUtil.postOnUiThread(
@@ -93,62 +108,77 @@ public class TranscriptionTask implements Runnable {
});
}
+ protected abstract Pair<String, TranscriptionStatus> getTranscription();
+
+ protected abstract DialerImpression.Type getRequestSentImpression();
+
private void transcribeVoicemail() {
VvmLog.i(TAG, "transcribeVoicemail");
- TranscribeVoicemailRequest request = makeRequest();
- TranscriptionClient client = clientFactory.getClient();
- String transcript = null;
- for (int i = 0; transcript == null && i < MAX_RETRIES; i++) {
- VvmLog.i(TAG, "transcribeVoicemail, try: " + (i + 1));
- if (i == 0) {
- Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_REQUEST_SENT);
- } else {
- Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_REQUEST_RETRY);
- }
- TranscriptionClient.TranscriptionResponseWrapper responseWrapper =
- client.transcribeVoicemail(request);
- if (responseWrapper.status != null) {
- VvmLog.i(TAG, "transcribeVoicemail, status: " + responseWrapper.status.getCode());
- if (shouldRetryRequest(responseWrapper.status)) {
+ Pair<String, TranscriptionStatus> pair = getTranscription();
+ String transcript = pair.first;
+ TranscriptionStatus status = pair.second;
+ if (!TextUtils.isEmpty(transcript)) {
+ updateTranscriptionAndState(transcript, VoicemailCompat.TRANSCRIPTION_AVAILABLE);
+ VvmLog.i(TAG, "transcribeVoicemail, got response");
+ Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_SUCCESS);
+ } else {
+ VvmLog.i(TAG, "transcribeVoicemail, transcription unsuccessful, " + status);
+ switch (status) {
+ case FAILED_LANGUAGE_NOT_SUPPORTED:
Logger.get(context)
- .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_RECOVERABLE_ERROR);
- backoff(i);
- } else {
+ .logImpression(
+ DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_LANGUAGE_NOT_SUPPORTED);
+ break;
+ case FAILED_NO_SPEECH_DETECTED:
Logger.get(context)
- .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_FATAL_ERROR);
+ .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_NO_SPEECH_DETECTED);
break;
- }
- } else if (responseWrapper.response != null) {
- if (!TextUtils.isEmpty(responseWrapper.response.getTranscript())) {
- VvmLog.i(TAG, "transcribeVoicemail, got response");
- transcript = responseWrapper.response.getTranscript();
+ case EXPIRED:
Logger.get(context)
- .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_SUCCESS);
- } else {
- VvmLog.i(TAG, "transcribeVoicemail, empty transcription");
+ .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_EXPIRED);
+ break;
+ default:
Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_EMPTY);
- }
- } else {
- VvmLog.w(TAG, "transcribeVoicemail, no response");
- Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_INVALID);
+ break;
}
+ updateTranscriptionAndState(transcript, VoicemailCompat.TRANSCRIPTION_FAILED);
}
-
- int newState =
- (transcript == null)
- ? VoicemailCompat.TRANSCRIPTION_FAILED
- : VoicemailCompat.TRANSCRIPTION_AVAILABLE;
- updateTranscriptionAndState(transcript, newState);
}
- private static boolean shouldRetryRequest(Status status) {
- return status.getCode() == Status.Code.UNAVAILABLE;
+ protected TranscriptionResponse sendRequest(Request request) {
+ VvmLog.i(TAG, "sendRequest");
+ TranscriptionClient client = clientFactory.getClient();
+ for (int i = 0; i < configProvider.getMaxTranscriptionRetries(); i++) {
+ VvmLog.i(TAG, "sendRequest, try: " + (i + 1));
+ if (i == 0) {
+ Logger.get(context).logImpression(getRequestSentImpression());
+ } else {
+ Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_REQUEST_RETRY);
+ }
+
+ TranscriptionResponse response = request.getResponse(client);
+ if (response.hasRecoverableError()) {
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_RECOVERABLE_ERROR);
+ backoff(i);
+ } else {
+ return response;
+ }
+ }
+
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_TOO_MANY_ERRORS);
+ return null;
}
private static void backoff(int retryCount) {
VvmLog.i(TAG, "backoff, count: " + retryCount);
+ long millis = (1L << retryCount) * 1000;
+ sleep(millis);
+ }
+
+ protected static void sleep(long millis) {
try {
- long millis = (1 << retryCount) * 1000;
Thread.sleep(millis);
} catch (InterruptedException e) {
VvmLog.w(TAG, "interrupted");
@@ -164,13 +194,6 @@ public class TranscriptionTask implements Runnable {
databaseHelper.setTranscriptionState(newState);
}
- private TranscribeVoicemailRequest makeRequest() {
- return TranscribeVoicemailRequest.newBuilder()
- .setVoicemailData(audioData)
- .setAudioFormat(encoding)
- .build();
- }
-
// Uses try-with-resource
@TargetApi(android.os.Build.VERSION_CODES.M)
private boolean readAndValidateAudioFile() {
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java
new file mode 100644
index 000000000..3c41aef89
--- /dev/null
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.voicemail.impl.transcribe;
+
+import android.app.job.JobWorkItem;
+import android.content.Context;
+import android.util.Pair;
+import com.android.dialer.common.Assert;
+import com.android.dialer.logging.DialerImpression;
+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.GetTranscriptRequest;
+import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest;
+import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus;
+
+/**
+ * Background task to get a voicemail transcription using the asynchronous API. The async API works
+ * as follows:
+ *
+ * <ol>
+ * <li>client uploads voicemail data to the server
+ * <li>server responds with a transcription-id and an estimated transcription wait time
+ * <li>client waits appropriate amount of time then begins polling for the result
+ * </ol>
+ *
+ * This implementation blocks until the response or an error is received, even though it is using
+ * the asynchronous server API.
+ */
+public class TranscriptionTaskAsync extends TranscriptionTask {
+ private static final String TAG = "TranscriptionTaskAsync";
+
+ public TranscriptionTaskAsync(
+ Context context,
+ JobCallback callback,
+ JobWorkItem workItem,
+ TranscriptionClientFactory clientFactory,
+ TranscriptionConfigProvider configProvider) {
+ super(context, callback, workItem, clientFactory, configProvider);
+ }
+
+ @Override
+ protected Pair<String, TranscriptionStatus> getTranscription() {
+ VvmLog.i(TAG, "getTranscription");
+
+ TranscriptionResponseAsync uploadResponse =
+ (TranscriptionResponseAsync)
+ sendRequest((client) -> client.sendUploadRequest(getUploadRequest()));
+
+ if (uploadResponse == null) {
+ VvmLog.i(TAG, "getTranscription, failed to upload voicemail.");
+ return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY);
+ } else {
+ waitForTranscription(uploadResponse);
+ return pollForTranscription(uploadResponse);
+ }
+ }
+
+ @Override
+ protected DialerImpression.Type getRequestSentImpression() {
+ return DialerImpression.Type.VVM_TRANSCRIPTION_REQUEST_SENT_ASYNC;
+ }
+
+ private static void waitForTranscription(TranscriptionResponseAsync uploadResponse) {
+ long millis = uploadResponse.getEstimatedWaitMillis();
+ VvmLog.i(TAG, "waitForTranscription, " + millis + " millis");
+ sleep(millis);
+ }
+
+ private Pair<String, TranscriptionStatus> pollForTranscription(
+ TranscriptionResponseAsync uploadResponse) {
+ VvmLog.i(TAG, "pollForTranscription");
+ GetTranscriptRequest request = getGetTranscriptRequest(uploadResponse);
+ for (int i = 0; i < configProvider.getMaxGetTranscriptPolls(); i++) {
+ GetTranscriptResponseAsync response =
+ (GetTranscriptResponseAsync)
+ sendRequest((client) -> client.sendGetTranscriptRequest(request));
+ if (response == null) {
+ VvmLog.i(TAG, "pollForTranscription, no transcription result.");
+ } else if (response.isTranscribing()) {
+ VvmLog.i(TAG, "pollForTranscription, poll count: " + (i + 1));
+ } else if (response.hasFatalError()) {
+ VvmLog.i(TAG, "pollForTranscription, fail. " + response.getErrorDescription());
+ return new Pair<>(null, response.getTranscriptionStatus());
+ } else {
+ VvmLog.i(TAG, "pollForTranscription, got transcription");
+ return new Pair<>(response.getTranscript(), TranscriptionStatus.SUCCESS);
+ }
+ sleep(configProvider.getGetTranscriptPollIntervalMillis());
+ }
+ VvmLog.i(TAG, "pollForTranscription, timed out.");
+ return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY);
+ }
+
+ private TranscribeVoicemailAsyncRequest getUploadRequest() {
+ return TranscribeVoicemailAsyncRequest.newBuilder()
+ .setVoicemailData(audioData)
+ .setAudioFormat(encoding)
+ .build();
+ }
+
+ private GetTranscriptRequest getGetTranscriptRequest(TranscriptionResponseAsync uploadResponse) {
+ Assert.checkArgument(uploadResponse.getTranscriptionId() != null);
+ return GetTranscriptRequest.newBuilder()
+ .setTranscriptionId(uploadResponse.getTranscriptionId())
+ .build();
+ }
+}
diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskSync.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskSync.java
new file mode 100644
index 000000000..bee68590a
--- /dev/null
+++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskSync.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.voicemail.impl.transcribe;
+
+import android.app.job.JobWorkItem;
+import android.content.Context;
+import android.util.Pair;
+import com.android.dialer.logging.DialerImpression;
+import com.android.voicemail.impl.VvmLog;
+import com.android.voicemail.impl.transcribe.TranscriptionService.JobCallback;
+import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory;
+import com.android.voicemail.impl.transcribe.grpc.TranscriptionResponseSync;
+import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailRequest;
+import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus;
+
+/** Background task to get a voicemail transcription using the synchronous API */
+public class TranscriptionTaskSync extends TranscriptionTask {
+ private static final String TAG = "TranscriptionTaskSync";
+
+ public TranscriptionTaskSync(
+ Context context,
+ JobCallback callback,
+ JobWorkItem workItem,
+ TranscriptionClientFactory clientFactory,
+ TranscriptionConfigProvider configProvider) {
+ super(context, callback, workItem, clientFactory, configProvider);
+ }
+
+ @Override
+ protected Pair<String, TranscriptionStatus> getTranscription() {
+ VvmLog.i(TAG, "getTranscription");
+
+ TranscriptionResponseSync response =
+ (TranscriptionResponseSync)
+ sendRequest((client) -> client.sendSyncRequest(getSyncRequest()));
+ if (response == null) {
+ VvmLog.i(TAG, "getTranscription, failed to transcribe voicemail.");
+ return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY);
+ } else {
+ VvmLog.i(TAG, "getTranscription, got transcription");
+ return new Pair<>(response.getTranscript(), TranscriptionStatus.SUCCESS);
+ }
+ }
+
+ @Override
+ protected DialerImpression.Type getRequestSentImpression() {
+ return DialerImpression.Type.VVM_TRANSCRIPTION_REQUEST_SENT;
+ }
+
+ private TranscribeVoicemailRequest getSyncRequest() {
+ return TranscribeVoicemailRequest.newBuilder()
+ .setVoicemailData(audioData)
+ .setAudioFormat(encoding)
+ .build();
+ }
+}
diff --git a/java/com/android/voicemail/impl/transcribe/grpc/GetTranscriptResponseAsync.java b/java/com/android/voicemail/impl/transcribe/grpc/GetTranscriptResponseAsync.java
new file mode 100644
index 000000000..f979d69ce
--- /dev/null
+++ b/java/com/android/voicemail/impl/transcribe/grpc/GetTranscriptResponseAsync.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.voicemail.impl.transcribe.grpc;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import com.android.dialer.common.Assert;
+import com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse;
+import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus;
+import io.grpc.Status;
+
+/** Container for response and status objects for an asynchronous get-transcript request */
+public class GetTranscriptResponseAsync extends TranscriptionResponse {
+ @Nullable private final GetTranscriptResponse response;
+
+ @VisibleForTesting
+ public GetTranscriptResponseAsync(GetTranscriptResponse response) {
+ Assert.checkArgument(response != null);
+ this.response = response;
+ }
+
+ @VisibleForTesting
+ public GetTranscriptResponseAsync(Status status) {
+ super(status);
+ this.response = null;
+ }
+
+ public @Nullable String getTranscript() {
+ if (response != null) {
+ return response.getTranscript();
+ }
+ return null;
+ }
+
+ public @Nullable String getErrorDescription() {
+ if (!hasRecoverableError() && !hasFatalError()) {
+ return null;
+ }
+ if (status != null) {
+ return "Grpc error: " + status;
+ }
+ if (response != null) {
+ return "Transcription error: " + response.getStatus();
+ }
+ Assert.fail("Impossible state");
+ return null;
+ }
+
+ public TranscriptionStatus getTranscriptionStatus() {
+ if (response == null) {
+ return TranscriptionStatus.TRANSCRIPTION_STATUS_UNSPECIFIED;
+ } else {
+ return response.getStatus();
+ }
+ }
+
+ public boolean isTranscribing() {
+ return response != null && response.getStatus() == TranscriptionStatus.PENDING;
+ }
+
+ @Override
+ public boolean hasRecoverableError() {
+ if (super.hasRecoverableError()) {
+ return true;
+ }
+
+ if (response != null) {
+ return response.getStatus() == TranscriptionStatus.EXPIRED
+ || response.getStatus() == TranscriptionStatus.FAILED_RETRY;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean hasFatalError() {
+ if (super.hasFatalError()) {
+ return true;
+ }
+
+ if (response != null) {
+ return response.getStatus() == TranscriptionStatus.FAILED_NO_RETRY
+ || response.getStatus() == TranscriptionStatus.FAILED_LANGUAGE_NOT_SUPPORTED
+ || response.getStatus() == TranscriptionStatus.FAILED_NO_SPEECH_DETECTED;
+ }
+
+ return false;
+ }
+}
diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java
index 27603d910..b18d95627 100644
--- a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java
+++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java
@@ -15,13 +15,11 @@
*/
package com.android.voicemail.impl.transcribe.grpc;
-import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
-import com.android.dialer.common.Assert;
+import com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest;
+import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest;
import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailRequest;
-import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailResponse;
import com.google.internal.communications.voicemailtranscription.v1.VoicemailTranscriptionServiceGrpc;
-import io.grpc.Status;
import io.grpc.StatusRuntimeException;
/** Wrapper around Grpc transcription server stub */
@@ -29,33 +27,35 @@ public class TranscriptionClient {
private final VoicemailTranscriptionServiceGrpc.VoicemailTranscriptionServiceBlockingStub stub;
- /** Wraps the server response and status objects, either of which may be null. */
- public static class TranscriptionResponseWrapper {
- public final TranscribeVoicemailResponse response;
- public final Status status;
+ TranscriptionClient(
+ VoicemailTranscriptionServiceGrpc.VoicemailTranscriptionServiceBlockingStub stub) {
+ this.stub = stub;
+ }
- public TranscriptionResponseWrapper(
- @Nullable TranscribeVoicemailResponse response, @Nullable Status status) {
- Assert.checkArgument(!(response == null && status == null));
- this.response = response;
- this.status = status;
+ @WorkerThread
+ public TranscriptionResponseSync sendSyncRequest(TranscribeVoicemailRequest request) {
+ try {
+ return new TranscriptionResponseSync(stub.transcribeVoicemail(request));
+ } catch (StatusRuntimeException e) {
+ return new TranscriptionResponseSync(e.getStatus());
}
}
- TranscriptionClient(
- VoicemailTranscriptionServiceGrpc.VoicemailTranscriptionServiceBlockingStub stub) {
- this.stub = stub;
+ @WorkerThread
+ public TranscriptionResponseAsync sendUploadRequest(TranscribeVoicemailAsyncRequest request) {
+ try {
+ return new TranscriptionResponseAsync(stub.transcribeVoicemailAsync(request));
+ } catch (StatusRuntimeException e) {
+ return new TranscriptionResponseAsync(e.getStatus());
+ }
}
@WorkerThread
- public TranscriptionResponseWrapper transcribeVoicemail(TranscribeVoicemailRequest request) {
- TranscribeVoicemailResponse response = null;
- Status status = null;
+ public GetTranscriptResponseAsync sendGetTranscriptRequest(GetTranscriptRequest request) {
try {
- response = stub.transcribeVoicemail(request);
+ return new GetTranscriptResponseAsync(stub.getTranscript(request));
} catch (StatusRuntimeException e) {
- status = e.getStatus();
+ return new GetTranscriptResponseAsync(e.getStatus());
}
- return new TranscriptionClient.TranscriptionResponseWrapper(response, status);
}
}
diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClientFactory.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClientFactory.java
index 6101ed516..c57b01fd7 100644
--- a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClientFactory.java
+++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClientFactory.java
@@ -76,7 +76,9 @@ public class TranscriptionClientFactory {
public void shutdown() {
LogUtil.enterBlock("TranscriptionClientFactory.shutdown");
- originalChannel.shutdown();
+ if (!originalChannel.isShutdown()) {
+ originalChannel.shutdown();
+ }
}
private static ManagedChannel getManagedChannel(TranscriptionConfigProvider configProvider) {
diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponse.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponse.java
new file mode 100644
index 000000000..f0823de32
--- /dev/null
+++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponse.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.voicemail.impl.transcribe.grpc;
+
+import android.support.annotation.Nullable;
+import com.android.dialer.common.Assert;
+import io.grpc.Status;
+
+/**
+ * Base class for encapulating a voicemail transcription server response. This handles the Grpc
+ * status response, subclasses will handle request specific responses.
+ */
+public abstract class TranscriptionResponse {
+ @Nullable public final Status status;
+
+ TranscriptionResponse() {
+ this.status = null;
+ }
+
+ TranscriptionResponse(Status status) {
+ Assert.checkArgument(status != null);
+ this.status = status;
+ }
+
+ public boolean hasRecoverableError() {
+ if (status != null) {
+ return status.getCode() == Status.Code.UNAVAILABLE;
+ }
+
+ return false;
+ }
+
+ public boolean hasFatalError() {
+ if (status != null) {
+ return status.getCode() != Status.Code.OK && status.getCode() != Status.Code.UNAVAILABLE;
+ }
+
+ return false;
+ }
+}
diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseAsync.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseAsync.java
new file mode 100644
index 000000000..38b463053
--- /dev/null
+++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseAsync.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.voicemail.impl.transcribe.grpc;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import com.android.dialer.common.Assert;
+import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncResponse;
+import io.grpc.Status;
+
+/** Container for response and status objects for an asynchronous transcription upload request */
+public class TranscriptionResponseAsync extends TranscriptionResponse {
+ @Nullable private final TranscribeVoicemailAsyncResponse response;
+
+ @VisibleForTesting
+ public TranscriptionResponseAsync(TranscribeVoicemailAsyncResponse response) {
+ Assert.checkArgument(response != null);
+ this.response = response;
+ }
+
+ @VisibleForTesting
+ public TranscriptionResponseAsync(Status status) {
+ super(status);
+ this.response = null;
+ }
+
+ public @Nullable String getTranscriptionId() {
+ if (response != null) {
+ return response.getTranscriptionId();
+ }
+ return null;
+ }
+
+ public long getEstimatedWaitMillis() {
+ if (response != null) {
+ return response.getEstimatedWaitSecs() * 1_000L;
+ }
+ return 0;
+ }
+}
diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseSync.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseSync.java
new file mode 100644
index 000000000..d2e2e218c
--- /dev/null
+++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseSync.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.voicemail.impl.transcribe.grpc;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import com.android.dialer.common.Assert;
+import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailResponse;
+import io.grpc.Status;
+
+/** Container for response and status objects for a synchronous transcription request */
+public class TranscriptionResponseSync extends TranscriptionResponse {
+ @Nullable private final TranscribeVoicemailResponse response;
+
+ @VisibleForTesting
+ public TranscriptionResponseSync(Status status) {
+ super(status);
+ this.response = null;
+ }
+
+ @VisibleForTesting
+ public TranscriptionResponseSync(TranscribeVoicemailResponse response) {
+ Assert.checkArgument(response != null);
+ this.response = response;
+ }
+
+ public @Nullable String getTranscript() {
+ return (response != null) ? response.getTranscript() : null;
+ }
+}
diff --git a/java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto b/java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto
index dd2170ade..a2064d193 100644
--- a/java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto
+++ b/java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto
@@ -19,6 +19,43 @@ enum AudioFormat {
AMR_NB_8KHZ = 1;
}
+// Enum that describes the status of the transcription process.
+enum TranscriptionStatus {
+ // Default but invalid value.
+ TRANSCRIPTION_STATUS_UNSPECIFIED = 0;
+
+ // Transcription was successful and the transcript is present.
+ SUCCESS = 1;
+
+ // Transcription is progress. Check again later.
+ PENDING = 2;
+
+ // Transcription was successful, but the expiration period has passed, which
+ // means that the sensative data (including the transcript) has been deleted.
+ // Resend the voicemail through TranscribeVoicemailAsync to retry.
+ EXPIRED = 3;
+
+ // Internal error encountered during the transcription.
+ // Resend the voicemail through TranscribeVoicemailAsync to retry.
+ // This is a catch-all status for all retriable errors that aren't captured by
+ // a more specfic status.
+ FAILED_RETRY = 4;
+
+ // Internal error encountered during the transcription.
+ // Do not resend the voicemail.
+ // This is a catch-all status for all non-retriable errors that aren't
+ // captured by a more specfic status.
+ FAILED_NO_RETRY = 5;
+
+ // The language detected is not yet supported by this service.
+ // Do not resend the voicemail.
+ FAILED_LANGUAGE_NOT_SUPPORTED = 6;
+
+ // No speech was detected in the voicemail.
+ // Do not resend the voicemail.
+ FAILED_NO_SPEECH_DETECTED = 7;
+}
+
// Request for synchronous voicemail transcription.
message TranscribeVoicemailRequest {
// Voicemail audio file containing the raw bytes we receive from the carrier.
@@ -34,11 +71,62 @@ message TranscribeVoicemailResponse {
optional string transcript = 1;
}
+// Request for asynchronous voicemail transcription.
+message TranscribeVoicemailAsyncRequest {
+ // Voicemail audio data encoded in the format specified by audio_format.
+ optional bytes voicemail_data = 1;
+
+ // Audio format of the voicemail file.
+ optional AudioFormat audio_format = 2;
+}
+
+// Response for asynchronous voicemail transcription containing information
+// needed to fetch the transcription results through the GetTranscript method.
+message TranscribeVoicemailAsyncResponse {
+ // Unique ID for the transcription. This ID is used for retrieving the
+ // voicemail transcript later.
+ optional string transcription_id = 1;
+
+ // The estimated amount of time in seconds before the transcription will be
+ // available.
+ // The client should not call GetTranscript until this time has elapsed, but
+ // the transcript is not guaranteed to be ready by this time.
+ optional int64 estimated_wait_secs = 2;
+}
+
+// Request for retrieving an asynchronously generated transcript.
+message GetTranscriptRequest {
+ // Unique ID for the transcription. This ID was returned by
+ // TranscribeVoicemailAsync.
+ optional string transcription_id = 1;
+}
+
+// Response for retrieving an asynchronously generated transcript.
+message GetTranscriptResponse {
+ // Status of the trascription process.
+ optional TranscriptionStatus status = 1;
+
+ // The transcribed text of the voicemail. This is only present if the status
+ // is SUCCESS.
+ optional string transcript = 2;
+}
+
// RPC service for transcribing voicemails.
service VoicemailTranscriptionService {
// Returns a transcript of the given voicemail.
rpc TranscribeVoicemail(TranscribeVoicemailRequest)
returns (TranscribeVoicemailResponse) {}
+
+ // Schedules a transcription of the given voicemail. The transcript can be
+ // retrieved using the returned ID.
+ rpc TranscribeVoicemailAsync(TranscribeVoicemailAsyncRequest)
+ returns (TranscribeVoicemailAsyncResponse) {
+ }
+
+ // Returns the transcript corresponding to the given ID, which was returned
+ // by TranscribeVoicemailAsync.
+ rpc GetTranscript(GetTranscriptRequest) returns (GetTranscriptResponse) {
+ }
}
// LINT.ThenChange(//depot/google3/google/internal/communications/voicemailtranscription/v1/\