Merge "Revert "Add a system TextToSpeech implementation that initiates ..."" into sc-dev
This commit is contained in:
@@ -4582,14 +4582,6 @@ public abstract class Context {
|
||||
*/
|
||||
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
|
||||
|
||||
/**
|
||||
* Official published name of the (internal) text to speech manager service.
|
||||
*
|
||||
* @hide
|
||||
* @see #getSystemService(String)
|
||||
*/
|
||||
public static final String TEXT_TO_SPEECH_MANAGER_SERVICE = "texttospeech";
|
||||
|
||||
/**
|
||||
* Official published name of the content capture service.
|
||||
*
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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.tts;
|
||||
|
||||
import android.speech.tts.ITextToSpeechSessionCallback;
|
||||
|
||||
/**
|
||||
* TextToSpeechManagerService interface. Allows opening {@link TextToSpeech} session with the
|
||||
* specified provider proxied by the system service.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITextToSpeechManager {
|
||||
void createSession(in String engine, in ITextToSpeechSessionCallback managerCallback);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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.tts;
|
||||
|
||||
/**
|
||||
* TextToSpeech session interface. Allows to control remote TTS service session once connected.
|
||||
*
|
||||
* @see ITextToSpeechManager
|
||||
* @see ITextToSpeechSessionCallback
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface ITextToSpeechSession {
|
||||
|
||||
/**
|
||||
* Disconnects the client from the TTS provider.
|
||||
*/
|
||||
void disconnect();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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.tts;
|
||||
import android.speech.tts.ITextToSpeechSession;
|
||||
|
||||
/**
|
||||
* Callback interface for a session created by {@link ITextToSpeechManager} API.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITextToSpeechSessionCallback {
|
||||
|
||||
void onConnected(in ITextToSpeechSession session, in IBinder serviceBinder);
|
||||
|
||||
void onDisconnected();
|
||||
|
||||
void onError(in String errorInfo);
|
||||
}
|
||||
@@ -35,7 +35,6 @@ import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -52,7 +51,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -697,8 +695,6 @@ public class TextToSpeech {
|
||||
public static final String KEY_FEATURE_NETWORK_RETRIES_COUNT = "networkRetriesCount";
|
||||
}
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final Context mContext;
|
||||
@UnsupportedAppUsage
|
||||
private Connection mConnectingServiceConnection;
|
||||
@@ -720,9 +716,6 @@ public class TextToSpeech {
|
||||
private final Map<CharSequence, Uri> mUtterances;
|
||||
private final Bundle mParams = new Bundle();
|
||||
private final TtsEngines mEnginesHelper;
|
||||
private final boolean mIsSystem;
|
||||
@Nullable private final Executor mInitExecutor;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private volatile String mCurrentEngine = null;
|
||||
|
||||
@@ -765,21 +758,8 @@ public class TextToSpeech {
|
||||
*/
|
||||
public TextToSpeech(Context context, OnInitListener listener, String engine,
|
||||
String packageName, boolean useFallback) {
|
||||
this(context, /* initExecutor= */ null, listener, engine, packageName,
|
||||
useFallback, /* isSystem= */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally to instantiate TextToSpeech objects.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
private TextToSpeech(Context context, @Nullable Executor initExecutor,
|
||||
OnInitListener initListener, String engine, String packageName, boolean useFallback,
|
||||
boolean isSystem) {
|
||||
mContext = context;
|
||||
mInitExecutor = initExecutor;
|
||||
mInitListener = initListener;
|
||||
mInitListener = listener;
|
||||
mRequestedEngine = engine;
|
||||
mUseFallback = useFallback;
|
||||
|
||||
@@ -788,9 +768,6 @@ public class TextToSpeech {
|
||||
mUtteranceProgressListener = null;
|
||||
|
||||
mEnginesHelper = new TtsEngines(mContext);
|
||||
|
||||
mIsSystem = isSystem;
|
||||
|
||||
initTts();
|
||||
}
|
||||
|
||||
@@ -865,14 +842,10 @@ public class TextToSpeech {
|
||||
}
|
||||
|
||||
private boolean connectToEngine(String engine) {
|
||||
Connection connection;
|
||||
if (mIsSystem) {
|
||||
connection = new SystemConnection();
|
||||
} else {
|
||||
connection = new DirectConnection();
|
||||
}
|
||||
|
||||
boolean bound = connection.connect(engine);
|
||||
Connection connection = new Connection();
|
||||
Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
|
||||
intent.setPackage(engine);
|
||||
boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
|
||||
if (!bound) {
|
||||
Log.e(TAG, "Failed to bind to " + engine);
|
||||
return false;
|
||||
@@ -884,19 +857,11 @@ public class TextToSpeech {
|
||||
}
|
||||
|
||||
private void dispatchOnInit(int result) {
|
||||
Runnable onInitCommand = () -> {
|
||||
synchronized (mStartLock) {
|
||||
if (mInitListener != null) {
|
||||
mInitListener.onInit(result);
|
||||
mInitListener = null;
|
||||
}
|
||||
synchronized (mStartLock) {
|
||||
if (mInitListener != null) {
|
||||
mInitListener.onInit(result);
|
||||
mInitListener = null;
|
||||
}
|
||||
};
|
||||
|
||||
if (mInitExecutor != null) {
|
||||
mInitExecutor.execute(onInitCommand);
|
||||
} else {
|
||||
onInitCommand.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2162,17 +2127,13 @@ public class TextToSpeech {
|
||||
return mEnginesHelper.getEngines();
|
||||
}
|
||||
|
||||
private abstract class Connection implements ServiceConnection {
|
||||
private class Connection implements ServiceConnection {
|
||||
private ITextToSpeechService mService;
|
||||
|
||||
private SetupConnectionAsyncTask mOnSetupConnectionAsyncTask;
|
||||
|
||||
private boolean mEstablished;
|
||||
|
||||
abstract boolean connect(String engine);
|
||||
|
||||
abstract void disconnect();
|
||||
|
||||
private final ITextToSpeechCallback.Stub mCallback =
|
||||
new ITextToSpeechCallback.Stub() {
|
||||
public void onStop(String utteranceId, boolean isStarted)
|
||||
@@ -2238,6 +2199,11 @@ public class TextToSpeech {
|
||||
};
|
||||
|
||||
private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> {
|
||||
private final ComponentName mName;
|
||||
|
||||
public SetupConnectionAsyncTask(ComponentName name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Void... params) {
|
||||
@@ -2261,7 +2227,7 @@ public class TextToSpeech {
|
||||
mParams.putString(Engine.KEY_PARAM_VOICE_NAME, defaultVoiceName);
|
||||
}
|
||||
|
||||
Log.i(TAG, "Setting up the connection to TTS engine...");
|
||||
Log.i(TAG, "Set up connection to " + mName);
|
||||
return SUCCESS;
|
||||
} catch (RemoteException re) {
|
||||
Log.e(TAG, "Error connecting to service, setCallback() failed");
|
||||
@@ -2283,11 +2249,11 @@ public class TextToSpeech {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName componentName, IBinder service) {
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
synchronized(mStartLock) {
|
||||
mConnectingServiceConnection = null;
|
||||
|
||||
Log.i(TAG, "Connected to TTS engine");
|
||||
Log.i(TAG, "Connected to " + name);
|
||||
|
||||
if (mOnSetupConnectionAsyncTask != null) {
|
||||
mOnSetupConnectionAsyncTask.cancel(false);
|
||||
@@ -2297,7 +2263,7 @@ public class TextToSpeech {
|
||||
mServiceConnection = Connection.this;
|
||||
|
||||
mEstablished = false;
|
||||
mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask();
|
||||
mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask(name);
|
||||
mOnSetupConnectionAsyncTask.execute();
|
||||
}
|
||||
}
|
||||
@@ -2311,7 +2277,7 @@ public class TextToSpeech {
|
||||
*
|
||||
* @return true if we cancel mOnSetupConnectionAsyncTask in progress.
|
||||
*/
|
||||
protected boolean clearServiceConnection() {
|
||||
private boolean clearServiceConnection() {
|
||||
synchronized(mStartLock) {
|
||||
boolean result = false;
|
||||
if (mOnSetupConnectionAsyncTask != null) {
|
||||
@@ -2329,8 +2295,8 @@ public class TextToSpeech {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
Log.i(TAG, "Disconnected from TTS engine");
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
Log.i(TAG, "Asked to disconnect from " + name);
|
||||
if (clearServiceConnection()) {
|
||||
/* We need to protect against a rare case where engine
|
||||
* dies just after successful connection - and we process onServiceDisconnected
|
||||
@@ -2342,6 +2308,11 @@ public class TextToSpeech {
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
mContext.unbindService(this);
|
||||
clearServiceConnection();
|
||||
}
|
||||
|
||||
public boolean isEstablished() {
|
||||
return mService != null && mEstablished;
|
||||
}
|
||||
@@ -2371,91 +2342,6 @@ public class TextToSpeech {
|
||||
}
|
||||
}
|
||||
|
||||
// Currently all the clients are routed through the System connection. Direct connection
|
||||
// is left for debugging, testing and benchmarking purposes.
|
||||
// TODO(b/179599071): Remove direct connection once system one is fully tested.
|
||||
private class DirectConnection extends Connection {
|
||||
@Override
|
||||
boolean connect(String engine) {
|
||||
Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
|
||||
intent.setPackage(engine);
|
||||
return mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
void disconnect() {
|
||||
mContext.unbindService(this);
|
||||
clearServiceConnection();
|
||||
}
|
||||
}
|
||||
|
||||
private class SystemConnection extends Connection {
|
||||
|
||||
@Nullable
|
||||
private volatile ITextToSpeechSession mSession;
|
||||
|
||||
@Override
|
||||
boolean connect(String engine) {
|
||||
IBinder binder = ServiceManager.getService(Context.TEXT_TO_SPEECH_MANAGER_SERVICE);
|
||||
|
||||
ITextToSpeechManager manager = ITextToSpeechManager.Stub.asInterface(binder);
|
||||
|
||||
if (manager == null) {
|
||||
Log.e(TAG, "System service is not available!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Connecting to engine: " + engine);
|
||||
}
|
||||
|
||||
try {
|
||||
manager.createSession(engine, new ITextToSpeechSessionCallback.Stub() {
|
||||
@Override
|
||||
public void onConnected(ITextToSpeechSession session, IBinder serviceBinder) {
|
||||
mSession = session;
|
||||
onServiceConnected(
|
||||
/* componentName= */ null,
|
||||
serviceBinder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
onServiceDisconnected(/* componentName= */ null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorInfo) {
|
||||
Log.w(TAG, "System TTS connection error: " + errorInfo);
|
||||
// The connection was not established successfully - handle as
|
||||
// disconnection: clear the state and notify the user.
|
||||
onServiceDisconnected(/* componentName= */ null);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Error communicating with the System Server: ", ex);
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void disconnect() {
|
||||
ITextToSpeechSession session = mSession;
|
||||
|
||||
if (session != null) {
|
||||
try {
|
||||
session.disconnect();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Error disconnecting session", ex);
|
||||
}
|
||||
|
||||
clearServiceConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface Action<R> {
|
||||
R run(ITextToSpeechService service) throws RemoteException;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ filegroup {
|
||||
":services.startop.iorap-sources",
|
||||
":services.systemcaptions-sources",
|
||||
":services.translation-sources",
|
||||
":services.texttospeech-sources",
|
||||
":services.usage-sources",
|
||||
":services.usb-sources",
|
||||
":services.voiceinteraction-sources",
|
||||
@@ -84,7 +83,6 @@ java_library {
|
||||
"services.startop",
|
||||
"services.systemcaptions",
|
||||
"services.translation",
|
||||
"services.texttospeech",
|
||||
"services.usage",
|
||||
"services.usb",
|
||||
"services.voiceinteraction",
|
||||
|
||||
@@ -371,9 +371,6 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
|
||||
int durationMs) {
|
||||
Slog.i(mTag, "setTemporaryService(" + userId + ") to " + componentName + " for "
|
||||
+ durationMs + "ms");
|
||||
if (mServiceNameResolver == null) {
|
||||
return;
|
||||
}
|
||||
enforceCallingPermissionForManagement();
|
||||
|
||||
Objects.requireNonNull(componentName);
|
||||
@@ -407,9 +404,6 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
|
||||
enforceCallingPermissionForManagement();
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mServiceNameResolver == null) {
|
||||
return false;
|
||||
}
|
||||
final boolean changed = mServiceNameResolver.setDefaultServiceEnabled(userId, enabled);
|
||||
if (!changed) {
|
||||
if (verbose) {
|
||||
@@ -440,10 +434,6 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
|
||||
public final boolean isDefaultServiceEnabled(@UserIdInt int userId) {
|
||||
enforceCallingPermissionForManagement();
|
||||
|
||||
if (mServiceNameResolver == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
return mServiceNameResolver.isDefaultServiceEnabled(userId);
|
||||
}
|
||||
@@ -968,10 +958,6 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
|
||||
public void onPackageModified(String packageName) {
|
||||
if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
|
||||
|
||||
if (mServiceNameResolver == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int userId = getChangingUserId();
|
||||
final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
|
||||
if (serviceName == null) {
|
||||
|
||||
@@ -326,8 +326,6 @@ public final class SystemServer implements Dumpable {
|
||||
"com.android.server.musicrecognition.MusicRecognitionManagerService";
|
||||
private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
|
||||
"com.android.server.systemcaptions.SystemCaptionsManagerService";
|
||||
private static final String TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS =
|
||||
"com.android.server.texttospeech.TextToSpeechManagerService";
|
||||
private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
|
||||
"com.android.server.timezone.RulesManagerService$Lifecycle";
|
||||
private static final String IOT_SERVICE_CLASS =
|
||||
@@ -1715,7 +1713,6 @@ public final class SystemServer implements Dumpable {
|
||||
startAttentionService(context, t);
|
||||
startRotationResolverService(context, t);
|
||||
startSystemCaptionsManagerService(context, t);
|
||||
startTextToSpeechManagerService(context, t);
|
||||
|
||||
// System Speech Recognition Service
|
||||
if (deviceHasConfigString(context,
|
||||
@@ -2921,13 +2918,6 @@ public final class SystemServer implements Dumpable {
|
||||
t.traceEnd();
|
||||
}
|
||||
|
||||
private void startTextToSpeechManagerService(@NonNull Context context,
|
||||
@NonNull TimingsTraceAndSlog t) {
|
||||
t.traceBegin("StartTextToSpeechManagerService");
|
||||
mSystemServiceManager.startService(TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS);
|
||||
t.traceEnd();
|
||||
}
|
||||
|
||||
private void startContentCaptureService(@NonNull Context context,
|
||||
@NonNull TimingsTraceAndSlog t) {
|
||||
// First check if it was explicitly enabled by DeviceConfig
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
filegroup {
|
||||
name: "services.texttospeech-sources",
|
||||
srcs: ["java/**/*.java"],
|
||||
path: "java",
|
||||
visibility: ["//frameworks/base/services"],
|
||||
}
|
||||
|
||||
java_library_static {
|
||||
name: "services.texttospeech",
|
||||
defaults: ["platform_service_defaults"],
|
||||
srcs: [":services.texttospeech-sources"],
|
||||
libs: ["services.core"],
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.texttospeech;
|
||||
|
||||
import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.AppGlobals;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.IBinder.DeathRecipient;
|
||||
import android.os.RemoteException;
|
||||
import android.speech.tts.ITextToSpeechService;
|
||||
import android.speech.tts.ITextToSpeechSession;
|
||||
import android.speech.tts.ITextToSpeechSessionCallback;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.infra.ServiceConnector;
|
||||
import com.android.server.infra.AbstractPerUserSystemService;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Manages per-user text to speech session activated by {@link TextToSpeechManagerService}.
|
||||
* Creates {@link TtsClient} interface object with direct connection to
|
||||
* {@link android.speech.tts.TextToSpeechService} provider.
|
||||
*
|
||||
* @see ITextToSpeechSession
|
||||
* @see TextToSpeech
|
||||
*/
|
||||
final class TextToSpeechManagerPerUserService extends
|
||||
AbstractPerUserSystemService<TextToSpeechManagerPerUserService,
|
||||
TextToSpeechManagerService> {
|
||||
|
||||
private static final String TAG = TextToSpeechManagerPerUserService.class.getSimpleName();
|
||||
|
||||
TextToSpeechManagerPerUserService(
|
||||
@NonNull TextToSpeechManagerService master,
|
||||
@NonNull Object lock, @UserIdInt int userId) {
|
||||
super(master, lock, userId);
|
||||
}
|
||||
|
||||
void createSessionLocked(String engine, ITextToSpeechSessionCallback sessionCallback) {
|
||||
TextToSpeechSessionConnection.start(getContext(), mUserId, engine, sessionCallback);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@Override // from PerUserSystemService
|
||||
@NonNull
|
||||
protected ServiceInfo newServiceInfoLocked(
|
||||
@SuppressWarnings("unused") @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);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TextToSpeechSessionConnection extends
|
||||
ServiceConnector.Impl<ITextToSpeechService> {
|
||||
|
||||
private final String mEngine;
|
||||
private final ITextToSpeechSessionCallback mCallback;
|
||||
private final DeathRecipient mUnbindOnDeathHandler;
|
||||
|
||||
static void start(Context context, @UserIdInt int userId, String engine,
|
||||
ITextToSpeechSessionCallback callback) {
|
||||
new TextToSpeechSessionConnection(context, userId, engine, callback).start();
|
||||
}
|
||||
|
||||
private TextToSpeechSessionConnection(Context context, @UserIdInt int userId, String engine,
|
||||
ITextToSpeechSessionCallback callback) {
|
||||
super(context,
|
||||
new Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE).setPackage(engine),
|
||||
Context.BIND_AUTO_CREATE,
|
||||
userId,
|
||||
ITextToSpeechService.Stub::asInterface);
|
||||
mEngine = engine;
|
||||
mCallback = callback;
|
||||
mUnbindOnDeathHandler = () -> unbindEngine("client process death is reported");
|
||||
}
|
||||
|
||||
private void start() {
|
||||
Slog.d(TAG, "Trying to start connection to TTS engine: " + mEngine);
|
||||
|
||||
connect()
|
||||
.thenAccept(
|
||||
serviceBinder -> {
|
||||
if (serviceBinder != null) {
|
||||
Slog.d(TAG,
|
||||
"Connected successfully to TTS engine: " + mEngine);
|
||||
try {
|
||||
mCallback.onConnected(new ITextToSpeechSession.Stub() {
|
||||
@Override
|
||||
public void disconnect() {
|
||||
unbindEngine("client disconnection request");
|
||||
}
|
||||
}, serviceBinder.asBinder());
|
||||
|
||||
mCallback.asBinder().linkToDeath(mUnbindOnDeathHandler, 0);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Error notifying the client on connection", ex);
|
||||
|
||||
unbindEngine(
|
||||
"failed communicating with the client - process "
|
||||
+ "is dead");
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Failed to obtain TTS engine binder");
|
||||
runSessionCallbackMethod(
|
||||
() -> mCallback.onError("Failed creating TTS session"));
|
||||
}
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
Slog.w(TAG, "TTS engine binding error", ex);
|
||||
runSessionCallbackMethod(
|
||||
() -> mCallback.onError(
|
||||
"Failed creating TTS session: " + ex.getCause()));
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override // from ServiceConnector.Impl
|
||||
protected void onServiceConnectionStatusChanged(
|
||||
ITextToSpeechService service, boolean connected) {
|
||||
if (!connected) {
|
||||
Slog.w(TAG, "Disconnected from TTS engine");
|
||||
runSessionCallbackMethod(mCallback::onDisconnected);
|
||||
|
||||
try {
|
||||
mCallback.asBinder().unlinkToDeath(mUnbindOnDeathHandler, 0);
|
||||
} catch (NoSuchElementException ex) {
|
||||
Slog.d(TAG, "The death recipient was not linked.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from ServiceConnector.Impl
|
||||
protected long getAutoDisconnectTimeoutMs() {
|
||||
return PERMANENT_BOUND_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
private void unbindEngine(String reason) {
|
||||
Slog.d(TAG, "Unbinding TTS engine: " + mEngine + ". Reason: " + reason);
|
||||
unbind();
|
||||
}
|
||||
}
|
||||
|
||||
static void runSessionCallbackMethod(ThrowingRunnable callbackRunnable) {
|
||||
try {
|
||||
callbackRunnable.runOrThrow();
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed running callback method", ex);
|
||||
}
|
||||
}
|
||||
|
||||
interface ThrowingRunnable {
|
||||
void runOrThrow() throws RemoteException;
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.texttospeech;
|
||||
|
||||
import static com.android.server.texttospeech.TextToSpeechManagerPerUserService.runSessionCallbackMethod;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.speech.tts.ITextToSpeechManager;
|
||||
import android.speech.tts.ITextToSpeechSessionCallback;
|
||||
|
||||
import com.android.server.infra.AbstractMasterSystemService;
|
||||
|
||||
|
||||
/**
|
||||
* A service that allows secured synthesizing of text to speech audio. Upon request creates a
|
||||
* session
|
||||
* that is managed by {@link TextToSpeechManagerPerUserService}.
|
||||
*
|
||||
* @see ITextToSpeechManager
|
||||
*/
|
||||
public final class TextToSpeechManagerService extends
|
||||
AbstractMasterSystemService<TextToSpeechManagerService,
|
||||
TextToSpeechManagerPerUserService> {
|
||||
|
||||
private static final String TAG = TextToSpeechManagerService.class.getSimpleName();
|
||||
|
||||
public TextToSpeechManagerService(@NonNull Context context) {
|
||||
super(context, /* serviceNameResolver= */ null,
|
||||
/* disallowProperty = */null);
|
||||
}
|
||||
|
||||
@Override // from SystemService
|
||||
public void onStart() {
|
||||
publishBinderService(Context.TEXT_TO_SPEECH_MANAGER_SERVICE,
|
||||
new TextToSpeechManagerServiceStub());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextToSpeechManagerPerUserService newServiceLocked(
|
||||
@UserIdInt int resolvedUserId, boolean disabled) {
|
||||
return new TextToSpeechManagerPerUserService(this, mLock, resolvedUserId);
|
||||
}
|
||||
|
||||
private final class TextToSpeechManagerServiceStub extends ITextToSpeechManager.Stub {
|
||||
@Override
|
||||
public void createSession(String engine,
|
||||
ITextToSpeechSessionCallback sessionCallback) {
|
||||
synchronized (mLock) {
|
||||
TextToSpeechManagerPerUserService perUserService = getServiceForUserLocked(
|
||||
UserHandle.getCallingUserId());
|
||||
if (perUserService != null) {
|
||||
perUserService.createSessionLocked(engine, sessionCallback);
|
||||
} else {
|
||||
runSessionCallbackMethod(
|
||||
() -> sessionCallback.onError("Service is not available for user"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user