Merge "Add System speech recognition service." into sc-dev

This commit is contained in:
Sergey Volnov
2021-01-28 16:55:27 +00:00
committed by Android (Google) Code Review
15 changed files with 566 additions and 35 deletions

View File

@@ -38576,6 +38576,7 @@ package android.speech {
public class SpeechRecognizer {
method public void cancel();
method @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context);
method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context);
method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName);
method public void destroy();

View File

@@ -64,7 +64,6 @@ import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.StatFs;
import android.os.StrictMode;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -3575,6 +3574,7 @@ public abstract class Context {
LIGHTS_SERVICE,
//@hide: PEOPLE_SERVICE,
//@hide: DEVICE_STATE_SERVICE,
//@hide: SPEECH_RECOGNITION_SERVICE,
UWB_SERVICE,
MEDIA_METRICS_SERVICE,
})
@@ -5409,6 +5409,14 @@ public abstract class Context {
*/
public static final String MEDIA_METRICS_SERVICE = "media_metrics";
/**
* Use with {@link #getSystemService(String)} to access system speech recognition service.
*
* @see #getSystemService(String)
* @hide
*/
public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition";
/**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
@@ -6469,7 +6477,7 @@ public abstract class Context {
* {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or
* {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI
* contexts throws {@link android.os.strictmode.Violation} if
* {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
* {@link android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
* <p>
* Examples of UI contexts are
* an {@link android.app.Activity Activity}, a context created from
@@ -6479,7 +6487,7 @@ public abstract class Context {
*
* @see #getDisplay()
* @see #getSystemService(String)
* @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
* @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
*/
public static boolean isUiContext(@NonNull Context context) {
return context.isUiContext();

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2021 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.speech.IRecognitionServiceManagerCallback;
/**
* Binder service allowing speech recognition proxied by the system.
*
* {@hide}
*/
interface IRecognitionServiceManager {
void createSession(in IRecognitionServiceManagerCallback callback);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2021 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.speech.IRecognitionService;
/**
* Callback for the service allowing speech recognition proxied by the system.
*
* {@hide}
*/
oneway interface IRecognitionServiceManagerCallback {
void onSuccess(in IRecognitionService service);
void onError();
}

View File

@@ -28,6 +28,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -50,7 +51,9 @@ public abstract class RecognitionService extends Service {
/**
* Name under which a RecognitionService component publishes information about itself.
* This meta-data should reference an XML resource containing a
* <code>&lt;{@link android.R.styleable#RecognitionService recognition-service}&gt;</code> tag.
* <code>&lt;{@link android.R.styleable#RecognitionService recognition-service}&gt;</code> or
* <code>&lt;{@link android.R.styleable#RecognitionService on-device-recognition-service}
* &gt;</code> tag.
*/
public static final String SERVICE_META_DATA = "android.speech";
@@ -182,6 +185,13 @@ public abstract class RecognitionService extends Service {
private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery,
@NonNull String packageName, @Nullable String featureId) {
if (DBG) Log.d(TAG, "checkPermissions");
final int callingUid = Binder.getCallingUid();
if (callingUid == Process.SYSTEM_UID) {
// Assuming system has verified permissions of the caller.
return true;
}
if (forDataDelivery) {
if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
android.Manifest.permission.RECORD_AUDIO, packageName, featureId,
@@ -342,6 +352,7 @@ public abstract class RecognitionService extends Service {
* Return the Linux uid assigned to the process that sent you the current transaction that
* is being processed. This is obtained from {@link Binder#getCallingUid()}.
*/
// TODO(b/176578753): need to make sure this is fixed when proxied through system.
public int getCallingUid() {
return mCallingUid;
}

View File

@@ -16,6 +16,7 @@
package android.speech;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -27,6 +28,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -38,8 +40,9 @@ import java.util.Queue;
/**
* This class provides access to the speech recognition service. This service allows access to the
* speech recognizer. Do not instantiate this class directly, instead, call
* {@link SpeechRecognizer#createSpeechRecognizer(Context)}. This class's methods must be
* invoked only from the main application thread.
* {@link SpeechRecognizer#createSpeechRecognizer(Context)}, or
* {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be
* invoked only from the main application thread.
*
* <p>The implementation of this API is likely to stream audio to remote servers to perform speech
* recognition. As such this API is not intended to be used for continuous recognition, which would
@@ -122,8 +125,13 @@ public class SpeechRecognizer {
/** Component to direct service intent to */
private final ComponentName mServiceComponent;
/** Whether to use on-device speech recognizer. */
private final boolean mOnDevice;
private IRecognitionServiceManager mManagerService;
/** Handler that will execute the main tasks */
private Handler mHandler = new Handler() {
private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -159,6 +167,17 @@ public class SpeechRecognizer {
private SpeechRecognizer(final Context context, final ComponentName serviceComponent) {
mContext = context;
mServiceComponent = serviceComponent;
mOnDevice = false;
}
/**
* The right way to create a {@code SpeechRecognizer} is by using
* {@link #createOnDeviceSpeechRecognizer} static factory method
*/
private SpeechRecognizer(final Context context, boolean onDevice) {
mContext = context;
mServiceComponent = null;
mOnDevice = onDevice;
}
/**
@@ -194,6 +213,7 @@ public class SpeechRecognizer {
* @return {@code true} if recognition is available, {@code false} otherwise
*/
public static boolean isRecognitionAvailable(final Context context) {
// TODO(b/176578753): make sure this works well with system speech recognizers.
final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
new Intent(RecognitionService.SERVICE_INTERFACE), 0);
return list != null && list.size() != 0;
@@ -231,12 +251,31 @@ public class SpeechRecognizer {
public static SpeechRecognizer createSpeechRecognizer(final Context context,
final ComponentName serviceComponent) {
if (context == null) {
throw new IllegalArgumentException("Context cannot be null)");
throw new IllegalArgumentException("Context cannot be null");
}
checkIsCalledFromMainThread();
return new SpeechRecognizer(context, serviceComponent);
}
/**
* Factory method to create a new {@code SpeechRecognizer}.
*
* <p>Please note that {@link #setRecognitionListener(RecognitionListener)} should be called
* before dispatching any command to the created {@code SpeechRecognizer}, otherwise no
* notifications will be received.
*
* @param context in which to create {@code SpeechRecognizer}
* @return a new on-device {@code SpeechRecognizer}.
*/
@NonNull
public static SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull final Context context) {
if (context == null) {
throw new IllegalArgumentException("Context cannot be null");
}
checkIsCalledFromMainThread();
return new SpeechRecognizer(context, /* onDevice */ true);
}
/**
* Sets the listener that will receive all the callbacks. The previous unfinished commands will
* be executed with the old listener, while any following command will be executed with the new
@@ -265,36 +304,74 @@ public class SpeechRecognizer {
}
checkIsCalledFromMainThread();
if (mConnection == null) { // first time connection
mConnection = new Connection();
Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
if (mServiceComponent == null) {
String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE);
if (TextUtils.isEmpty(serviceComponent)) {
Log.e(TAG, "no selected voice recognition service");
mListener.onError(ERROR_CLIENT);
return;
}
serviceIntent.setComponent(ComponentName.unflattenFromString(serviceComponent));
// TODO(b/176578753): both flows should go through system service.
if (mOnDevice) {
connectToSystemService();
} else {
serviceIntent.setComponent(mServiceComponent);
}
if (!mContext.bindService(serviceIntent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
Log.e(TAG, "bind to recognition service failed");
mConnection = null;
mService = null;
mListener.onError(ERROR_CLIENT);
return;
connectToService();
}
}
putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
}
private void connectToSystemService() {
mManagerService = IRecognitionServiceManager.Stub.asInterface(
ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
if (mManagerService == null) {
mListener.onError(ERROR_CLIENT);
return;
}
try {
// TODO(b/176578753): this has to supply information on whether to use on-device impl.
mManagerService.createSession(new IRecognitionServiceManagerCallback.Stub(){
@Override
public void onSuccess(IRecognitionService service) throws RemoteException {
mService = service;
}
@Override
public void onError() throws RemoteException {
Log.e(TAG, "Bind to system recognition service failed");
mListener.onError(ERROR_CLIENT);
}
});
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
private void connectToService() {
mConnection = new Connection();
Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
if (mServiceComponent == null) {
String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE);
if (TextUtils.isEmpty(serviceComponent)) {
Log.e(TAG, "no selected voice recognition service");
mListener.onError(ERROR_CLIENT);
return;
}
serviceIntent.setComponent(
ComponentName.unflattenFromString(serviceComponent));
} else {
serviceIntent.setComponent(mServiceComponent);
}
if (!mContext.bindService(serviceIntent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
Log.e(TAG, "bind to recognition service failed");
mConnection = null;
mService = null;
mListener.onError(ERROR_CLIENT);
return;
}
}
/**
* Stops listening for speech. Speech captured so far will be recognized as if the user had
* stopped speaking at this point. Note that in the default case, this does not need to be
@@ -378,7 +455,7 @@ public class SpeechRecognizer {
mListener.onError(ERROR_CLIENT);
}
}
private boolean checkOpenConnection() {
if (mService != null) {
return true;
@@ -433,7 +510,7 @@ public class SpeechRecognizer {
private final static int MSG_RMS_CHANGED = 8;
private final static int MSG_ON_EVENT = 9;
private final Handler mInternalHandler = new Handler() {
private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (mInternalListener == null) {

View File

@@ -8287,6 +8287,23 @@
<attr name="maxLongVersionCode" format="string" />
</declare-styleable>
<!-- =============================== -->
<!-- System Speech Recognition attributes -->
<!-- =============================== -->
<eat-comment />
<!-- Use <code>on-device-recognition-service</code> as the root tag of the XML resource that
describes a {@link android.service.speech.RecognitionService}, which is referenced
from its {@link android.service.speech.RecognitionService#SERVICE_META_DATA} meta-data
entry.
@hide @SystemApi
-->
<declare-styleable name="OnDeviceRecognitionService">
<!-- Fully qualified class name of an activity that allows the user to modify
the settings for this service. -->
<attr name="settingsActivity" />
</declare-styleable>
<!-- =============================== -->
<!-- Content Capture attributes -->
<!-- =============================== -->

View File

@@ -3800,6 +3800,12 @@
-->
<string name="config_defaultSearchUiService" translatable="false"></string>
<!-- The package name for the system's speech recognition service.
This service must be trusted, as it can be activated without explicit consent of the user.
Example: "com.android.speech/.RecognitionService"
-->
<string name="config_defaultOnDeviceSpeechRecognitionService" translatable="false"></string>
<string name="config_defaultMusicRecognitionService" translatable="false"></string>
<!-- The package name for the default retail demo app.

View File

@@ -3489,6 +3489,7 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="notification_channel_accessibility_magnification" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
<java-symbol type="string" name="config_defaultWellbeingPackage" />
<java-symbol type="string" name="config_defaultContentCaptureService" />

View File

@@ -28,6 +28,7 @@ filegroup {
":services.profcollect-sources",
":services.restrictions-sources",
":services.searchui-sources",
":services.speech-sources",
":services.startop.iorap-sources",
":services.systemcaptions-sources",
":services.translation-sources",
@@ -75,6 +76,7 @@ java_library {
"services.profcollect",
"services.restrictions",
"services.searchui",
"services.speech",
"services.startop",
"services.systemcaptions",
"services.translation",

View File

@@ -0,0 +1,13 @@
filegroup {
name: "services.speech-sources",
srcs: ["java/**/*.java"],
path: "java",
visibility: ["//frameworks/base/services"],
}
java_library_static {
name: "services.speech",
defaults: ["platform_service_defaults"],
srcs: [":services.speech-sources"],
libs: ["services.core"],
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2021 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.server.speech;
import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionService;
import android.speech.RecognitionService;
import android.util.Slog;
import com.android.internal.infra.ServiceConnector;
final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecognitionService> {
private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName();
private static final boolean DEBUG = true;
RemoteSpeechRecognitionService(Context context, ComponentName serviceName, int userId) {
super(context,
new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName),
Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE
| Context.BIND_INCLUDE_CAPABILITIES
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
userId,
IRecognitionService.Stub::asInterface);
if (DEBUG) {
Slog.i(TAG, "Bound to recognition service at: " + serviceName.flattenToString());
}
}
void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName,
String featureId) throws RemoteException {
if (DEBUG) {
Slog.i(TAG, "#startListening for package: " + packageName + ", feature=" + featureId);
}
run(service -> service.startListening(recognizerIntent, listener, packageName, featureId));
}
void stopListening(IRecognitionListener listener, String packageName, String featureId)
throws RemoteException {
if (DEBUG) {
Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId);
}
run(service -> service.stopListening(listener, packageName, featureId));
}
void cancel(IRecognitionListener listener, String packageName, String featureId)
throws RemoteException {
if (DEBUG) {
Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId);
}
run(service -> service.cancel(listener, packageName, featureId));
}
@Override // from ServiceConnector.Impl
protected void onServiceConnectionStatusChanged(
IRecognitionService service, boolean connected) {
if (!DEBUG) {
return;
}
if (connected) {
Slog.i(TAG, "Connected to ASR service");
} else {
Slog.w(TAG, "Disconnected from ASR service");
}
}
@Override // from AbstractRemoteService
protected long getAutoDisconnectTimeoutMs() {
return PERMANENT_BOUND_TIMEOUT_MS;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2021 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.server.speech;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.UserHandle;
import android.speech.IRecognitionServiceManager;
import android.speech.IRecognitionServiceManagerCallback;
import com.android.internal.R;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
/**
* System service implementation for Speech Recognizer.
*
* <p>This service uses RemoteSpeechRecognitionService to bind to a default implementation of
* ISpeechRecognition. It relays all the requests from the client to the default system impl of
* ISpeechRecognition service (denoted by {@code
* R.string.config_defaultOnDeviceSpeechRecognitionService}).
*/
public final class SpeechRecognitionManagerService extends
AbstractMasterSystemService<SpeechRecognitionManagerService,
SpeechRecognitionManagerServiceImpl> {
private static final String TAG = SpeechRecognitionManagerService.class.getSimpleName();
public SpeechRecognitionManagerService(@NonNull Context context) {
super(context,
new FrameworkResourcesServiceNameResolver(
context,
R.string.config_defaultOnDeviceSpeechRecognitionService),
/*disallowProperty=*/ null);
}
@Override // from SystemService
public void onStart() {
SpeechRecognitionManagerServiceStub serviceStub = new SpeechRecognitionManagerServiceStub();
publishBinderService(Context.SPEECH_RECOGNITION_SERVICE, serviceStub);
}
@Override
protected SpeechRecognitionManagerServiceImpl newServiceLocked(
@UserIdInt int resolvedUserId, boolean disabled) {
return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled);
}
final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub {
@Override
public void createSession(IRecognitionServiceManagerCallback callback) {
int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
SpeechRecognitionManagerServiceImpl service = getServiceForUserLocked(userId);
service.createSessionLocked(callback);
}
}
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2021 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.server.speech;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.RemoteException;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionService;
import android.speech.IRecognitionServiceManagerCallback;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;
final class SpeechRecognitionManagerServiceImpl extends
AbstractPerUserSystemService<SpeechRecognitionManagerServiceImpl,
SpeechRecognitionManagerService> {
private static final String TAG = SpeechRecognitionManagerServiceImpl.class.getSimpleName();
@GuardedBy("mLock")
@Nullable
private RemoteSpeechRecognitionService mRemoteService;
SpeechRecognitionManagerServiceImpl(
@NonNull SpeechRecognitionManagerService master,
@NonNull Object lock, @UserIdInt int userId, boolean disabled) {
super(master, lock, userId);
updateRemoteServiceLocked();
}
@GuardedBy("mLock")
@Override // from PerUserSystemService
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws PackageManager.NameNotFoundException {
try {
return AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
PackageManager.GET_META_DATA, mUserId);
} catch (RemoteException e) {
throw new PackageManager.NameNotFoundException(
"Could not get service for " + serviceComponent);
}
}
@GuardedBy("mLock")
@Override // from PerUserSystemService
protected boolean updateLocked(boolean disabled) {
final boolean enabledChanged = super.updateLocked(disabled);
updateRemoteServiceLocked();
return enabledChanged;
}
/**
* Updates the reference to the remote service.
*/
@GuardedBy("mLock")
private void updateRemoteServiceLocked() {
if (mRemoteService != null) {
if (mMaster.debug) {
Slog.d(TAG, "updateRemoteService(): destroying old remote service");
}
mRemoteService.unbind();
mRemoteService = null;
}
}
void createSessionLocked(IRecognitionServiceManagerCallback callback) {
// TODO(b/176578753): check clients have record audio permission.
// TODO(b/176578753): verify caller package is the one supplied
RemoteSpeechRecognitionService service = ensureRemoteServiceLocked();
if (service == null) {
tryRespondWithError(callback);
return;
}
service.connect().thenAccept(binderService -> {
if (binderService != null) {
try {
callback.onSuccess(new IRecognitionService.Stub() {
@Override
public void startListening(Intent recognizerIntent,
IRecognitionListener listener,
String packageName, String featureId) throws RemoteException {
service.startListening(
recognizerIntent, listener, packageName, featureId);
}
@Override
public void stopListening(IRecognitionListener listener,
String packageName,
String featureId) throws RemoteException {
service.stopListening(listener, packageName, featureId);
}
@Override
public void cancel(IRecognitionListener listener,
String packageName,
String featureId) throws RemoteException {
service.cancel(listener, packageName, featureId);
}
});
} catch (RemoteException e) {
Slog.e(TAG, "Error creating a speech recognition session", e);
tryRespondWithError(callback);
}
} else {
tryRespondWithError(callback);
}
});
}
@GuardedBy("mLock")
@Nullable
private RemoteSpeechRecognitionService ensureRemoteServiceLocked() {
if (mRemoteService == null) {
final String serviceName = getComponentNameLocked();
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
}
return null;
}
final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
mRemoteService =
new RemoteSpeechRecognitionService(getContext(), serviceComponent, mUserId);
}
return mRemoteService;
}
private static void tryRespondWithError(IRecognitionServiceManagerCallback callback) {
try {
callback.onError();
} catch (RemoteException e) {
Slog.w(TAG, "Failed to respond with error");
}
}
}

View File

@@ -333,6 +333,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
private static final String ADB_SERVICE_CLASS =
"com.android.server.adb.AdbService$Lifecycle";
private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.speech.SpeechRecognitionManagerService";
private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
"com.android.server.appprediction.AppPredictionManagerService";
private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
@@ -1629,12 +1631,21 @@ public final class SystemServer implements Dumpable {
"MusicRecognitionManagerService not defined by OEM or disabled by flag");
}
startContentCaptureService(context, t);
startAttentionService(context, t);
startRotationResolverService(context, t);
startSystemCaptionsManagerService(context, t);
// System Speech Recognition Service
if (deviceHasConfigString(context,
R.string.config_defaultOnDeviceSpeechRecognitionService)) {
t.traceBegin("StartSpeechRecognitionManagerService");
mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
t.traceEnd();
} else {
Slog.d(TAG, "System speech recognition is not defined by OEM");
}
// App prediction manager service
if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) {
t.traceBegin("StartAppPredictionService");