diff options
author | mdooley <mdooley@google.com> | 2017-08-16 13:25:50 -0700 |
---|---|---|
committer | Eric Erfanian <erfanian@google.com> | 2017-08-30 15:44:51 +0000 |
commit | 0feba780889cb1e67df64a457109fc6134111261 (patch) | |
tree | 17b86dc18831c4f58088f0aea29f81c3886b3958 /java/com/android/voicemail/impl | |
parent | 780a361bb4416d1b3e34ed8bb3dab213070fcd17 (diff) |
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
Diffstat (limited to 'java/com/android/voicemail/impl')
7 files changed, 149 insertions, 18 deletions
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 @@ -97,6 +97,11 @@ android:exported="false"/> <service + android:name="com.android.voicemail.impl.transcribe.TranscriptionBackfillService" + android:permission="android.permission.BIND_JOB_SERVICE" + android:exported="false"/> + + <service android:name="com.android.voicemail.impl.OmtpService" android:permission="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE" android:exported="true" diff --git a/java/com/android/voicemail/impl/VoicemailClientImpl.java b/java/com/android/voicemail/impl/VoicemailClientImpl.java index 1b9890381..9bb14f2dc 100644 --- a/java/com/android/voicemail/impl/VoicemailClientImpl.java +++ b/java/com/android/voicemail/impl/VoicemailClientImpl.java @@ -36,6 +36,7 @@ import com.android.voicemail.impl.settings.VisualVoicemailSettingsUtil; import com.android.voicemail.impl.settings.VoicemailChangePinActivity; import com.android.voicemail.impl.settings.VoicemailSettingsFragment; import com.android.voicemail.impl.sync.VvmAccountManager; +import com.android.voicemail.impl.transcribe.TranscriptionBackfillService; import java.util.List; import javax.inject.Inject; @@ -230,4 +231,10 @@ public class VoicemailClientImpl implements VoicemailClient { where.append(")"); } } + + @Override + public void onTosAccepted(Context context) { + LogUtil.i("VoicemailClientImpl.onTosAccepted", "try backfilling voicemail transcriptions"); + TranscriptionBackfillService.scheduleTask(context); + } } diff --git a/java/com/android/voicemail/impl/fetch/VoicemailFetchedCallback.java b/java/com/android/voicemail/impl/fetch/VoicemailFetchedCallback.java index d15ce12ef..3e825407b 100644 --- a/java/com/android/voicemail/impl/fetch/VoicemailFetchedCallback.java +++ b/java/com/android/voicemail/impl/fetch/VoicemailFetchedCallback.java @@ -97,7 +97,7 @@ public class VoicemailFetchedCallback { if (updateVoicemail(values)) { ThreadUtil.postOnUiThread( () -> { - 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<Uri> 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<String, Integer> getTranscriptionAndState() { - Assert.checkArgument(BuildCompat.isAtLeastO()); + Assert.checkState(BuildCompat.isAtLeastO()); Assert.isWorkerThread(); try (Cursor cursor = contentResolver.query(uri, PROJECTION, null, null, null)) { if (cursor == null) { @@ -72,6 +83,27 @@ public class TranscriptionDbHelper { } @WorkerThread + @TargetApi(VERSION_CODES.M) // used for try with resources + List<Uri> getUntranscribedVoicemails() { + Assert.checkArgument(BuildCompat.isAtLeastO()); + Assert.isWorkerThread(); + List<Uri> 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(); LogUtil.i( 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); - } } |