Merge "speech: Add api to check request is supported"

This commit is contained in:
Andrea Ambu
2022-01-19 09:34:02 +00:00
committed by Android (Google) Code Review
10 changed files with 739 additions and 9 deletions

View File

@@ -40053,8 +40053,10 @@ package android.speech {
ctor public RecognitionService();
method public final android.os.IBinder onBind(android.content.Intent);
method protected abstract void onCancel(android.speech.RecognitionService.Callback);
method public void onCheckRecognitionSupport(@NonNull android.content.Intent, @NonNull android.speech.RecognitionService.SupportCallback);
method protected abstract void onStartListening(android.content.Intent, android.speech.RecognitionService.Callback);
method protected abstract void onStopListening(android.speech.RecognitionService.Callback);
method public void triggerModelDownload(@NonNull android.content.Intent);
field public static final String SERVICE_INTERFACE = "android.speech.RecognitionService";
field public static final String SERVICE_META_DATA = "android.speech";
}
@@ -40072,6 +40074,36 @@ package android.speech {
method public void rmsChanged(float) throws android.os.RemoteException;
}
public static class RecognitionService.SupportCallback {
method public void onError(int);
method public void onSupportResult(@NonNull android.speech.RecognitionSupport);
}
public final class RecognitionSupport implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getInstalledLanguages();
method @NonNull public java.util.List<java.lang.String> getPendingLanguages();
method @NonNull public java.util.List<java.lang.String> getSupportedLanguages();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.speech.RecognitionSupport> CREATOR;
}
public static final class RecognitionSupport.Builder {
ctor public RecognitionSupport.Builder();
method @NonNull public android.speech.RecognitionSupport.Builder addInstalledLanguages(@NonNull String);
method @NonNull public android.speech.RecognitionSupport.Builder addPendingLanguages(@NonNull String);
method @NonNull public android.speech.RecognitionSupport.Builder addSupportedLanguages(@NonNull String);
method @NonNull public android.speech.RecognitionSupport build();
method @NonNull public android.speech.RecognitionSupport.Builder setInstalledLanguages(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.speech.RecognitionSupport.Builder setPendingLanguages(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.speech.RecognitionSupport.Builder setSupportedLanguages(@NonNull java.util.List<java.lang.String>);
}
public interface RecognitionSupportCallback {
method public void onError(int);
method public void onSupportResult(@NonNull android.speech.RecognitionSupport);
}
public class RecognizerIntent {
method public static final android.content.Intent getVoiceDetailsIntent(android.content.Context);
field public static final String ACTION_GET_LANGUAGE_DETAILS = "android.speech.action.GET_LANGUAGE_DETAILS";
@@ -40121,6 +40153,7 @@ package android.speech {
public class SpeechRecognizer {
method @MainThread public void cancel();
method public void checkRecognitionSupport(@NonNull android.content.Intent, @NonNull android.speech.RecognitionSupportCallback);
method @MainThread @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context);
method @MainThread public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context);
method @MainThread public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName);
@@ -40130,8 +40163,10 @@ package android.speech {
method @MainThread public void setRecognitionListener(android.speech.RecognitionListener);
method @MainThread public void startListening(android.content.Intent);
method @MainThread public void stopListening();
method public void triggerModelDownload(@NonNull android.content.Intent);
field public static final String CONFIDENCE_SCORES = "confidence_scores";
field public static final int ERROR_AUDIO = 3; // 0x3
field public static final int ERROR_CANNOT_CHECK_SUPPORT = 14; // 0xe
field public static final int ERROR_CLIENT = 5; // 0x5
field public static final int ERROR_INSUFFICIENT_PERMISSIONS = 9; // 0x9
field public static final int ERROR_LANGUAGE_NOT_SUPPORTED = 12; // 0xc

View File

@@ -20,6 +20,7 @@ import android.os.Bundle;
import android.content.AttributionSource;
import android.content.Intent;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionSupportCallback;
/**
* A Service interface to speech recognition. Call startListening when
@@ -60,4 +61,18 @@ oneway interface IRecognitionService {
* @param listener to receive callbacks, note that this must be non-null
*/
void cancel(in IRecognitionListener listener, boolean isShutdown);
/**
* Checks whether this RecognitionService could {@link #startListening} successfully on the
* given recognizerIntent. For more information see {@link #startListening} and
* {@link RecognizerIntent}.
*/
void checkRecognitionSupport(in Intent recognizerIntent, in IRecognitionSupportCallback listener);
/**
* Requests RecognitionService to download the support for the given recognizerIntent. For more
* information see {@link #checkRecognitionSupport}, {@link #startListening} and
* {@link RecognizerIntent}.
*/
void triggerModelDownload(in Intent recognizerIntent);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2022 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 android.speech;
import android.os.Bundle;
import android.speech.RecognitionSupport;
/**
* Callback for speech recognition support checks, used with RecognitionService.
* This provides the {@link RecognitionSupport} for a given recognition request, callers can use
* it to check whether RecognitionService can fulfill a given recognition request.
* {@hide}
*/
oneway interface IRecognitionSupportCallback {
void onSupportResult(in RecognitionSupport recognitionSupport);
/**
* A network or recognition error occurred.
*
* @param error code is defined in {@link SpeechRecognizer}
*/
void onError(in int error);
}

View File

@@ -37,6 +37,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -90,6 +91,10 @@ public abstract class RecognitionService extends Service {
private static final int MSG_RESET = 4;
private static final int MSG_CHECK_RECOGNITION_SUPPORT = 5;
private static final int MSG_TRIGGER_MODEL_DOWNLOAD = 6;
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -107,6 +112,15 @@ public abstract class RecognitionService extends Service {
case MSG_RESET:
dispatchClearCallback();
break;
case MSG_CHECK_RECOGNITION_SUPPORT:
Pair<Intent, IRecognitionSupportCallback> intentAndListener =
(Pair<Intent, IRecognitionSupportCallback>) msg.obj;
dispatchCheckRecognitionSupport(
intentAndListener.first, intentAndListener.second);
break;
case MSG_TRIGGER_MODEL_DOWNLOAD:
dispatchTriggerModelDownload((Intent) msg.obj);
break;
}
}
};
@@ -179,6 +193,15 @@ public abstract class RecognitionService extends Service {
mStartedDataDelivery = false;
}
private void dispatchCheckRecognitionSupport(
Intent intent, IRecognitionSupportCallback callback) {
RecognitionService.this.onCheckRecognitionSupport(intent, new SupportCallback(callback));
}
private void dispatchTriggerModelDownload(Intent intent) {
RecognitionService.this.triggerModelDownload(intent);
}
private class StartListeningArgs {
public final Intent mIntent;
@@ -238,6 +261,34 @@ public abstract class RecognitionService extends Service {
*/
protected abstract void onStopListening(Callback listener);
/**
* Queries the service on whether it would support a {@link #onStartListening(Intent, Callback)}
* for the same {@code recognizerIntent}.
*
* <p>The service will notify the caller about the level of support or error via
* {@link SupportCallback}.
*
* <p>If the service does not offer the support check it will notify the caller with
* {@link SpeechRecognizer#ERROR_CANNOT_CHECK_SUPPORT}.
*/
public void onCheckRecognitionSupport(
@NonNull Intent recognizerIntent,
@NonNull SupportCallback supportCallback) {
if (DBG) {
Log.i(TAG, String.format("#onSupports [%s]", recognizerIntent));
}
supportCallback.onError(SpeechRecognizer.ERROR_CANNOT_CHECK_SUPPORT);
}
/**
* Requests the download of the recognizer support for {@code recognizerIntent}.
*/
public void triggerModelDownload(@NonNull Intent recognizerIntent) {
if (DBG) {
Log.i(TAG, String.format("#downloadModel [%s]", recognizerIntent));
}
}
@Override
@SuppressLint("MissingNullability")
public Context createContext(@NonNull ContextParams contextParams) {
@@ -410,7 +461,45 @@ public abstract class RecognitionService extends Service {
}
}
/** Binder of the recognition service */
/**
* This class receives callbacks from the speech recognition service and forwards them to the
* user. An instance of this class is passed to the
* {@link RecognitionService#onCheckRecognitionSupport(Intent, SupportCallback)} method. Recognizers may call
* these methods on any thread.
*/
public static class SupportCallback {
private final IRecognitionSupportCallback mCallback;
private SupportCallback(IRecognitionSupportCallback callback) {
this.mCallback = callback;
}
/** The service should call this method to notify the caller about the level of support. */
public void onSupportResult(@NonNull RecognitionSupport recognitionSupport) {
try {
mCallback.onSupportResult(recognitionSupport);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* The service should call this method when an error occurred and can't satisfy the support
* request.
*
* @param errorCode code is defined in {@link SpeechRecognizer}
*/
public void onError(@SpeechRecognizer.RecognitionError int errorCode) {
try {
mCallback.onError(errorCode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/** Binder of the recognition service */
private static final class RecognitionServiceBinder extends IRecognitionService.Stub {
private final WeakReference<RecognitionService> mServiceRef;
@@ -452,6 +541,27 @@ public abstract class RecognitionService extends Service {
}
}
@Override
public void checkRecognitionSupport(
Intent recognizerIntent, IRecognitionSupportCallback callback) {
final RecognitionService service = mServiceRef.get();
if (service != null) {
service.mHandler.sendMessage(
Message.obtain(service.mHandler, MSG_CHECK_RECOGNITION_SUPPORT,
Pair.create(recognizerIntent, callback)));
}
}
@Override
public void triggerModelDownload(Intent recognizerIntent) {
final RecognitionService service = mServiceRef.get();
if (service != null) {
service.mHandler.sendMessage(
Message.obtain(
service.mHandler, MSG_TRIGGER_MODEL_DOWNLOAD, recognizerIntent));
}
}
public void clearReference() {
mServiceRef.clear();
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2022 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 android.speech;
parcelable RecognitionSupport;

View File

@@ -0,0 +1,328 @@
/*
* Copyright (C) 2022 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 android.speech;
import android.annotation.NonNull;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
import java.util.List;
/** Encodes the level of support for a given speech recognition request */
@DataClass(
genConstructor = false,
genBuilder = true,
genEqualsHashCode = true,
genHiddenConstDefs = true,
genParcelable = true,
genToString = true
)
public final class RecognitionSupport implements Parcelable {
/** Support for this request is ready for use on this device for the returned languages. */
@NonNull
private List<String> mInstalledLanguages = null;
/** Support for this request is scheduled for download for the returned languages. */
@NonNull private List<String> mPendingLanguages = null;
/** These languages are supported but need to be downloaded before use. */
@NonNull
private List<String> mSupportedLanguages = null;
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/speech/RecognitionSupport.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
@DataClass.Generated.Member
/* package-private */ RecognitionSupport(
@NonNull List<String> installedLanguages,
@NonNull List<String> pendingLanguages,
@NonNull List<String> supportedLanguages) {
this.mInstalledLanguages = installedLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInstalledLanguages);
this.mPendingLanguages = pendingLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPendingLanguages);
this.mSupportedLanguages = supportedLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSupportedLanguages);
// onConstructed(); // You can define this method to get a callback
}
/**
* Support for this request is ready for use on this device for the returned languages.
*/
@DataClass.Generated.Member
public @NonNull List<String> getInstalledLanguages() {
return mInstalledLanguages;
}
/**
* Support for this request is scheduled for download for the returned languages.
*/
@DataClass.Generated.Member
public @NonNull List<String> getPendingLanguages() {
return mPendingLanguages;
}
/**
* These languages are supported but need to be downloaded before use.
*/
@DataClass.Generated.Member
public @NonNull List<String> getSupportedLanguages() {
return mSupportedLanguages;
}
@Override
@DataClass.Generated.Member
public String toString() {
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
return "RecognitionSupport { " +
"installedLanguages = " + mInstalledLanguages + ", " +
"pendingLanguages = " + mPendingLanguages + ", " +
"supportedLanguages = " + mSupportedLanguages +
" }";
}
@Override
@DataClass.Generated.Member
public boolean equals(@android.annotation.Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(RecognitionSupport other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
RecognitionSupport that = (RecognitionSupport) o;
//noinspection PointlessBooleanExpression
return true
&& java.util.Objects.equals(mInstalledLanguages, that.mInstalledLanguages)
&& java.util.Objects.equals(mPendingLanguages, that.mPendingLanguages)
&& java.util.Objects.equals(mSupportedLanguages, that.mSupportedLanguages);
}
@Override
@DataClass.Generated.Member
public int hashCode() {
// You can override field hashCode logic by defining methods like:
// int fieldNameHashCode() { ... }
int _hash = 1;
_hash = 31 * _hash + java.util.Objects.hashCode(mInstalledLanguages);
_hash = 31 * _hash + java.util.Objects.hashCode(mPendingLanguages);
_hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLanguages);
return _hash;
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeStringList(mInstalledLanguages);
dest.writeStringList(mPendingLanguages);
dest.writeStringList(mSupportedLanguages);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ RecognitionSupport(@NonNull android.os.Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
List<String> installedLanguages = new java.util.ArrayList<>();
in.readStringList(installedLanguages);
List<String> pendingLanguages = new java.util.ArrayList<>();
in.readStringList(pendingLanguages);
List<String> supportedLanguages = new java.util.ArrayList<>();
in.readStringList(supportedLanguages);
this.mInstalledLanguages = installedLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInstalledLanguages);
this.mPendingLanguages = pendingLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPendingLanguages);
this.mSupportedLanguages = supportedLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSupportedLanguages);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<RecognitionSupport> CREATOR
= new Parcelable.Creator<RecognitionSupport>() {
@Override
public RecognitionSupport[] newArray(int size) {
return new RecognitionSupport[size];
}
@Override
public RecognitionSupport createFromParcel(@NonNull android.os.Parcel in) {
return new RecognitionSupport(in);
}
};
/**
* A builder for {@link RecognitionSupport}
*/
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
public static final class Builder {
private @NonNull List<String> mInstalledLanguages;
private @NonNull List<String> mPendingLanguages;
private @NonNull List<String> mSupportedLanguages;
private long mBuilderFieldsSet = 0L;
public Builder() {
}
/**
* Support for this request is ready for use on this device for the returned languages.
*/
@DataClass.Generated.Member
public @NonNull Builder setInstalledLanguages(@NonNull List<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
mInstalledLanguages = value;
return this;
}
/** @see #setInstalledLanguages */
@DataClass.Generated.Member
public @NonNull Builder addInstalledLanguages(@NonNull String value) {
// You can refine this method's name by providing item's singular name, e.g.:
// @DataClass.PluralOf("item")) mItems = ...
if (mInstalledLanguages == null) setInstalledLanguages(new java.util.ArrayList<>());
mInstalledLanguages.add(value);
return this;
}
/**
* Support for this request is scheduled for download for the returned languages.
*/
@DataClass.Generated.Member
public @NonNull Builder setPendingLanguages(@NonNull List<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
mPendingLanguages = value;
return this;
}
/** @see #setPendingLanguages */
@DataClass.Generated.Member
public @NonNull Builder addPendingLanguages(@NonNull String value) {
// You can refine this method's name by providing item's singular name, e.g.:
// @DataClass.PluralOf("item")) mItems = ...
if (mPendingLanguages == null) setPendingLanguages(new java.util.ArrayList<>());
mPendingLanguages.add(value);
return this;
}
/**
* These languages are supported but need to be downloaded before use.
*/
@DataClass.Generated.Member
public @NonNull Builder setSupportedLanguages(@NonNull List<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
mSupportedLanguages = value;
return this;
}
/** @see #setSupportedLanguages */
@DataClass.Generated.Member
public @NonNull Builder addSupportedLanguages(@NonNull String value) {
// You can refine this method's name by providing item's singular name, e.g.:
// @DataClass.PluralOf("item")) mItems = ...
if (mSupportedLanguages == null) setSupportedLanguages(new java.util.ArrayList<>());
mSupportedLanguages.add(value);
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull RecognitionSupport build() {
checkNotUsed();
mBuilderFieldsSet |= 0x8; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mInstalledLanguages = null;
}
if ((mBuilderFieldsSet & 0x2) == 0) {
mPendingLanguages = null;
}
if ((mBuilderFieldsSet & 0x4) == 0) {
mSupportedLanguages = null;
}
RecognitionSupport o = new RecognitionSupport(
mInstalledLanguages,
mPendingLanguages,
mSupportedLanguages);
return o;
}
private void checkNotUsed() {
if ((mBuilderFieldsSet & 0x8) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
}
}
@DataClass.Generated(
time = 1639158640137L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/speech/RecognitionSupport.java",
inputSignatures = "private @android.annotation.NonNull java.util.List<java.lang.String> mInstalledLanguages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mPendingLanguages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mSupportedLanguages\nclass RecognitionSupport extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022 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 android.speech;
import android.annotation.NonNull;
/**
* Used for receiving notifications from the SpeechRecognizer about the device support status for
* the given recognition request.
*/
public interface RecognitionSupportCallback {
/** Notifies the caller about the support for the given request. */
void onSupportResult(@NonNull RecognitionSupport recognitionSupport);
/** Notifies the caller about an error during the recognition support request */
void onError(@SpeechRecognizer.RecognitionError int error);
}

View File

@@ -38,6 +38,7 @@ import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
@@ -46,6 +47,7 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -112,7 +114,8 @@ public class SpeechRecognizer {
ERROR_TOO_MANY_REQUESTS,
ERROR_SERVER_DISCONNECTED,
ERROR_LANGUAGE_NOT_SUPPORTED,
ERROR_LANGUAGE_UNAVAILABLE
ERROR_LANGUAGE_UNAVAILABLE,
ERROR_CANNOT_CHECK_SUPPORT,
})
public @interface RecognitionError {}
@@ -155,19 +158,24 @@ public class SpeechRecognizer {
/** Requested language is supported, but not available currently (e.g. not downloaded yet). */
public static final int ERROR_LANGUAGE_UNAVAILABLE = 13;
/** The service does not allow to check for support. */
public static final int ERROR_CANNOT_CHECK_SUPPORT = 14;
/** action codes */
private static final int MSG_START = 1;
private static final int MSG_STOP = 2;
private static final int MSG_CANCEL = 3;
private static final int MSG_CHANGE_LISTENER = 4;
private static final int MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT = 5;
private static final int MSG_CHECK_RECOGNITION_SUPPORT = 6;
private static final int MSG_TRIGGER_MODEL_DOWNLOAD = 7;
/** The actual RecognitionService endpoint */
private IRecognitionService mService;
/** Context with which the manager was created */
private final Context mContext;
/** Component to direct service intent to */
private final ComponentName mServiceComponent;
@@ -197,6 +205,15 @@ public class SpeechRecognizer {
case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT:
handleSetTemporaryComponent((ComponentName) msg.obj);
break;
case MSG_CHECK_RECOGNITION_SUPPORT:
Pair<Intent, RecognitionSupportCallback> intentAndListener =
(Pair<Intent, RecognitionSupportCallback>) msg.obj;
handleCheckRecognitionSupport(
intentAndListener.first, intentAndListener.second);
break;
case MSG_TRIGGER_MODEL_DOWNLOAD:
handleTriggerModelDownload((Intent) msg.obj);
break;
}
}
};
@@ -208,7 +225,7 @@ public class SpeechRecognizer {
private final Queue<Message> mPendingTasks = new LinkedBlockingQueue<>();
/** The Listener that will receive all the callbacks */
private final InternalListener mListener = new InternalListener();
private final InternalRecognitionListener mListener = new InternalRecognitionListener();
private final IBinder mClientToken = new Binder();
@@ -464,6 +481,38 @@ public class SpeechRecognizer {
putMessage(Message.obtain(mHandler, MSG_CANCEL));
}
/**
* Checks whether {@code recognizerIntent} is supported by
* {@link SpeechRecognizer#startListening(Intent)}.
*
* @param recognizerIntent contains parameters for the recognition to be performed. The intent
* may also contain optional extras. See {@link RecognizerIntent} for the list of
* supported extras, any unlisted extra might be ignored.
* @param supportListener the listener on which to receive the support query results.
*/
public void checkRecognitionSupport(
@NonNull Intent recognizerIntent,
@NonNull RecognitionSupportCallback supportListener) {
Objects.requireNonNull(recognizerIntent, "intent must not be null");
Objects.requireNonNull(supportListener, "listener must not be null");
putMessage(Message.obtain(mHandler, MSG_CHECK_RECOGNITION_SUPPORT,
Pair.create(recognizerIntent, supportListener)));
}
/**
* Attempts to download the support for the given {@code recognizerIntent}. This might trigger
* user interaction to approve the download. Callers can verify the status of the request via
* {@link #checkRecognitionSupport(Intent, RecognitionSupportCallback)}.
*
* @param recognizerIntent contains parameters for the recognition to be performed. The intent
* may also contain optional extras, see {@link RecognizerIntent}.
*/
public void triggerModelDownload(@NonNull Intent recognizerIntent) {
Objects.requireNonNull(recognizerIntent, "intent must not be null");
putMessage(Message.obtain(mHandler, MSG_TRIGGER_MODEL_DOWNLOAD));
}
/**
* Sets a temporary component to power on-device speech recognizer.
*
@@ -503,7 +552,7 @@ public class SpeechRecognizer {
}
try {
mService.startListening(recognizerIntent, mListener, mContext.getAttributionSource());
if (DBG) Log.d(TAG, "service start listening command succeded");
if (DBG) Log.d(TAG, "service start listening command succeeded");
} catch (final RemoteException e) {
Log.e(TAG, "startListening() failed", e);
mListener.onError(ERROR_CLIENT);
@@ -517,7 +566,7 @@ public class SpeechRecognizer {
}
try {
mService.stopListening(mListener);
if (DBG) Log.d(TAG, "service stop listening command succeded");
if (DBG) Log.d(TAG, "service stop listening command succeeded");
} catch (final RemoteException e) {
Log.e(TAG, "stopListening() failed", e);
mListener.onError(ERROR_CLIENT);
@@ -531,7 +580,7 @@ public class SpeechRecognizer {
}
try {
mService.cancel(mListener, /*isShutdown*/ false);
if (DBG) Log.d(TAG, "service cancel command succeded");
if (DBG) Log.d(TAG, "service cancel command succeeded");
} catch (final RemoteException e) {
Log.e(TAG, "cancel() failed", e);
mListener.onError(ERROR_CLIENT);
@@ -554,6 +603,35 @@ public class SpeechRecognizer {
}
}
private void handleCheckRecognitionSupport(
Intent recognizerIntent, RecognitionSupportCallback recognitionSupportCallback) {
if (!maybeInitializeManagerService()) {
return;
}
try {
mService.checkRecognitionSupport(
recognizerIntent,
new InternalSupportCallback(recognitionSupportCallback));
if (DBG) Log.d(TAG, "service support command succeeded");
} catch (final RemoteException e) {
Log.e(TAG, "checkRecognitionSupport() failed", e);
mListener.onError(ERROR_CLIENT);
}
}
private void handleTriggerModelDownload(Intent recognizerIntent) {
if (!maybeInitializeManagerService()) {
return;
}
try {
mService.triggerModelDownload(recognizerIntent);
if (DBG) Log.d(TAG, "service download support command succeeded");
} catch (final RemoteException e) {
Log.e(TAG, "downloadModel() failed", e);
mListener.onError(ERROR_CLIENT);
}
}
private boolean checkOpenConnection() {
if (mService != null) {
return true;
@@ -626,7 +704,7 @@ public class SpeechRecognizer {
}
}
private boolean maybeInitializeManagerService() {
private synchronized boolean maybeInitializeManagerService() {
if (mManagerService != null) {
return true;
}
@@ -678,7 +756,7 @@ public class SpeechRecognizer {
* Internal wrapper of IRecognitionListener which will propagate the results to
* RecognitionListener
*/
private static class InternalListener extends IRecognitionListener.Stub {
private static class InternalRecognitionListener extends IRecognitionListener.Stub {
private RecognitionListener mInternalListener;
private static final int MSG_BEGINNING_OF_SPEECH = 1;
@@ -766,4 +844,42 @@ public class SpeechRecognizer {
.sendToTarget();
}
}
private static class InternalSupportCallback extends IRecognitionSupportCallback.Stub {
private final RecognitionSupportCallback mCallback;
private static final int MSG_SUPPORT_RESULT = 1;
private static final int MSG_ERROR = 2;
private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (mCallback == null) {
return;
}
switch (msg.what) {
case MSG_SUPPORT_RESULT:
mCallback.onSupportResult((RecognitionSupport) msg.obj);
break;
case MSG_ERROR:
mCallback.onError((Integer) msg.obj);
break;
}
}
};
private InternalSupportCallback(RecognitionSupportCallback callback) {
this.mCallback = callback;
}
@Override
public void onSupportResult(RecognitionSupport recognitionSupport) throws RemoteException {
Message.obtain(mInternalHandler, MSG_SUPPORT_RESULT, recognitionSupport).sendToTarget();
}
@Override
public void onError(int errorCode) throws RemoteException {
Message.obtain(mInternalHandler, MSG_ERROR, errorCode).sendToTarget();
}
}
}

View File

@@ -28,6 +28,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionService;
import android.speech.IRecognitionSupportCallback;
import android.speech.RecognitionService;
import android.speech.SpeechRecognizer;
import android.util.Log;
@@ -211,6 +212,30 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
}
}
void checkRecognitionSupport(
Intent recognizerIntent,
IRecognitionSupportCallback callback) {
if (!mConnected) {
try {
callback.onError(SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report the connection broke to the caller.", e);
e.printStackTrace();
}
return;
}
run(service -> service.checkRecognitionSupport(recognizerIntent, callback));
}
void triggerModelDownload(Intent recognizerIntent) {
if (!mConnected) {
Slog.e(TAG, "#downloadModel failed due to connection.");
return;
}
run(service -> service.triggerModelDownload(recognizerIntent));
}
void shutdown() {
synchronized (mLock) {
if (this.mListener == null) {

View File

@@ -33,6 +33,7 @@ import android.permission.PermissionManager;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionService;
import android.speech.IRecognitionServiceManagerCallback;
import android.speech.IRecognitionSupportCallback;
import android.speech.RecognitionService;
import android.speech.SpeechRecognizer;
import android.util.Slog;
@@ -169,6 +170,18 @@ final class SpeechRecognitionManagerServiceImpl extends
clientToken.unlinkToDeath(deathRecipient, 0);
}
}
@Override
public void checkRecognitionSupport(
Intent recognizerIntent,
IRecognitionSupportCallback callback) {
service.checkRecognitionSupport(recognizerIntent, callback);
}
@Override
public void triggerModelDownload(Intent recognizerIntent) {
service.triggerModelDownload(recognizerIntent);
}
});
} catch (RemoteException e) {
Slog.e(TAG, "Error creating a speech recognition session", e);