diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2018-01-14 19:26:10 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-01-14 19:26:10 +0000 |
commit | 0123d0030fd6dcbb33020240217a40e641d60c5e (patch) | |
tree | e50dd75250358ee2cf1a86eecbbbef2e40daab79 | |
parent | 25f24a79a24334e23706c2a8d69903820c622edb (diff) | |
parent | 9fbb81d83f24d6f7ad9832bc16c3a41065eb9337 (diff) |
Merge "Make sure that voicemail transcriptions are processed serially"
5 files changed, 124 insertions, 18 deletions
diff --git a/java/com/android/voicemail/impl/AndroidManifest.xml b/java/com/android/voicemail/impl/AndroidManifest.xml index e7ab5818e..1998d35f6 100644 --- a/java/com/android/voicemail/impl/AndroidManifest.xml +++ b/java/com/android/voicemail/impl/AndroidManifest.xml @@ -141,6 +141,10 @@ <receiver android:name="com.android.voicemail.impl.transcribe.GetTranscriptReceiver" android:exported="false"> + <intent-filter> + <action + android:name="com.android.voicemail.impl.transcribe.GetTranscriptReceiver.POLL_ALARM" /> + </intent-filter> </receiver> </application> </manifest> diff --git a/java/com/android/voicemail/impl/transcribe/GetTranscriptReceiver.java b/java/com/android/voicemail/impl/transcribe/GetTranscriptReceiver.java index cc204ff53..cbf165753 100644 --- a/java/com/android/voicemail/impl/transcribe/GetTranscriptReceiver.java +++ b/java/com/android/voicemail/impl/transcribe/GetTranscriptReceiver.java @@ -23,11 +23,13 @@ import android.content.Intent; import android.net.Uri; import android.os.SystemClock; import android.support.annotation.Nullable; +import android.telecom.PhoneAccountHandle; import android.util.Pair; import com.android.dialer.common.Assert; import com.android.dialer.common.backoff.ExponentialBaseCalculator; import com.android.dialer.common.concurrent.DialerExecutor.Worker; import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.voicemail.impl.VvmLog; @@ -36,6 +38,7 @@ import com.android.voicemail.impl.transcribe.grpc.TranscriptionClient; import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory; import com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest; import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus; +import java.util.List; /** * This class uses the AlarmManager to poll for the result of a voicemail transcription request. @@ -50,6 +53,9 @@ public class GetTranscriptReceiver extends BroadcastReceiver { static final String EXTRA_DELAY_MILLIS = "extra_delay_millis"; static final String EXTRA_BASE_MULTIPLIER = "extra_base_multiplier"; static final String EXTRA_REMAINING_ATTEMPTS = "extra_remaining_attempts"; + static final String EXTRA_PHONE_ACCOUNT = "extra_phone_account"; + static final String POLL_ALARM_ACTION = + "com.android.voicemail.impl.transcribe.GetTranscriptReceiver.POLL_ALARM"; // Schedule an initial alarm to begin checking for a voicemail transcription result. static void beginPolling( @@ -57,7 +63,9 @@ public class GetTranscriptReceiver extends BroadcastReceiver { Uri voicemailUri, String transcriptId, long estimatedTranscriptionTimeMillis, - TranscriptionConfigProvider configProvider) { + TranscriptionConfigProvider configProvider, + PhoneAccountHandle account) { + Assert.checkState(!hasPendingAlarm(context)); long initialDelayMillis = configProvider.getInitialGetTranscriptPollDelayMillis(); long maxBackoffMillis = configProvider.getMaxGetTranscriptPollTimeMillis(); int maxAttempts = configProvider.getMaxGetTranscriptPolls(); @@ -65,7 +73,13 @@ public class GetTranscriptReceiver extends BroadcastReceiver { ExponentialBaseCalculator.findBase(initialDelayMillis, maxBackoffMillis, maxAttempts); Intent intent = makeAlarmIntent( - context, voicemailUri, transcriptId, initialDelayMillis, baseMultiplier, maxAttempts); + context, + voicemailUri, + transcriptId, + initialDelayMillis, + baseMultiplier, + maxAttempts, + account); // Add an extra to distinguish this initial estimated transcription wait from subsequent backoff // waits intent.putExtra(EXTRA_IS_INITIAL_ESTIMATED_WAIT, true); @@ -77,9 +91,17 @@ public class GetTranscriptReceiver extends BroadcastReceiver { scheduleAlarm(context, estimatedTranscriptionTimeMillis, intent); } + static boolean hasPendingAlarm(Context context) { + Intent intent = makeBaseAlarmIntent(context); + return getPendingIntent(context, intent, PendingIntent.FLAG_NO_CREATE) != null; + } + // Alarm fired, poll for transcription result on a background thread @Override public void onReceive(Context context, Intent intent) { + if (intent == null || !POLL_ALARM_ACTION.equals(intent.getAction())) { + return; + } String transcriptId = intent.getStringExtra(EXTRA_TRANSCRIPT_ID); VvmLog.i(TAG, "onReceive, for transcript id: " + transcriptId); DialerExecutorComponent.get(context) @@ -101,7 +123,7 @@ public class GetTranscriptReceiver extends BroadcastReceiver { private static void scheduleAlarm(Context context, long delayMillis, Intent intent) { PendingIntent alarmIntent = - PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + getPendingIntent(context, intent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); alarmMgr.set( AlarmManager.ELAPSED_REALTIME_WAKEUP, @@ -109,22 +131,46 @@ public class GetTranscriptReceiver extends BroadcastReceiver { alarmIntent); } + private static boolean cancelAlarm(Context context, Intent intent) { + PendingIntent alarmIntent = getPendingIntent(context, intent, PendingIntent.FLAG_NO_CREATE); + if (alarmIntent != null) { + AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmMgr.cancel(alarmIntent); + alarmIntent.cancel(); + return true; + } else { + return false; + } + } + private static Intent makeAlarmIntent( Context context, Uri voicemailUri, String transcriptId, long delayMillis, double baseMultiplier, - int remainingAttempts) { - Intent intent = new Intent(context, GetTranscriptReceiver.class); + int remainingAttempts, + PhoneAccountHandle account) { + Intent intent = makeBaseAlarmIntent(context); intent.putExtra(EXTRA_VOICEMAIL_URI, voicemailUri); intent.putExtra(EXTRA_TRANSCRIPT_ID, transcriptId); intent.putExtra(EXTRA_DELAY_MILLIS, delayMillis); intent.putExtra(EXTRA_BASE_MULTIPLIER, baseMultiplier); intent.putExtra(EXTRA_REMAINING_ATTEMPTS, remainingAttempts); + intent.putExtra(EXTRA_PHONE_ACCOUNT, account); + return intent; + } + + private static Intent makeBaseAlarmIntent(Context context) { + Intent intent = new Intent(context.getApplicationContext(), GetTranscriptReceiver.class); + intent.setAction(POLL_ALARM_ACTION); return intent; } + private static PendingIntent getPendingIntent(Context context, Intent intent, int flags) { + return PendingIntent.getBroadcast(context.getApplicationContext(), 0, intent, flags); + } + private static class PollWorker implements Worker<Intent, Void> { private final Context context; @@ -158,9 +204,33 @@ public class GetTranscriptReceiver extends BroadcastReceiver { Uri voicemailUri = intent.getParcelableExtra(EXTRA_VOICEMAIL_URI); TranscriptionDbHelper dbHelper = new TranscriptionDbHelper(context, voicemailUri); TranscriptionTask.recordResult(context, result, dbHelper); + + // Check if there are other pending transcriptions + PhoneAccountHandle account = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT); + processPendingTranscriptions(account); return null; } + private void processPendingTranscriptions(PhoneAccountHandle account) { + TranscriptionDbHelper dbHelper = new TranscriptionDbHelper(context); + List<Uri> inProgress = dbHelper.getTranscribingVoicemails(); + if (!inProgress.isEmpty()) { + Uri uri = inProgress.get(0); + VvmLog.i(TAG, "getPendingTranscription, found pending transcription " + uri); + if (hasPendingAlarm(context)) { + // Cancel the current alarm so that the next transcription task won't be postponed + cancelAlarm(context, makeBaseAlarmIntent(context)); + } + ThreadUtil.postOnUiThread( + () -> { + TranscriptionService.scheduleNewVoicemailTranscriptionJob( + context, uri, account, true); + }); + } else { + VvmLog.i(TAG, "getPendingTranscription, no more pending transcriptions"); + } + } + private Pair<String, TranscriptionStatus> pollForTranscription(String transcriptId) { VvmLog.i(TAG, "pollForTranscription, transcript id: " + transcriptId); GetTranscriptRequest request = getGetTranscriptRequest(transcriptId); @@ -214,7 +284,8 @@ public class GetTranscriptReceiver extends BroadcastReceiver { previous.getStringExtra(EXTRA_TRANSCRIPT_ID), nextDelay, baseMultiplier, - remainingAttempts); + remainingAttempts, + previous.getParcelableExtra(EXTRA_PHONE_ACCOUNT)); } } diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionDbHelper.java b/java/com/android/voicemail/impl/transcribe/TranscriptionDbHelper.java index a9a37225b..24c07cc37 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionDbHelper.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionDbHelper.java @@ -22,11 +22,10 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.os.Build.VERSION_CODES; +import android.os.Build; import android.provider.VoicemailContract.Voicemails; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; -import android.support.v4.os.BuildCompat; import android.util.Pair; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; @@ -35,7 +34,7 @@ import java.util.ArrayList; import java.util.List; /** Helper class for reading and writing transcription data in the database */ -@TargetApi(VERSION_CODES.O) +@TargetApi(Build.VERSION_CODES.O) public class TranscriptionDbHelper { @VisibleForTesting static final String[] PROJECTION = @@ -63,9 +62,8 @@ public class TranscriptionDbHelper { } @WorkerThread - @TargetApi(VERSION_CODES.M) // used for try with resources Pair<String, Integer> getTranscriptionAndState() { - Assert.checkState(BuildCompat.isAtLeastO()); + Assert.checkState(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O); Assert.isWorkerThread(); try (Cursor cursor = contentResolver.query(uri, PROJECTION, null, null, null)) { if (cursor == null) { @@ -84,9 +82,8 @@ public class TranscriptionDbHelper { } @WorkerThread - @TargetApi(VERSION_CODES.M) // used for try with resources List<Uri> getUntranscribedVoicemails() { - Assert.checkArgument(BuildCompat.isAtLeastO()); + Assert.checkState(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O); Assert.isWorkerThread(); List<Uri> untranscribed = new ArrayList<>(); String whereClause = @@ -105,6 +102,25 @@ public class TranscriptionDbHelper { } @WorkerThread + List<Uri> getTranscribingVoicemails() { + Assert.checkState(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O); + Assert.isWorkerThread(); + List<Uri> inProgress = new ArrayList<>(); + String whereClause = VoicemailCompat.TRANSCRIPTION_STATE + "=?"; + String[] whereArgs = {String.valueOf(VoicemailCompat.TRANSCRIPTION_IN_PROGRESS)}; + try (Cursor cursor = contentResolver.query(uri, PROJECTION, whereClause, whereArgs, null)) { + if (cursor == null) { + LogUtil.e("TranscriptionDbHelper.getTranscribingVoicemails", "query failed."); + } else { + while (cursor.moveToNext()) { + inProgress.add(ContentUris.withAppendedId(uri, cursor.getLong(ID))); + } + } + } + return inProgress; + } + + @WorkerThread void setTranscriptionState(int transcriptionState) { Assert.isWorkerThread(); LogUtil.i( diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java index 0f5300358..c206c0818 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java @@ -63,8 +63,7 @@ public class TranscriptionService extends JobService { } // Schedule a task to transcribe the indicated voicemail, return true if transcription task was - // scheduled. If the PhoneAccountHandle is null then the voicemail will not be considered for - // donation. + // scheduled. @MainThread public static boolean scheduleNewVoicemailTranscriptionJob( Context context, Uri voicemailUri, PhoneAccountHandle account, boolean highPriority) { diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java index bb7aa5f38..f6035fd2c 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java @@ -58,9 +58,24 @@ public class TranscriptionTaskAsync extends TranscriptionTask { protected Pair<String, TranscriptionStatus> getTranscription() { VvmLog.i(TAG, "getTranscription"); + if (GetTranscriptReceiver.hasPendingAlarm(context)) { + // Don't start a transcription while another is still active + VvmLog.i( + TAG, + "getTranscription, pending transcription, postponing transcription of: " + voicemailUri); + return new Pair<>(null, null); + } + + TranscribeVoicemailAsyncRequest uploadRequest = getUploadRequest(); + VvmLog.i( + TAG, + "getTranscription, uploading voicemail: " + + voicemailUri + + ", id: " + + uploadRequest.getTranscriptionId()); TranscriptionResponseAsync uploadResponse = (TranscriptionResponseAsync) - sendRequest((client) -> client.sendUploadRequest(getUploadRequest())); + sendRequest((client) -> client.sendUploadRequest(uploadRequest)); if (cancelled) { VvmLog.i(TAG, "getTranscription, cancelled."); @@ -72,13 +87,14 @@ public class TranscriptionTaskAsync extends TranscriptionTask { VvmLog.i(TAG, "getTranscription, upload error: " + uploadResponse.status); return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY); } else { - VvmLog.i(TAG, "getTranscription, begin polling for result."); + VvmLog.i(TAG, "getTranscription, begin polling for: " + uploadResponse.getTranscriptionId()); GetTranscriptReceiver.beginPolling( context, voicemailUri, uploadResponse.getTranscriptionId(), uploadResponse.getEstimatedWaitMillis(), - configProvider); + configProvider, + phoneAccountHandle); // This indicates that the result is not available yet return new Pair<>(null, null); } |