Changed createOnDeviceTranslator to be asynchronous with callback.

Bug: 176208267
Test: atest CtsTranslationTestCases
Change-Id: Icedc953611e70dc208cbef54b4e459ebb0ccd5a9
diff --git a/core/api/current.txt b/core/api/current.txt
index 4cdc519..145719b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52839,7 +52839,8 @@
     method public void addOnDeviceTranslationCapabilityUpdateListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.translation.TranslationCapability>);
     method @Deprecated public void addOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
     method @Deprecated public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
-    method @Nullable @WorkerThread public android.view.translation.Translator createOnDeviceTranslator(@NonNull android.view.translation.TranslationContext);
+    method public void createOnDeviceTranslator(@NonNull android.view.translation.TranslationContext, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.translation.Translator>);
+    method @Deprecated @Nullable @WorkerThread public android.view.translation.Translator createOnDeviceTranslator(@NonNull android.view.translation.TranslationContext);
     method @Deprecated @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext);
     method @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getOnDeviceTranslationCapabilities(int, int);
     method @Nullable public android.app.PendingIntent getOnDeviceTranslationSettingsActivityIntent();
diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java
index e755774..20d817d 100644
--- a/core/java/android/view/translation/TranslationManager.java
+++ b/core/java/android/view/translation/TranslationManager.java
@@ -100,10 +100,6 @@
 
     private final ITranslationManager mService;
 
-    @Nullable
-    @GuardedBy("mLock")
-    private ITranslationDirectManager mDirectServiceBinder;
-
     @NonNull
     @GuardedBy("mLock")
     private final SparseArray<Translator> mTranslators = new SparseArray<>();
@@ -131,11 +127,72 @@
     /**
      * Creates an on-device Translator for natural language translation.
      *
+     * @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<Translator> callback) {
+        Objects.requireNonNull(translationContext, "translationContext cannot be null");
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(callback, "callback cannot be null");
+
+        synchronized (mLock) {
+            // TODO(b/176464808): Disallow multiple Translator now, it will throw
+            //  IllegalStateException. Need to discuss if we can allow multiple Translators.
+            if (mTranslatorIds.containsKey(translationContext)) {
+                executor.execute(() -> callback.accept(
+                        mTranslators.get(mTranslatorIds.get(translationContext))));
+                return;
+            }
+
+            int translatorId;
+            do {
+                translatorId = Math.abs(ID_GENERATOR.nextInt());
+            } while (translatorId == 0 || mTranslators.indexOfKey(translatorId) >= 0);
+            final int tId = translatorId;
+
+            new Translator(mContext, translationContext, translatorId, this, mHandler, mService,
+                    new Consumer<Translator>() {
+                        @Override
+                        public void accept(Translator translator) {
+                            if (translator == null) {
+                                final long token = Binder.clearCallingIdentity();
+                                try {
+                                    executor.execute(() -> callback.accept(null));
+                                } finally {
+                                    Binder.restoreCallingIdentity(token);
+                                }
+                                return;
+                            }
+
+                            mTranslators.put(tId, translator);
+                            mTranslatorIds.put(translationContext, tId);
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.accept(translator));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Creates an on-device Translator for natural language translation.
+     *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
+     * @deprecated 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) {
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 6037302..b0d95b3 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -101,10 +101,18 @@
     public static final String EXTRA_SESSION_ID = "sessionId";
 
     static class ServiceBinderReceiver extends IResultReceiver.Stub {
+        // TODO: refactor how translator is instantiated after removing deprecated createTranslator.
         private final WeakReference<Translator> mTranslator;
         private final CountDownLatch mLatch = new CountDownLatch(1);
         private int mSessionId;
 
+        private Consumer<Translator> mCallback;
+
+        ServiceBinderReceiver(Translator translator, Consumer<Translator> callback) {
+            mTranslator = new WeakReference<>(translator);
+            mCallback = callback;
+        }
+
         ServiceBinderReceiver(Translator translator) {
             mTranslator = new WeakReference<>(translator);
         }
@@ -126,6 +134,9 @@
         public void send(int resultCode, Bundle resultData) {
             if (resultCode == STATUS_SYNC_CALL_FAIL) {
                 mLatch.countDown();
+                if (mCallback != null) {
+                    mCallback.accept(null);
+                }
                 return;
             }
             mSessionId = resultData.getInt(EXTRA_SESSION_ID);
@@ -146,6 +157,9 @@
             }
             translator.setServiceBinder(binder);
             mLatch.countDown();
+            if (mCallback != null) {
+                mCallback.accept(translator);
+            }
         }
 
         // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor public
@@ -165,6 +179,32 @@
     public Translator(@NonNull Context context,
             @NonNull TranslationContext translationContext, int sessionId,
             @NonNull TranslationManager translationManager, @NonNull Handler handler,
+            @Nullable ITranslationManager systemServerBinder,
+            @NonNull Consumer<Translator> callback) {
+        mContext = context;
+        mTranslationContext = translationContext;
+        mId = sessionId;
+        mManager = translationManager;
+        mHandler = handler;
+        mSystemServerBinder = systemServerBinder;
+        mServiceBinderReceiver = new ServiceBinderReceiver(this, callback);
+
+        try {
+            mSystemServerBinder.onSessionCreated(mTranslationContext, mId,
+                    mServiceBinderReceiver, mContext.getUserId());
+        } catch (RemoteException e) {
+            Log.w(TAG, "RemoteException calling startSession(): " + e);
+        }
+    }
+
+    /**
+     * Create the Translator.
+     *
+     * @hide
+     */
+    public Translator(@NonNull Context context,
+            @NonNull TranslationContext translationContext, int sessionId,
+            @NonNull TranslationManager translationManager, @NonNull Handler handler,
             @Nullable ITranslationManager systemServerBinder) {
         mContext = context;
         mTranslationContext = translationContext;