Merge "Add APIs that can notify client ui translation state."
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 275b31b..d91ea2c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -137,6 +137,7 @@
     field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION";
+    field public static final String MANAGE_UI_TRANSLATION = "android.permission.MANAGE_UI_TRANSLATION";
     field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
@@ -1947,6 +1948,7 @@
     field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
     field public static final String TETHERING_SERVICE = "tethering";
     field public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
+    field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
     field public static final String VR_SERVICE = "vrmanager";
     field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
     field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
@@ -13437,6 +13439,17 @@
 
 }
 
+package android.view.translation {
+
+  public final class UiTranslationManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void pauseTranslation(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void resumeTranslation(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, int);
+  }
+
+}
+
 package android.webkit {
 
   public abstract class CookieManager {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fe13fd4..bbda871 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -210,6 +210,7 @@
 import android.view.textservice.TextServicesManager;
 import android.view.translation.ITranslationManager;
 import android.view.translation.TranslationManager;
+import android.view.translation.UiTranslationManager;
 
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
@@ -1187,6 +1188,19 @@
                         return null;
                     }});
 
+        registerService(Context.UI_TRANSLATION_SERVICE, UiTranslationManager.class,
+                new CachedServiceFetcher<UiTranslationManager>() {
+                    @Override
+                    public UiTranslationManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getService(Context.TRANSLATION_MANAGER_SERVICE);
+                        ITranslationManager service = ITranslationManager.Stub.asInterface(b);
+                        if (service != null) {
+                            return new UiTranslationManager(ctx.getOuterContext(), service);
+                        }
+                        return null;
+                    }});
+
         registerService(Context.SEARCH_UI_SERVICE, SearchUiManager.class,
             new CachedServiceFetcher<SearchUiManager>() {
                 @Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index eb70ae1..2190140 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4520,6 +4520,15 @@
     public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
 
     /**
+     * Official published name of the translation service which supports ui translation function.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String UI_TRANSLATION_SERVICE = "ui_translation";
+
+    /**
      * Used for getting content selections and classifications for task snapshots.
      *
      * @hide
diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
index 73addf4..e175453 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -16,10 +16,15 @@
 
 package android.view.translation;
 
+import android.content.ComponentName;
+import android.os.IBinder;
 import android.service.translation.TranslationRequest;
+import android.view.autofill.AutofillId;
 import android.view.translation.TranslationSpec;
 import com.android.internal.os.IResultReceiver;
 
+import java.util.List;
+
 /**
  * Mediator between apps being translated and translation service implementation.
  *
@@ -29,4 +34,8 @@
     void getSupportedLocales(in IResultReceiver receiver, int userId);
     void onSessionCreated(in TranslationSpec sourceSpec, in TranslationSpec destSpec,
          int sessionId, in IResultReceiver receiver, int userId);
+
+    void updateUiTranslationState(int state, in TranslationSpec sourceSpec,
+         in TranslationSpec destSpec, in List<AutofillId> viewIds, in int taskId,
+         int userId);
 }
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
new file mode 100644
index 0000000..eeb463a
--- /dev/null
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -0,0 +1,179 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.RemoteException;
+import android.view.View;
+import android.view.autofill.AutofillId;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The {@link UiTranslationManager} class provides ways for apps to use the ui translation
+ * function in framework.
+ *
+ * @hide
+ */
+@SystemApi
+public final class UiTranslationManager {
+
+    private static final String TAG = "UiTranslationManager";
+
+    /**
+     * The state caller request to disable utranslation,, it is no longer need to ui translation.
+     *
+     * @hide
+     */
+    public static final int STATE_UI_TRANSLATION_STARTED = 0;
+    /**
+     * The state caller request to pause ui translation, it will switch back to the original text.
+     *
+     * @hide
+     */
+    public static final int STATE_UI_TRANSLATION_PAUSED = 1;
+    /**
+     * The state caller request to resume the paused ui translation, it will show the translated
+     * text again if the text had been translated.
+     *
+     * @hide
+     */
+    public static final int STATE_UI_TRANSLATION_RESUMED = 2;
+    /**
+     * The state the caller request to enable ui translation.
+     *
+     * @hide
+     */
+    public static final int STATE_UI_TRANSLATION_FINISHED = 3;
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"STATE__TRANSLATION"}, value = {
+            STATE_UI_TRANSLATION_STARTED,
+            STATE_UI_TRANSLATION_PAUSED,
+            STATE_UI_TRANSLATION_RESUMED,
+            STATE_UI_TRANSLATION_FINISHED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UiTranslationState {
+    }
+
+    @NonNull
+    private final Context mContext;
+
+    private final ITranslationManager mService;
+
+    /**
+     * @hide
+     */
+    public UiTranslationManager(@NonNull Context context, ITranslationManager service) {
+        mContext = Objects.requireNonNull(context);
+        mService = service;
+    }
+
+    /**
+     * Request ui translation for a given Views.
+     *
+     * @param sourceSpec {@link TranslationSpec} for the data to be translated.
+     * @param destSpec {@link TranslationSpec} for the translated data.
+     * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
+     * @param taskId the Activity Task id which needs ui translation
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    public void startTranslation(@NonNull TranslationSpec sourceSpec,
+            @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
+            int taskId) {
+        // TODO(b/177789967): Return result code or find a way to notify the status.
+        // TODO(b/177394471): The is a temparary API, the expected is requestUiTranslation(
+        //  TranslationSpec, TranslationSpec,List<AutofillId>, Binder). We may need more time to
+        //  implement it, use task id as initial version for demo.
+        Objects.requireNonNull(sourceSpec);
+        Objects.requireNonNull(destSpec);
+        Objects.requireNonNull(viewIds);
+
+        try {
+            mService.updateUiTranslationState(STATE_UI_TRANSLATION_STARTED, sourceSpec,
+                    destSpec, viewIds, taskId, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request to disable the ui translation. It will destroy all the {@link Translator}s and no
+     * longer to show to show the translated text.
+     *
+     * @param taskId the Activity Task id which needs ui translation
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    public void finishTranslation(int taskId) {
+        try {
+            // TODO(b/177394471): The is a temparary API, the expected is finishUiTranslation(
+            //  Binder). We may need more time to implement it, use task id as initial version.
+            mService.updateUiTranslationState(STATE_UI_TRANSLATION_FINISHED,
+                    null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId,
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request to pause the current ui translation's {@link Translator} which will switch back to
+     * the original language.
+     *
+     * @param taskId the Activity Task id which needs ui translation
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    public void pauseTranslation(int taskId) {
+        try {
+            // TODO(b/177394471): The is a temparary API, the expected is pauseUiTranslation(Binder)
+            // We may need more time to implement it, use task id as initial version for demo
+            mService.updateUiTranslationState(STATE_UI_TRANSLATION_PAUSED,
+                    null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId,
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request to resume the paused ui translation's {@link Translator} which will switch to the
+     * translated language if the text had been translated.
+     *
+     * @param taskId the Activity Task id which needs ui translation
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    public void resumeTranslation(int taskId) {
+        try {
+            // TODO(b/177394471): The is a temparary API, the expected is resumeUiTranslation(
+            //  Binder). We may need more time to implement it, use task id as initial version.
+            mService.updateUiTranslationState(STATE_UI_TRANSLATION_RESUMED,
+                    null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+                    taskId, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 911f22d..253aece 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3620,6 +3620,15 @@
     <permission android:name="android.permission.BIND_TRANSLATION_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows apps to use ui translation functions.
+         <p>Protection level: signature|privileged
+         <p> TODO(b/177703453): Restrict to a specific Role instead of allowing access to any
+         privileged app.
+         @hide Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.MANAGE_UI_TRANSLATION"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService,
          to ensure that only the system can bind to it.
          @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
index e2aabe6..84c6e7b 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerService.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -22,13 +22,17 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
 import android.view.translation.ITranslationManager;
 import android.view.translation.TranslationSpec;
+import android.view.translation.UiTranslationManager.UiTranslationState;
 
 import com.android.internal.os.IResultReceiver;
 import com.android.server.infra.AbstractMasterSystemService;
 import com.android.server.infra.FrameworkResourcesServiceNameResolver;
 
+import java.util.List;
+
 /**
  * Entry point service for translation management.
  *
@@ -82,6 +86,19 @@
                 }
             }
         }
+
+        @Override
+        public void updateUiTranslationState(@UiTranslationState int state,
+                TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds,
+                int taskId, int userId) {
+            synchronized (mLock) {
+                final TranslationManagerServiceImpl service = getServiceForUserLocked(userId);
+                if (service != null) {
+                    service.updateUiTranslationState(state, sourceSpec, destSpec, viewIds,
+                            taskId);
+                }
+            }
+        }
     }
 
     @Override // from SystemService
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index b1f6f80..5b1074f 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -26,7 +26,9 @@
 import android.os.RemoteException;
 import android.service.translation.TranslationServiceInfo;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
 import android.view.translation.TranslationSpec;
+import android.view.translation.UiTranslationManager.UiTranslationState;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
@@ -34,6 +36,7 @@
 import com.android.server.infra.AbstractPerUserSystemService;
 
 import java.util.ArrayList;
+import java.util.List;
 
 final class TranslationManagerServiceImpl extends
         AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> {
@@ -122,4 +125,11 @@
             remoteService.onSessionCreated(sourceSpec, destSpec, sessionId, resultReceiver);
         }
     }
+
+    @GuardedBy("mLock")
+    public void updateUiTranslationState(@UiTranslationState int state,
+            TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds,
+            int taskId) {
+        // TODO: implement this in next change
+    }
 }