From 0feba780889cb1e67df64a457109fc6134111261 Mon Sep 17 00:00:00 2001 From: mdooley Date: Wed, 16 Aug 2017 13:25:50 -0700 Subject: Transcribe old voicemails This cl adds a transcription backfill service to transcribe old voicemails. This service queries the database for any voicemails without a transcription and whose transcription_state column has not been set and schedules a transcription task to update them. This service is only run once, after the user accepts the voicemail TOS and we have an un-metered network connection. Bug: 62423649 Test: manual and updated unit tests PiperOrigin-RevId: 165486104 Change-Id: Ic85c9d728937411638074fec07cf44bb83862acb --- java/com/android/voicemail/VoicemailClient.java | 2 + .../com/android/voicemail/impl/AndroidManifest.xml | 5 ++ .../voicemail/impl/VoicemailClientImpl.java | 7 ++ .../impl/fetch/VoicemailFetchedCallback.java | 2 +- .../transcribe/TranscriptionBackfillService.java | 79 ++++++++++++++++++++++ .../impl/transcribe/TranscriptionDbHelper.java | 44 ++++++++++-- .../impl/transcribe/TranscriptionService.java | 24 +++++-- .../impl/transcribe/TranscriptionTask.java | 6 +- .../voicemail/stub/StubVoicemailClient.java | 3 + 9 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java (limited to 'java/com/android/voicemail') diff --git a/java/com/android/voicemail/VoicemailClient.java b/java/com/android/voicemail/VoicemailClient.java index 28d2bf0ff..21846a480 100644 --- a/java/com/android/voicemail/VoicemailClient.java +++ b/java/com/android/voicemail/VoicemailClient.java @@ -148,4 +148,6 @@ public interface VoicemailClient { @MainThread void onShutdown(@NonNull Context context); + + void onTosAccepted(Context context); } diff --git a/java/com/android/voicemail/impl/AndroidManifest.xml b/java/com/android/voicemail/impl/AndroidManifest.xml index 69d9efe64..4cad2247c 100644 --- a/java/com/android/voicemail/impl/AndroidManifest.xml +++ b/java/com/android/voicemail/impl/AndroidManifest.xml @@ -96,6 +96,11 @@ android:permission="android.permission.BIND_JOB_SERVICE" android:exported="false"/> + + { - if (!TranscriptionService.transcribeVoicemail(mContext, mUri)) { + if (!TranscriptionService.scheduleNewVoicemailTranscriptionJob(mContext, mUri, true)) { VvmLog.w(TAG, String.format("Failed to schedule transcription for %s", mUri)); } }); diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java new file mode 100644 index 000000000..d6e362014 --- /dev/null +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionBackfillService.java @@ -0,0 +1,79 @@ +/* + * 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.JobInfo; +import android.app.job.JobScheduler; +import android.app.job.JobWorkItem; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.v4.app.JobIntentService; +import android.support.v4.os.BuildCompat; +import com.android.dialer.common.LogUtil; +import com.android.dialer.constants.ScheduledJobIds; +import java.util.List; + +/** + * JobScheduler service for transcribing old voicemails. This service does a database scan for + * un-transcribed voicemails and schedules transcription tasks for them, once we have an un-metered + * network connection. + */ +public class TranscriptionBackfillService extends JobIntentService { + + /** Schedule a task to scan the database for untranscribed voicemails */ + public static boolean scheduleTask(Context context) { + if (BuildCompat.isAtLeastO()) { + LogUtil.enterBlock("TranscriptionBackfillService.transcribeOldVoicemails"); + ComponentName componentName = new ComponentName(context, TranscriptionBackfillService.class); + JobInfo.Builder builder = + new JobInfo.Builder(ScheduledJobIds.VVM_TRANSCRIPTION_BACKFILL_JOB, componentName) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); + JobScheduler scheduler = context.getSystemService(JobScheduler.class); + return scheduler.enqueue(builder.build(), makeWorkItem()) == JobScheduler.RESULT_SUCCESS; + } else { + LogUtil.i("TranscriptionBackfillService.transcribeOldVoicemails", "not supported"); + return false; + } + } + + private static JobWorkItem makeWorkItem() { + Intent intent = new Intent(); + return new JobWorkItem(intent); + } + + @Override + protected void onHandleWork(Intent intent) { + LogUtil.enterBlock("TranscriptionBackfillService.onHandleWork"); + + TranscriptionDbHelper dbHelper = new TranscriptionDbHelper(this); + List untranscribed = dbHelper.getUntranscribedVoicemails(); + LogUtil.i( + "TranscriptionBackfillService.onHandleWork", + "found " + untranscribed.size() + " untranscribed voicemails"); + // TODO(mdooley): Consider doing the actual transcriptions here instead of scheduling jobs. + for (Uri uri : untranscribed) { + TranscriptionService.scheduleNewVoicemailTranscriptionJob(this, uri, false); + } + } + + @Override + public void onDestroy() { + LogUtil.enterBlock("TranscriptionBackfillService.onDestroy"); + super.onDestroy(); + } +} diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionDbHelper.java b/java/com/android/voicemail/impl/transcribe/TranscriptionDbHelper.java index cbc5cb8a0..9d3c2e4a7 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionDbHelper.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionDbHelper.java @@ -17,29 +17,36 @@ package com.android.voicemail.impl.transcribe; import android.annotation.TargetApi; import android.content.ContentResolver; +import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build.VERSION_CODES; import android.provider.VoicemailContract.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; +import java.util.ArrayList; +import java.util.List; /** Helper class for reading and writing transcription data in the database */ @TargetApi(VERSION_CODES.O) public class TranscriptionDbHelper { - private static final String[] PROJECTION = + @VisibleForTesting + static final String[] PROJECTION = new String[] { - Voicemails.TRANSCRIPTION, // 0 - VoicemailCompat.TRANSCRIPTION_STATE // 1 + Voicemails._ID, // 0 + Voicemails.TRANSCRIPTION, // 1 + VoicemailCompat.TRANSCRIPTION_STATE // 2 }; - public static final int TRANSCRIPTION = 0; - public static final int TRANSCRIPTION_STATE = 1; + static final int ID = 0; + static final int TRANSCRIPTION = 1; + static final int TRANSCRIPTION_STATE = 2; private final ContentResolver contentResolver; private final Uri uri; @@ -50,10 +57,14 @@ public class TranscriptionDbHelper { this.uri = uri; } + TranscriptionDbHelper(Context context) { + this(context, Voicemails.buildSourceUri(context.getPackageName())); + } + @WorkerThread @TargetApi(VERSION_CODES.M) // used for try with resources Pair getTranscriptionAndState() { - Assert.checkArgument(BuildCompat.isAtLeastO()); + Assert.checkState(BuildCompat.isAtLeastO()); Assert.isWorkerThread(); try (Cursor cursor = contentResolver.query(uri, PROJECTION, null, null, null)) { if (cursor == null) { @@ -71,6 +82,27 @@ public class TranscriptionDbHelper { return null; } + @WorkerThread + @TargetApi(VERSION_CODES.M) // used for try with resources + List getUntranscribedVoicemails() { + Assert.checkArgument(BuildCompat.isAtLeastO()); + Assert.isWorkerThread(); + List untranscribed = new ArrayList<>(); + String whereClause = + Voicemails.TRANSCRIPTION + " is NULL AND " + VoicemailCompat.TRANSCRIPTION_STATE + "=?"; + String[] whereArgs = {String.valueOf(VoicemailCompat.TRANSCRIPTION_NOT_STARTED)}; + try (Cursor cursor = contentResolver.query(uri, PROJECTION, whereClause, whereArgs, null)) { + if (cursor == null) { + LogUtil.e("TranscriptionDbHelper.getUntranscribedVoicemails", "query failed."); + } else { + while (cursor.moveToNext()) { + untranscribed.add(ContentUris.withAppendedId(uri, cursor.getLong(ID))); + } + } + } + return untranscribed; + } + @WorkerThread void setTranscriptionState(int transcriptionState) { Assert.isWorkerThread(); diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java index 6933a3a66..85e524302 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java @@ -58,22 +58,30 @@ public class TranscriptionService extends JobService { // Schedule a task to transcribe the indicated voicemail, return true if transcription task was // scheduled. - public static boolean transcribeVoicemail(Context context, Uri voicemailUri) { + public static boolean scheduleNewVoicemailTranscriptionJob( + Context context, Uri voicemailUri, boolean highPriority) { Assert.isMainThread(); if (BuildCompat.isAtLeastO()) { - LogUtil.i("TranscriptionService.transcribeVoicemail", "scheduling transcription"); + LogUtil.i( + "TranscriptionService.scheduleNewVoicemailTranscriptionJob", "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) - .setMinimumLatency(0) - .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + new JobInfo.Builder(ScheduledJobIds.VVM_TRANSCRIPTION_JOB, componentName); + if (highPriority) { + builder + .setMinimumLatency(0) + .setOverrideDeadline(0) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + } else { + builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); + } JobScheduler scheduler = context.getSystemService(JobScheduler.class); JobWorkItem workItem = makeWorkItem(voicemailUri); return scheduler.enqueue(builder.build(), workItem) == JobScheduler.RESULT_SUCCESS; } else { - LogUtil.i("TranscriptionService.transcribeVoicemail", "not supported"); + LogUtil.i("TranscriptionService.scheduleNewVoicemailTranscriptionJob", "not supported"); return false; } } @@ -165,6 +173,10 @@ public class TranscriptionService extends JobService { } } + static Uri getVoicemailUri(JobWorkItem workItem) { + return workItem.getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI); + } + private ExecutorService getExecutorService() { if (executorService == null) { // The common use case is transcribing a single voicemail so just use a single thread executor diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java index 61a5ee8de..b5f29da00 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java @@ -81,7 +81,7 @@ public abstract class TranscriptionTask implements Runnable { this.callback = callback; this.workItem = workItem; this.clientFactory = clientFactory; - this.voicemailUri = getVoicemailUri(workItem); + this.voicemailUri = TranscriptionService.getVoicemailUri(workItem); this.configProvider = configProvider; databaseHelper = new TranscriptionDbHelper(context, voicemailUri); } @@ -222,8 +222,4 @@ public abstract class TranscriptionTask implements Runnable { return true; } - - private static Uri getVoicemailUri(JobWorkItem workItem) { - return workItem.getIntent().getParcelableExtra(TranscriptionService.EXTRA_VOICEMAIL_URI); - } } diff --git a/java/com/android/voicemail/stub/StubVoicemailClient.java b/java/com/android/voicemail/stub/StubVoicemailClient.java index e5d51455c..992950340 100644 --- a/java/com/android/voicemail/stub/StubVoicemailClient.java +++ b/java/com/android/voicemail/stub/StubVoicemailClient.java @@ -99,4 +99,7 @@ public final class StubVoicemailClient implements VoicemailClient { @Override public void onShutdown(@NonNull Context context) {} + + @Override + public void onTosAccepted(Context context) {} } -- cgit v1.2.3