Merge "Revert "Add a system TextToSpeech implementation that initiates ..."" into sc-dev

This commit is contained in:
Greg Kaiser
2021-02-18 05:39:24 +00:00
committed by Android (Google) Code Review
11 changed files with 27 additions and 543 deletions

View File

@@ -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.
*

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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

View File

@@ -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"],
}

View File

@@ -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;
}
}

View File

@@ -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"));
}
}
}
}
}