Add APIs that can notify client ui translation state.

The initial APIs for ui translation. There is no implementation in
this change, we will implement it in the next CL.

Bug: 172969740
Bug: 176871912
Test: manual. build pass and build success.

Change-Id: I4ae0bc7a695076a87bed73e458396312d87f48c5
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 730fce7..141a78f 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";
@@ -1943,6 +1944,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";
@@ -13405,6 +13407,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 dd016a2..e62cd4f 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -211,6 +211,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;
@@ -1188,6 +1189,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 5ccceca..5736985 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 c18e15e..05fbe4b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3599,6 +3599,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
+    }
 }