/* * 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 android.view.translation; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.WorkerThread; import android.app.PendingIntent; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; import android.os.Looper; import android.os.RemoteException; import android.os.SynchronousResultReceiver; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; import android.util.Log; import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.SyncResultReceiver; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; /** * The {@link TranslationManager} class provides ways for apps to integrate and use the * translation framework. * *
The TranslationManager manages {@link Translator}s and help bridge client calls to * the server translation service
*/ @SystemService(Context.TRANSLATION_MANAGER_SERVICE) public final class TranslationManager { private static final String TAG = "TranslationManager"; /** * Timeout for calls to system_server, default 1 minute. */ static final int SYNC_CALLS_TIMEOUT_MS = 60_000; /** * The result code from result receiver success. * @hide */ public static final int STATUS_SYNC_CALL_SUCCESS = 1; /** * The result code from result receiver fail. * @hide */ public static final int STATUS_SYNC_CALL_FAIL = 2; /** * Name of the extra used to pass the translation capabilities. * @hide */ public static final String EXTRA_CAPABILITIES = "translation_capabilities"; @GuardedBy("mLock") private final ArrayMapIn Android 12, this method provided the same cached Translator object when given the * same TranslationContext object. Do not use a Translator destroyed elsewhere as this will * cause an exception on Android 12. * *
In later versions, this method never returns a cached Translator.
*
* @param translationContext {@link TranslationContext} containing the specs for creating the
* Translator.
* @param executor Executor to run callback operations
* @param callback {@link Consumer} to receive the translator. A {@code null} value is returned
* if the service could not create the translator.
*/
public void createOnDeviceTranslator(@NonNull TranslationContext translationContext,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer NOTE: Call on a worker thread.
*
* @removed use {@link #createOnDeviceTranslator(TranslationContext, Executor, Consumer)}
* instead.
*
* @param translationContext {@link TranslationContext} containing the specs for creating the
* Translator.
*/
@Deprecated
@Nullable
@WorkerThread
public Translator createOnDeviceTranslator(@NonNull TranslationContext translationContext) {
Objects.requireNonNull(translationContext, "translationContext cannot be null");
synchronized (mLock) {
int translatorId;
do {
translatorId = Math.abs(ID_GENERATOR.nextInt());
} while (translatorId == 0 || mTranslatorIds.indexOf(translatorId) >= 0);
final Translator newTranslator = new Translator(mContext, translationContext,
translatorId, this, mHandler, mService);
// Start the Translator session and wait for the result
newTranslator.start();
try {
if (!newTranslator.isSessionCreated()) {
return null;
}
mTranslatorIds.add(translatorId);
return newTranslator;
} catch (Translator.ServiceBinderReceiver.TimeoutException e) {
// TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor
// public and use it.
Log.e(TAG, "Timed out getting create session: " + e);
return null;
}
}
}
/** @removed Use {@link #createOnDeviceTranslator(TranslationContext)} */
@Deprecated
@Nullable
@WorkerThread
public Translator createTranslator(@NonNull TranslationContext translationContext) {
return createOnDeviceTranslator(translationContext);
}
/**
* Returns a set of {@link TranslationCapability}s describing the capabilities for on-device
* {@link Translator}s.
*
* These translation capabilities contains a source and target {@link TranslationSpec}
* representing the data expected for both ends of translation process. The capabilities
* provides the information and limitations for generating a {@link TranslationContext}.
* The context object can then be used by
* {@link #createOnDeviceTranslator(TranslationContext, Executor, Consumer)} to obtain a
* {@link Translator} for translations. NOTE: Call on a worker thread.
*
* @param sourceFormat data format for the input data to be translated.
* @param targetFormat data format for the expected translated output data.
* @return A set of {@link TranslationCapability}s.
*/
@NonNull
@WorkerThread
public Set
*
**/
@Nullable
public PendingIntent getOnDeviceTranslationSettingsActivityIntent() {
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.getServiceSettingsActivity(resultReceiver, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
try {
return resultReceiver.getParcelableResult();
} catch (SyncResultReceiver.TimeoutException e) {
Log.e(TAG, "Fail to get translation service settings activity.");
return null;
}
}
/** @removed Use {@link #getOnDeviceTranslationSettingsActivityIntent()} */
@Deprecated
@Nullable
public PendingIntent getTranslationSettingsActivityIntent() {
return getOnDeviceTranslationSettingsActivityIntent();
}
void removeTranslator(int id) {
synchronized (mLock) {
int index = mTranslatorIds.indexOf(id);
if (index >= 0) {
mTranslatorIds.remove(index);
}
}
}
AtomicInteger getAvailableRequestId() {
synchronized (mLock) {
return sAvailableRequestId;
}
}
private static class TranslationCapabilityRemoteCallback extends
IRemoteCallback.Stub {
private final Executor mExecutor;
private final Consumer