Autofill compatibility mode.

Autofill helps users fill credentials, addresses, payment methods,
emails, etc without manually typing. When focus lands on a fillable
element the platform captures a snapshot of the screen content and
sends it to an autofill service for analysis and suggestions. The
screen snapshot is a structured representation of the screen content.
If this content is composed of standard widgets, autofill works
out-of-the-box. However, some apps do their own rendering and
the content in this case looks like a single view to the platform
while it may have semantic structure. For example, a view may render
a login page with two input test fields.

The platform exposes APIs for apps to report virtual view structure
allowing autofill services to handle apps that have virtual content.
As opposed to apps using standard widgets, this case requires the app
developer to implement the new APIs which may require a fair amount
of code and could be seen as a processes that could take some time.
The most prominent typs of apps that fall into this category are
browsers.

Until most apps rendering virtual content and specifically browsers
don't implement the virutal APIs, autofill providers need to fall-
back to using the accessibliity APIs to provide autofill support
for these apps. This requires developers to work against two sets
of APIs - autofill and accessibility - which is incovenient and error
prone. Also, users need to enable two plugins - autofill and
accessibility which is confusing. Additionally, the privacy and
perfomance impact of using the accessibility APIs cannot be addressed
while autofill providers need to use thes APis.

This change adds an autofill compatibility mode that would allow
autofill services to work with apps that don't implement the
virtual structure autofill APIs. The key idea is to locally enable
accessibility for the target package and remap accessibility to
autofill APIs and vise versa. This way an autofill provider codes
against a single set of APIs, the users enable a single plugin,
the privacy/performance implications of using the accessibility
APIs are addressed, the target app only takes a performance hit
since accessibility is enabled locally which is still more efficient
compared to the performance hit it would incur if accessibility is
enabled globally.

To enable compatibility mode an autofill service declares in its
metadata which packages it is interested in and also what is
the max version code of the package for which to enable compat
mode. Targeted versioning allows targeting only older versions of
the package that are known to not support autofill while newer
versions that are known to support autofill would work in normal
mode.

Since compatibility mode should be used only as a fallback we
have a white list setting with the packages for which this mode
can be requested. This allows applying policy to target only
apps that are known to not support autofill.

Test:
     cts-tradefed run cts-dev -m CtsAutoFillServiceTestCases
     cts-tradefed run cts-dev -m CtsAccessibilityServiceTestCases

bug:72811034

Change-Id: I11f1580ced0f8b4300a10b3a5174a1758a5702a0
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 06a9b06..0b10a35 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.accessibilityservice;
 
+import android.annotation.IntDef;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -43,6 +44,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -346,6 +349,19 @@
      */
     public String[] packageNames;
 
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FEEDBACK_" }, value = {
+            FEEDBACK_AUDIBLE,
+            FEEDBACK_GENERIC,
+            FEEDBACK_HAPTIC,
+            FEEDBACK_SPOKEN,
+            FEEDBACK_VISUAL,
+            FEEDBACK_BRAILLE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeedbackType {}
+
     /**
      * The feedback type an {@link AccessibilityService} provides.
      * <p>
@@ -358,6 +374,7 @@
      * @see #FEEDBACK_VISUAL
      * @see #FEEDBACK_BRAILLE
      */
+    @FeedbackType
     public int feedbackType;
 
     /**
@@ -818,7 +835,8 @@
         return stringBuilder.toString();
     }
 
-    private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
+    private static void appendFeedbackTypes(StringBuilder stringBuilder,
+            @FeedbackType int feedbackTypes) {
         stringBuilder.append("feedbackTypes:");
         stringBuilder.append("[");
         while (feedbackTypes != 0) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 04c44a3..67a6e67 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -113,6 +113,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.autofill.AutofillPopupWindow;
@@ -125,7 +126,6 @@
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneWindow;
 
 import dalvik.system.VMRuntime;
@@ -5961,12 +5961,16 @@
      *
      * @return Returns the complete component name for this activity
      */
-    @Override
-    public ComponentName getComponentName()
-    {
+    public ComponentName getComponentName() {
         return mComponent;
     }
 
+    /** @hide */
+    @Override
+    public final ComponentName autofillClientGetComponentName() {
+        return getComponentName();
+    }
+
     /**
      * Retrieve a {@link SharedPreferences} object for accessing preferences
      * that are private to this activity.  This simply calls the underlying
@@ -6262,7 +6266,6 @@
      *
      * @param action the action to run on the UI thread
      */
-    @Override
     public final void runOnUiThread(Runnable action) {
         if (Thread.currentThread() != mUiThread) {
             mHandler.post(action);
@@ -6271,6 +6274,12 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public final void autofillClientRunOnUiThread(Runnable action) {
+        runOnUiThread(action);
+    }
+
     /**
      * Standard implementation of
      * {@link android.view.LayoutInflater.Factory#onCreateView} used when
@@ -7076,6 +7085,18 @@
         mCurrentConfig = config;
 
         mWindow.setColorMode(info.colorMode);
+
+        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
+        enableAutofillCompatibilityIfNeeded();
+    }
+
+    private void enableAutofillCompatibilityIfNeeded() {
+        if (isAutofillCompatibilityEnabled()) {
+            final AutofillManager afm = getSystemService(AutofillManager.class);
+            if (afm != null) {
+                afm.enableCompatibilityMode();
+            }
+        }
     }
 
     /** @hide */
@@ -7572,7 +7593,7 @@
 
     /** @hide */
     @Override
-    final public void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
+    public final void autofillClientAuthenticate(int authenticationId, IntentSender intent,
             Intent fillInIntent) {
         try {
             startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX,
@@ -7584,13 +7605,13 @@
 
     /** @hide */
     @Override
-    final public void autofillCallbackResetableStateAvailable() {
+    public final void autofillClientResetableStateAvailable() {
         mAutoFillResetNeeded = true;
     }
 
     /** @hide */
     @Override
-    final public boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width,
+    public final boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width,
             int height, @Nullable Rect anchorBounds, IAutofillWindowPresenter presenter) {
         final boolean wasShowing;
 
@@ -7607,7 +7628,7 @@
 
     /** @hide */
     @Override
-    final public boolean autofillCallbackRequestHideFillUi() {
+    public final boolean autofillClientRequestHideFillUi() {
         if (mAutofillPopupWindow == null) {
             return false;
         }
@@ -7618,8 +7639,16 @@
 
     /** @hide */
     @Override
-    @NonNull public View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds) {
-        final View[] views = new View[viewIds.length];
+    public final boolean autofillClientIsFillUiShowing() {
+        return mAutofillPopupWindow != null && mAutofillPopupWindow.isShowing();
+    }
+
+    /** @hide */
+    @Override
+    @NonNull
+    public final View[] autofillClientFindViewsByAutofillIdTraversal(
+            @NonNull AutofillId[] autofillId) {
+        final View[] views = new View[autofillId.length];
         final ArrayList<ViewRootImpl> roots =
                 WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
 
@@ -7627,10 +7656,11 @@
             final View rootView = roots.get(rootNum).getView();
 
             if (rootView != null) {
-                for (int viewNum = 0; viewNum < viewIds.length; viewNum++) {
+                final int viewCount = autofillId.length;
+                for (int viewNum = 0; viewNum < viewCount; viewNum++) {
                     if (views[viewNum] == null) {
                         views[viewNum] = rootView.findViewByAutofillIdTraversal(
-                                viewIds[viewNum]);
+                                autofillId[viewNum].getViewId());
                     }
                 }
             }
@@ -7641,14 +7671,15 @@
 
     /** @hide */
     @Override
-    @Nullable public View findViewByAutofillIdTraversal(int viewId) {
+    @Nullable
+    public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
         final ArrayList<ViewRootImpl> roots =
                 WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
         for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
             final View rootView = roots.get(rootNum).getView();
 
             if (rootView != null) {
-                final View view = rootView.findViewByAutofillIdTraversal(viewId);
+                final View view = rootView.findViewByAutofillIdTraversal(autofillId.getViewId());
                 if (view != null) {
                     return view;
                 }
@@ -7660,50 +7691,62 @@
 
     /** @hide */
     @Override
-    @NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
-        final boolean[] isVisible = new boolean[viewIds.length];
-        final View views[] = findViewsByAutofillIdTraversal(viewIds);
-
-        for (int i = 0; i < viewIds.length; i++) {
-            View view = views[i];
-            if (view == null) {
-                isVisible[i] = false;
-                continue;
-            }
-
-            isVisible[i] = true;
-
-            // Check if the view is visible by checking all parents
-            while (true) {
-                if (view instanceof DecorView && view.getViewRootImpl() == view.getParent()) {
-                    break;
-                }
-
-                if (view.getVisibility() != View.VISIBLE) {
-                    isVisible[i] = false;
-                    break;
-                }
-
-                if (view.getParent() instanceof View) {
-                    view = (View) view.getParent();
+    public final @NonNull boolean[] autofillClientGetViewVisibility(
+            @NonNull AutofillId[] autofillIds) {
+        final int autofillIdCount = autofillIds.length;
+        final boolean[] visible = new boolean[autofillIdCount];
+        for (int i = 0; i < autofillIdCount; i++) {
+            final AutofillId autofillId = autofillIds[i];
+            final View view = autofillClientFindViewByAutofillIdTraversal(autofillId);
+            if (view != null) {
+                if (!autofillId.isVirtual()) {
+                    visible[i] = view.isVisibleToUser();
                 } else {
-                    break;
+                    visible[i] = view.isVisibleToUserForAutofill(autofillId.getVirtualChildId());
                 }
             }
         }
+        return visible;
+    }
 
-        return isVisible;
+    /** @hide */
+    public final @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId,
+            int windowId) {
+        final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance()
+                .getRootViews(getActivityToken());
+        for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+            final View rootView = roots.get(rootNum).getView();
+            if (rootView != null && rootView.getAccessibilityWindowId() == windowId) {
+                final View view = rootView.findViewByAccessibilityIdTraversal(viewId);
+                if (view != null) {
+                    return view;
+                }
+            }
+        }
+        return null;
     }
 
     /** @hide */
     @Override
-    public boolean isVisibleForAutofill() {
+    public final @Nullable IBinder autofillClientGetActivityToken() {
+        return getActivityToken();
+    }
+
+    /** @hide */
+    @Override
+    public final boolean autofillClientIsVisibleForAutofill() {
         return !mStopped;
     }
 
     /** @hide */
     @Override
-    public boolean isDisablingEnterExitEventForAutofill() {
+    public final boolean autofillIsCompatibilityModeEnabled() {
+        return isAutofillCompatibilityEnabled();
+    }
+
+    /** @hide */
+    @Override
+    public final boolean isDisablingEnterExitEventForAutofill() {
         return mAutoFillIgnoreFirstResumePause || !mResumed;
     }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 42825f0..3d088ff 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -637,6 +637,8 @@
         /** Initial values for {@link Profiler}. */
         ProfilerInfo initProfilerInfo;
 
+        boolean autofillCompatibilityEnabled;
+
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
@@ -863,7 +865,7 @@
                 boolean enableBinderTracking, boolean trackAllocation,
                 boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                 CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
-                String buildSerial) {
+                String buildSerial, boolean autofillCompatibilityEnabled) {
 
             if (services != null) {
                 // Setup the service cache in the ServiceManager
@@ -889,6 +891,7 @@
             data.compatInfo = compatInfo;
             data.initProfilerInfo = profilerInfo;
             data.buildSerial = buildSerial;
+            data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
             sendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -5840,6 +5843,10 @@
             // If the app is being launched for full backup or restore, bring it up in
             // a restricted environment with the base application class.
             app = data.info.makeApplication(data.restrictedBackupMode, null);
+
+            // Propagate autofill compat state
+            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
+
             mInitialApplication = app;
 
             // don't bring up providers in restricted mode; they may depend on the
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index a13ac49..81cbbca 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.view.autofill.AutofillManager;
 
 /**
  * Base class for maintaining global application state. You can provide your own
@@ -299,4 +300,37 @@
             }
         }
     }
+
+    /** @hide */
+    @Override
+    public AutofillManager.AutofillClient getAutofillClient() {
+        final AutofillManager.AutofillClient client = super.getAutofillClient();
+        if (client != null) {
+            return client;
+        }
+        // Okay, ppl use the application context when they should not. This breaks
+        // autofill among other things. We pick the focused activity since autofill
+        // interacts only with the currently focused activity and we need the fill
+        // client only if a call comes from the focused activity. Sigh...
+        final ActivityThread activityThread = ActivityThread.currentActivityThread();
+        if (activityThread == null) {
+            return null;
+        }
+        final int activityCount = activityThread.mActivities.size();
+        for (int i = 0; i < activityCount; i++) {
+            final ActivityThread.ActivityClientRecord record =
+                    activityThread.mActivities.valueAt(i);
+            if (record == null) {
+                continue;
+            }
+            final Activity activity = record.activity;
+            if (activity == null) {
+                continue;
+            }
+            if (activity.getWindow().getDecorView().hasFocus()) {
+                return record.activity;
+            }
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6496110..b11ed69 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -187,6 +187,7 @@
     private @Nullable String mSplitName = null;
 
     private AutofillClient mAutofillClient = null;
+    private boolean mIsAutofillCompatEnabled;
 
     private final Object mSync = new Object();
 
@@ -2251,6 +2252,18 @@
         mAutofillClient = client;
     }
 
+    /** @hide */
+    @Override
+    public boolean isAutofillCompatibilityEnabled() {
+        return mIsAutofillCompatEnabled;
+    }
+
+    /** @hide */
+    @Override
+    public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+        mIsAutofillCompatEnabled = autofillCompatEnabled;
+    }
+
     static ContextImpl createSystemContext(ActivityThread mainThread) {
         LoadedApk packageInfo = new LoadedApk(mainThread);
         ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 893a41c..9e99a78 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -67,7 +67,7 @@
             int debugMode, boolean enableBinderTracking, boolean trackAllocation,
             boolean restrictedBackupMode, boolean persistent, in Configuration config,
             in CompatibilityInfo compatInfo, in Map services,
-            in Bundle coreSettings, in String buildSerial);
+            in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled);
     void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
     void scheduleExit();
     void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 87f2271..8f49bc1 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -4,6 +4,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.ComponentName;
+import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -500,9 +501,8 @@
             ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
             if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
                 if (forAutoFill) {
-                    final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
-                            ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
-                    view.onProvideAutofillStructure(builder, autofillFlags);
+                    final int viewFlags = resolveViewAutofillFlags(view.getContext(), flags);
+                    view.onProvideAutofillStructure(builder, viewFlags);
                 } else {
                     // This is a secure window, so it doesn't want a screenshot, and that
                     // means we should also not copy out its view hierarchy for Assist
@@ -512,9 +512,8 @@
                 }
             }
             if (forAutoFill) {
-                final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
-                        ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
-                view.dispatchProvideAutofillStructure(builder, autofillFlags);
+                final int viewFlags = resolveViewAutofillFlags(view.getContext(), flags);
+                view.dispatchProvideAutofillStructure(builder, viewFlags);
             } else {
                 view.dispatchProvideStructure(builder);
             }
@@ -532,6 +531,12 @@
             mRoot = new ViewNode(reader, 0);
         }
 
+        int resolveViewAutofillFlags(Context context, int fillRequestFlags) {
+            return (fillRequestFlags & FillRequest.FLAG_MANUAL_REQUEST) != 0
+                        || context.isAutofillCompatibilityEnabled()
+                    ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
+        }
+
         void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
             out.writeInt(mX);
             out.writeInt(mY);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f2eb4a0..1a2036b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4909,6 +4909,19 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean isAutofillCompatibilityEnabled() {
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+    }
+
+    /**
      * Throws an exception if the Context is using system resources,
      * which are non-runtime-overlay-themable and may show inconsistent UI.
      * @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 8c1293e..a788989 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -994,4 +994,20 @@
     public void setAutofillClient(AutofillClient client) {
         mBase.setAutofillClient(client);
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isAutofillCompatibilityEnabled() {
+        return mBase.isAutofillCompatibilityEnabled();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void setAutofillCompatibilityEnabled(boolean  autofillCompatEnabled) {
+        mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ad0ce49..a7463fa 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11344,6 +11344,15 @@
                 "chained_battery_attribution_enabled";
 
         /**
+         * The packages whitelisted to be run in autofill compatibility mode.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String AUTOFILL_COMPAT_ALLOWED_PACKAGES =
+                "autofill_compat_allowed_packages";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
@@ -11885,8 +11894,6 @@
           */
         public static final String MULTI_SIM_SMS_PROMPT = "multi_sim_sms_prompt";
 
-
-
         /** User preferred subscriptions setting.
           * This holds the details of the user selected subscription from the card and
           * the activation status. Each settings string have the comma separated values
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 12aa64e..7a304c7 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -490,8 +490,45 @@
  * strong suspicious that it could. For example, if an activity has four or more fields and one of
  * them is a list, chances are that these are address fields (like address, city, state, and
  * zip code).
+ *
+ * <a name="CompatibilityMode"></a>
+ * <h3>Compatibility mode</h3>
+ *
+ * <p>Apps that use standard Android widgets support autofill out-of-the-box and need to do
+ * very little to improve their user experience (annotating autofillable views and providing
+ * autofill hints). However, some apps do their own rendering and the rendered content may
+ * contain semantic structure that needs to be surfaced to the autofill framework. The platform
+ * exposes APIs to achieve this, however it could take some time until these apps implement
+ * autofill support.
+ *
+ * <p>To enable autofill for such apps the platform provides a compatibility mode in which the
+ * platform would fall back to the accessibility APIs to generate the state reported to autofill
+ * services and fill data. This mode needs to be explicitly requested for a given package up
+ * to a specified max version code allowing clean migration path when the target app begins to
+ * support autofill natively. Note that enabling compatibility may degrade performance for the
+ * target package and should be used with caution. The platform supports whitelisting which packages
+ * can be targeted in compatibility mode to ensure this mode is used only when needed and as long
+ * as needed.
+ *
+ * <p>You can request compatibility mode for packages of interest in the meta-data resource
+ * associated with your service. Below is a sample service declaration:
+ *
+ * <pre> &lt;service android:name=".MyAutofillService"
+ *              android:permission="android.permission.BIND_AUTOFILL_SERVICE"&gt;
+ *     &lt;intent-filter&gt;
+ *         &lt;action android:name="android.service.autofill.AutofillService" /&gt;
+ *     &lt;/intent-filter&gt;
+ *     &lt;meta-data android:name="android.autofillservice" android:resource="@xml/autofillservice" /&gt;
+ * &lt;/service&gt;</pre>
+ *
+ * <P>In the XML file you can specify one or more packages for which to enable compatibility
+ * mode. Below is a sample meta-data declaration:
+ *
+ * <pre> &lt;autofill-service xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ *     &lt;compatibility-package android:name="foo.bar.baz" android:maxLongVersionCode="1000000000"/&gt;
+ * &lt;/autofill-service&gt;</pre>
  */
-// TODO(b/70407264): add code snippets above???
+// TODO(b/70407264): add code snippets to field classification ???
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
 
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index 4f2f6cb..29c6cea 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
@@ -27,6 +28,8 @@
 import android.content.res.XmlResourceParser;
 import android.metrics.LogMaker;
 import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
@@ -35,11 +38,13 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
+import com.android.internal.util.XmlUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-
 import java.io.IOException;
+import java.util.Map;
 
 /**
  * {@link ServiceInfo} and meta-data about an {@link AutofillService}.
@@ -49,6 +54,9 @@
 public final class AutofillServiceInfo {
     private static final String TAG = "AutofillServiceInfo";
 
+    private static final String TAG_AUTOFILL_SERVICE = "autofill-service";
+    private static final String TAG_COMPATIBILITY_PACKAGE = "compatibility-package";
+
     private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
         try {
@@ -70,29 +78,15 @@
     @Nullable
     private final String mSettingsActivity;
 
-    public AutofillServiceInfo(PackageManager pm, ComponentName comp, int userHandle)
-            throws PackageManager.NameNotFoundException {
-        this(pm, getServiceInfoOrThrow(comp, userHandle));
-    }
-
-    public AutofillServiceInfo(PackageManager pm, ServiceInfo si) {
-        mServiceInfo = si;
-        final TypedArray metaDataArray = getMetaDataArray(pm, si);
-        if (metaDataArray != null) {
-            mSettingsActivity = metaDataArray
-                    .getString(R.styleable.AutofillService_settingsActivity);
-            metaDataArray.recycle();
-        } else {
-            mSettingsActivity = null;
-        }
-    }
-
-    /**
-     * Gets the meta-data as a {@link TypedArray}, or {@code null} if not provided,
-     * or throws if invalid.
-     */
     @Nullable
-    private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
+    private final Map<String, Long> mCompatibilityPackages;
+
+    public AutofillServiceInfo(Context context, ComponentName comp, int userHandle)
+            throws PackageManager.NameNotFoundException {
+        this(context, getServiceInfoOrThrow(comp, userHandle));
+    }
+
+    public AutofillServiceInfo(Context context, ServiceInfo si) {
         // Check for permissions.
         if (!Manifest.permission.BIND_AUTOFILL_SERVICE.equals(si.permission)) {
             if (Manifest.permission.BIND_AUTOFILL.equals(si.permission)) {
@@ -111,45 +105,115 @@
             }
         }
 
+        mServiceInfo = si;
+
         // Get the AutoFill metadata, if declared.
-        XmlResourceParser parser = si.loadXmlMetaData(pm, AutofillService.SERVICE_META_DATA);
+        final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(),
+                AutofillService.SERVICE_META_DATA);
         if (parser == null) {
-            return null;
+            mSettingsActivity = null;
+            mCompatibilityPackages = null;
+            return;
         }
 
-        // Parse service info and get the <autofill-service> tag as an AttributeSet.
-        AttributeSet attrs;
+        String settingsActivity = null;
+        Map<String, Long> compatibilityPackages = null;
+
         try {
-            // Move the XML parser to the first tag.
-            try {
-                int type;
-                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                        && type != XmlPullParser.START_TAG) {
+            final Resources resources = context.getPackageManager().getResourcesForApplication(
+                    si.applicationInfo);
+
+            int type = 0;
+            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+                type = parser.next();
+            }
+
+            if (TAG_AUTOFILL_SERVICE.equals(parser.getName())) {
+                final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+                TypedArray afsAttributes = null;
+                try {
+                    afsAttributes = resources.obtainAttributes(allAttributes,
+                            com.android.internal.R.styleable.AutofillService);
+                    settingsActivity = afsAttributes.getString(
+                            R.styleable.AutofillService_settingsActivity);
+                } finally {
+                    if (afsAttributes != null) {
+                        afsAttributes.recycle();
+                    }
                 }
-            } catch (XmlPullParserException | IOException e) {
-                Log.e(TAG, "Error parsing auto fill service meta-data", e);
-                return null;
-            }
-
-            if (!"autofill-service".equals(parser.getName())) {
+                compatibilityPackages = parseCompatibilityPackages(parser, resources);
+            } else {
                 Log.e(TAG, "Meta-data does not start with autofill-service tag");
-                return null;
             }
-            attrs = Xml.asAttributeSet(parser);
-
-            // Get resources required to read the AttributeSet.
-            Resources res;
-            try {
-                res = pm.getResourcesForApplication(si.applicationInfo);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(TAG, "Error getting application resources", e);
-                return null;
-            }
-
-            return res.obtainAttributes(attrs, R.styleable.AutofillService);
-        } finally {
-            parser.close();
+        } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
+            Log.e(TAG, "Error parsing auto fill service meta-data", e);
         }
+
+        mSettingsActivity = settingsActivity;
+        mCompatibilityPackages = compatibilityPackages;
+    }
+
+    private Map<String, Long> parseCompatibilityPackages(XmlPullParser parser, Resources resources)
+            throws IOException, XmlPullParserException {
+        Map<String, Long> compatibilityPackages = null;
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (TAG_COMPATIBILITY_PACKAGE.equals(parser.getName())) {
+                TypedArray cpAttributes = null;
+                try {
+                    final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+
+                    cpAttributes = resources.obtainAttributes(allAttributes,
+                           R.styleable.AutofillService_CompatibilityPackage);
+
+                    final String name = cpAttributes.getString(
+                            R.styleable.AutofillService_CompatibilityPackage_name);
+                    if (TextUtils.isEmpty(name)) {
+                        Log.e(TAG, "Invalid compatibility package:" + name);
+                        break;
+                    }
+
+                    final String maxVersionCodeStr = cpAttributes.getString(
+                            R.styleable.AutofillService_CompatibilityPackage_maxLongVersionCode);
+                    final Long maxVersionCode;
+                    if (maxVersionCodeStr != null) {
+                        try {
+                            maxVersionCode = Long.parseLong(maxVersionCodeStr);
+                        } catch (NumberFormatException e) {
+                            Log.e(TAG, "Invalid compatibility max version code:"
+                                    + maxVersionCodeStr);
+                            break;
+                        }
+                        if (maxVersionCode < 0) {
+                            Log.e(TAG, "Invalid compatibility max version code:"
+                                    + maxVersionCode);
+                            break;
+                        }
+                    } else {
+                        maxVersionCode = Long.MAX_VALUE;
+                    }
+
+                    if (compatibilityPackages == null) {
+                        compatibilityPackages = new ArrayMap<>();
+                    }
+                    compatibilityPackages.put(name, maxVersionCode);
+                } finally {
+                    XmlUtils.skipCurrentTag(parser);
+                    if (cpAttributes != null) {
+                        cpAttributes.recycle();
+                    }
+                }
+            }
+        }
+
+        return compatibilityPackages;
     }
 
     public ServiceInfo getServiceInfo() {
@@ -161,8 +225,26 @@
         return mSettingsActivity;
     }
 
+    @Nullable
+    public boolean isCompatibilityModeRequested(String packageName, long versionCode) {
+        if (mCompatibilityPackages == null) {
+            return false;
+        }
+        final Long maxVersionCode = mCompatibilityPackages.get(packageName);
+        if (maxVersionCode == null) {
+            return false;
+        }
+        return versionCode <= maxVersionCode;
+    }
+
     @Override
     public String toString() {
-        return mServiceInfo == null ? "null" : mServiceInfo.toString();
+        final StringBuilder builder = new StringBuilder();
+        builder.append(getClass().getSimpleName());
+        builder.append("[").append(mServiceInfo);
+        builder.append(", settings:").append(mSettingsActivity);
+        builder.append(", hasCompatPckgs:").append(mCompatibilityPackages != null
+                && !mCompatibilityPackages.isEmpty()).append("]");
+        return builder.toString();
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c3e9e73..79fc134 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -74,6 +74,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.text.InputType;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -7940,12 +7941,22 @@
      * optimal implementation providing this data.
      */
     public void onProvideVirtualStructure(ViewStructure structure) {
-        AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+        onProvideVirtualStructureCompat(structure, false);
+    }
+
+    /**
+     * Fallback implementation to populate a ViewStructure from accessibility state.
+     *
+     * @param structure The structure to populate.
+     * @param forAutofill Whether the structure is needed for autofill.
+     */
+    private void onProvideVirtualStructureCompat(ViewStructure structure, boolean forAutofill) {
+        final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
         if (provider != null) {
-            AccessibilityNodeInfo info = createAccessibilityNodeInfo();
+            final AccessibilityNodeInfo info = createAccessibilityNodeInfo();
             structure.setChildCount(1);
-            ViewStructure root = structure.newChild(0);
-            populateVirtualStructure(root, provider, info);
+            final ViewStructure root = structure.newChild(0);
+            populateVirtualStructure(root, provider, info, forAutofill);
             info.recycle();
         }
     }
@@ -7974,6 +7985,9 @@
      *   <li>Call {@link android.view.autofill.AutofillManager#notifyViewEntered(View, int, Rect)}
      *       and/or {@link android.view.autofill.AutofillManager#notifyViewExited(View, int)}
      *       when the focused virtual child changed.
+     *   <li>Override {@link #isVisibleToUserForAutofill(int)} to allow the platform to query
+     *       whether a given virtual view is visible to the user in order to support triggering
+     *       save when all views of interest go away.
      *   <li>Call
      *    {@link android.view.autofill.AutofillManager#notifyValueChanged(View, int, AutofillValue)}
      *       when the value of a virtual child changed.
@@ -8009,6 +8023,9 @@
      * @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
      */
     public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
+        if (mContext.isAutofillCompatibilityEnabled()) {
+            onProvideVirtualStructureCompat(structure, true);
+        }
     }
 
     /**
@@ -8086,6 +8103,25 @@
      * @attr ref android.R.styleable#Theme_autofilledHighlight
      */
     public void autofill(@NonNull @SuppressWarnings("unused") SparseArray<AutofillValue> values) {
+        if (!mContext.isAutofillCompatibilityEnabled()) {
+            return;
+        }
+        final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+        if (provider == null) {
+            return;
+        }
+        final int valueCount = values.size();
+        for (int i = 0; i < valueCount; i++) {
+            final AutofillValue value = values.valueAt(i);
+            if (value.isText()) {
+                final int virtualId = values.keyAt(i);
+                final CharSequence text = value.getTextValue();
+                final Bundle arguments = new Bundle();
+                arguments.putCharSequence(
+                        AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
+                provider.performAction(virtualId, AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
+            }
+        }
     }
 
     /**
@@ -8339,7 +8375,8 @@
     }
 
     private void populateVirtualStructure(ViewStructure structure,
-            AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
+            AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
+            boolean forAutofill) {
         structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
                 null, null, null);
         Rect rect = structure.getTempRect();
@@ -8374,21 +8411,43 @@
         if (info.isContextClickable()) {
             structure.setContextClickable(true);
         }
+        if (forAutofill) {
+            structure.setAutofillId(new AutofillId(getAutofillId(),
+                    AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
+        }
         CharSequence cname = info.getClassName();
         structure.setClassName(cname != null ? cname.toString() : null);
         structure.setContentDescription(info.getContentDescription());
         if ((info.getText() != null || info.getError() != null)) {
             structure.setText(info.getText(), info.getTextSelectionStart(),
                     info.getTextSelectionEnd());
+            if (forAutofill) {
+                if (info.isEditable()) {
+                    structure.setDataIsSensitive(true);
+                    structure.setAutofillType(AUTOFILL_TYPE_TEXT);
+                    final AutofillValue autofillValue = AutofillValue.forText(structure.getText());
+                    structure.setAutofillValue(autofillValue);
+                    if (info.isPassword()) {
+                        structure.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
+                    }
+                } else {
+                    structure.setDataIsSensitive(false);
+                }
+            }
         }
         final int NCHILDREN = info.getChildCount();
         if (NCHILDREN > 0) {
             structure.setChildCount(NCHILDREN);
             for (int i=0; i<NCHILDREN; i++) {
+                if (AccessibilityNodeInfo.getVirtualDescendantId(info.getChildNodeIds().get(i))
+                        == AccessibilityNodeProvider.HOST_VIEW_ID) {
+                    Log.e(VIEW_LOG_TAG, "Virtual view pointing to its host. Ignoring");
+                    continue;
+                }
                 AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo(
                         AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i)));
                 ViewStructure child = structure.newChild(i);
-                populateVirtualStructure(child, provider, cinfo);
+                populateVirtualStructure(child, provider, cinfo, forAutofill);
                 cinfo.recycle();
             }
         }
@@ -8717,6 +8776,24 @@
     }
 
     /**
+     * Computes whether this virtual autofill view is visible to the user.
+     *
+     * @return Whether the view is visible on the screen.
+     */
+    public boolean isVisibleToUserForAutofill(int virtualId) {
+        if (mContext.isAutofillCompatibilityEnabled()) {
+            final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+            if (provider != null) {
+                final AccessibilityNodeInfo node = provider.createAccessibilityNodeInfo(virtualId);
+                if (node != null) {
+                    return node.isVisibleToUser();
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
      * Computes whether this view is visible to the user. Such a view is
      * attached, visible, all its predecessors are visible, it is not clipped
      * entirely by its predecessors, and has an alpha greater than zero.
@@ -8725,7 +8802,7 @@
      *
      * @hide
      */
-    protected boolean isVisibleToUser() {
+    public boolean isVisibleToUser() {
         return isVisibleToUser(null);
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a8bdb85..273f097 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3555,13 +3555,16 @@
     public void dispatchProvideAutofillStructure(ViewStructure structure,
             @AutofillFlags int flags) {
         super.dispatchProvideAutofillStructure(structure, flags);
+
         if (structure.getChildCount() != 0) {
             return;
         }
 
         if (!isLaidOut()) {
-            Log.v(VIEW_LOG_TAG, "dispatchProvideAutofillStructure(): not laid out, ignoring "
-                    + mChildrenCount + " children of " + getAutofillId());
+            if (Helper.sVerbose) {
+                Log.v(VIEW_LOG_TAG, "dispatchProvideAutofillStructure(): not laid out, ignoring "
+                        + mChildrenCount + " children of " + getAutofillId());
+            }
             return;
         }
 
@@ -5120,6 +5123,20 @@
             // manually assembling the hierarchy, update the ancestor default-focus chain.
             setDefaultFocus(child);
         }
+
+        touchAccessibilityNodeProviderIfNeeded(child);
+    }
+
+    /**
+     * We may need to touch the provider to bring up the a11y layer. In a11y mode
+     * clients inspect the screen or the user touches it which triggers bringing up
+     * of the a11y infrastructure while in autofill mode we want the infra up and
+     * running from the beginning since we watch for a11y events to drive autofill.
+     */
+    private void touchAccessibilityNodeProviderIfNeeded(View child) {
+        if (mContext.isAutofillCompatibilityEnabled()) {
+            child.getAccessibilityNodeProvider();
+        }
     }
 
     private void addInArray(View child, int index) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7bd197e..810864e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -71,6 +71,7 @@
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.LongArray;
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -114,6 +115,8 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -2569,6 +2572,10 @@
                         ~WindowManager.LayoutParams
                                 .SOFT_INPUT_IS_FORWARD_NAVIGATION;
                 mHasHadWindowFocus = true;
+
+                // Refocusing a window that has a focused view should fire a
+                // focus event for the view since the global focused view changed.
+                fireAccessibilityFocusEventIfHasFocusedNode();
             } else {
                 if (mPointerCapture) {
                     handlePointerCaptureChanged(false);
@@ -2578,6 +2585,86 @@
         mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
     }
 
+    private void fireAccessibilityFocusEventIfHasFocusedNode() {
+        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+            return;
+        }
+        final View focusedView = mView.findFocus();
+        if (focusedView == null) {
+            return;
+        }
+        final AccessibilityNodeProvider provider = focusedView.getAccessibilityNodeProvider();
+        if (provider == null) {
+            focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+        } else {
+            final AccessibilityNodeInfo focusedNode = findFocusedVirtualNode(provider);
+            if (focusedNode != null) {
+                final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(
+                        focusedNode.getSourceNodeId());
+                // This is a best effort since clearing and setting the focus via the
+                // provider APIs could have side effects. We don't have a provider API
+                // similar to that on View to ask a given event to be fired.
+                final AccessibilityEvent event = AccessibilityEvent.obtain(
+                        AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                event.setSource(focusedView, virtualId);
+                event.setPackageName(focusedNode.getPackageName());
+                event.setChecked(focusedNode.isChecked());
+                event.setContentDescription(focusedNode.getContentDescription());
+                event.setPassword(focusedNode.isPassword());
+                event.getText().add(focusedNode.getText());
+                event.setEnabled(focusedNode.isEnabled());
+                focusedView.getParent().requestSendAccessibilityEvent(focusedView, event);
+                focusedNode.recycle();
+            }
+        }
+    }
+
+    private AccessibilityNodeInfo findFocusedVirtualNode(AccessibilityNodeProvider provider) {
+        AccessibilityNodeInfo focusedNode = provider.findFocus(
+                AccessibilityNodeInfo.FOCUS_INPUT);
+        if (focusedNode != null) {
+            return focusedNode;
+        }
+
+        if (!mContext.isAutofillCompatibilityEnabled()) {
+            return null;
+        }
+
+        // Unfortunately some provider implementations don't properly
+        // implement AccessibilityNodeProvider#findFocus
+        AccessibilityNodeInfo current = provider.createAccessibilityNodeInfo(
+                AccessibilityNodeProvider.HOST_VIEW_ID);
+        if (current.isFocused()) {
+            return current;
+        }
+
+        final Queue<AccessibilityNodeInfo> fringe = new LinkedList<>();
+        fringe.offer(current);
+
+        while (!fringe.isEmpty()) {
+            current = fringe.poll();
+            final LongArray childNodeIds = current.getChildNodeIds();
+            if (childNodeIds== null || childNodeIds.size() <= 0) {
+                continue;
+            }
+            final int childCount = childNodeIds.size();
+            for (int i = 0; i < childCount; i++) {
+                final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(
+                        childNodeIds.get(i));
+                final AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(virtualId);
+                if (child != null) {
+                    if (child.isFocused()) {
+                        return child;
+                    }
+                    fringe.offer(child);
+                }
+            }
+            current.recycle();
+        }
+
+        return null;
+    }
+
     private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
         Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
         try {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index b4499d1..d6831fb6 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -16,10 +16,9 @@
 
 package android.view.accessibility;
 
-import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
-
 import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -44,7 +43,7 @@
 import android.util.SparseArray;
 import android.view.IWindow;
 import android.view.View;
-
+import android.view.accessibility.AccessibilityEvent.EventType;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IntPair;
 
@@ -52,6 +51,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
+
 /**
  * System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
  * and provides facilities for querying the accessibility state of the system.
@@ -132,6 +133,8 @@
 
     boolean mIsHighTextContrastEnabled;
 
+    AccessibilityPolicy mAccessibilityPolicy;
+
     private final ArrayMap<AccessibilityStateChangeListener, Handler>
             mAccessibilityStateChangeListeners = new ArrayMap<>();
 
@@ -215,6 +218,60 @@
         void onHighTextContrastStateChanged(boolean enabled);
     }
 
+    /**
+     * Policy to inject behavior into the accessibility manager.
+     *
+     * @hide
+     */
+    public interface AccessibilityPolicy {
+        /**
+         * Checks whether accessibility is enabled.
+         *
+         * @param accessibilityEnabled Whether the accessibility layer is enabled.
+         * @return whether accessibility is enabled.
+         */
+        boolean isEnabled(boolean accessibilityEnabled);
+
+        /**
+         * Notifies the policy for an accessibility event.
+         *
+         * @param event The event.
+         * @param accessibilityEnabled Whether the accessibility layer is enabled.
+         * @param relevantEventTypes The events relevant events.
+         * @return The event to dispatch or null.
+         */
+        @Nullable AccessibilityEvent onAccessibilityEvent(@NonNull AccessibilityEvent event,
+                boolean accessibilityEnabled, @EventType int relevantEventTypes);
+
+        /**
+         * Gets the list of relevant events.
+         *
+         * @param relevantEventTypes The relevant events.
+         * @return The relevant events to report.
+         */
+        @EventType int getRelevantEventTypes(@EventType int relevantEventTypes);
+
+        /**
+         * Gets the list of installed services to report.
+         *
+         * @param installedService The installed services.
+         * @return The services to report.
+         */
+        @NonNull List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
+                @Nullable List<AccessibilityServiceInfo> installedService);
+
+        /**
+         * Gets the list of enabled accessibility services.
+         *
+         * @param feedbackTypeFlags The feedback type to query for.
+         * @param enabledService The enabled services.
+         * @return The services to report.
+         */
+        @Nullable List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+                @FeedbackType int feedbackTypeFlags,
+                @Nullable List<AccessibilityServiceInfo> enabledService);
+    }
+
     private final IAccessibilityManagerClient.Stub mClient =
             new IAccessibilityManagerClient.Stub() {
         @Override
@@ -341,11 +398,8 @@
      */
     public boolean isEnabled() {
         synchronized (mLock) {
-            IAccessibilityManager service = getServiceLocked();
-            if (service == null) {
-                return false;
-            }
-            return mIsEnabled;
+            return mIsEnabled || (mAccessibilityPolicy != null
+                    && mAccessibilityPolicy.isEnabled(mIsEnabled));
         }
     }
 
@@ -401,12 +455,23 @@
     public void sendAccessibilityEvent(AccessibilityEvent event) {
         final IAccessibilityManager service;
         final int userId;
+        final AccessibilityEvent dispatchedEvent;
         synchronized (mLock) {
             service = getServiceLocked();
             if (service == null) {
                 return;
             }
-            if (!mIsEnabled) {
+            event.setEventTime(SystemClock.uptimeMillis());
+            if (mAccessibilityPolicy != null) {
+                dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event,
+                        mIsEnabled, mRelevantEventTypes);
+                if (dispatchedEvent == null) {
+                    return;
+                }
+            } else {
+                dispatchedEvent = event;
+            }
+            if (!isEnabled()) {
                 Looper myLooper = Looper.myLooper();
                 if (myLooper == Looper.getMainLooper()) {
                     throw new IllegalStateException(
@@ -420,9 +485,9 @@
                     return;
                 }
             }
-            if ((event.getEventType() & mRelevantEventTypes) == 0) {
+            if ((dispatchedEvent.getEventType() & mRelevantEventTypes) == 0) {
                 if (DEBUG) {
-                    Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event
+                    Log.i(LOG_TAG, "Not dispatching irrelevant event: " + dispatchedEvent
                             + " that is not among "
                             + AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
                 }
@@ -431,23 +496,25 @@
             userId = mUserId;
         }
         try {
-            event.setEventTime(SystemClock.uptimeMillis());
             // it is possible that this manager is in the same process as the service but
             // client using it is called through Binder from another process. Example: MMS
             // app adds a SMS notification and the NotificationManagerService calls this method
             long identityToken = Binder.clearCallingIdentity();
             try {
-                service.sendAccessibilityEvent(event, userId);
+                service.sendAccessibilityEvent(dispatchedEvent, userId);
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
             }
             if (DEBUG) {
-                Log.i(LOG_TAG, event + " sent");
+                Log.i(LOG_TAG, dispatchedEvent + " sent");
             }
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error during sending " + event + " ", re);
+            Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re);
         } finally {
-            event.recycle();
+            if (event != dispatchedEvent) {
+                event.recycle();
+            }
+            dispatchedEvent.recycle();
         }
     }
 
@@ -462,7 +529,7 @@
             if (service == null) {
                 return;
             }
-            if (!mIsEnabled) {
+            if (!isEnabled()) {
                 Looper myLooper = Looper.myLooper();
                 if (myLooper == Looper.getMainLooper()) {
                     throw new IllegalStateException(
@@ -532,6 +599,9 @@
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
         }
+        if (mAccessibilityPolicy != null) {
+            services = mAccessibilityPolicy.getInstalledAccessibilityServiceList(services);
+        }
         if (services != null) {
             return Collections.unmodifiableList(services);
         } else {
@@ -574,6 +644,10 @@
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
         }
+        if (mAccessibilityPolicy != null) {
+            services = mAccessibilityPolicy.getEnabledAccessibilityServiceList(
+                    feedbackTypeFlags, services);
+        }
         if (services != null) {
             return Collections.unmodifiableList(services);
         } else {
@@ -783,6 +857,19 @@
     }
 
     /**
+     * Sets the {@link AccessibilityPolicy} controlling this manager.
+     *
+     * @param policy The policy.
+     *
+     * @hide
+     */
+    public void setAccessibilityPolicy(@Nullable AccessibilityPolicy policy) {
+        synchronized (mLock) {
+            mAccessibilityPolicy = policy;
+        }
+    }
+
+    /**
      * Check if the accessibility volume stream is active.
      *
      * @return True if accessibility volume is active (i.e. some service has requested it). False
@@ -834,7 +921,7 @@
         final boolean highTextContrastEnabled =
                 (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
 
-        final boolean wasEnabled = mIsEnabled;
+        final boolean wasEnabled = isEnabled();
         final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
         final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
 
@@ -843,7 +930,7 @@
         mIsTouchExplorationEnabled = touchExplorationEnabled;
         mIsHighTextContrastEnabled = highTextContrastEnabled;
 
-        if (wasEnabled != enabled) {
+        if (wasEnabled != isEnabled()) {
             notifyAccessibilityStateChanged();
         }
 
@@ -1052,16 +1139,15 @@
             if (mAccessibilityStateChangeListeners.isEmpty()) {
                 return;
             }
-            isEnabled = mIsEnabled;
+            isEnabled = isEnabled();
             listeners = new ArrayMap<>(mAccessibilityStateChangeListeners);
         }
 
-        int numListeners = listeners.size();
+        final int numListeners = listeners.size();
         for (int i = 0; i < numListeners; i++) {
-            final AccessibilityStateChangeListener listener =
-                    mAccessibilityStateChangeListeners.keyAt(i);
-            mAccessibilityStateChangeListeners.valueAt(i)
-                    .post(() -> listener.onAccessibilityStateChanged(isEnabled));
+            final AccessibilityStateChangeListener listener = listeners.keyAt(i);
+            listeners.valueAt(i).post(() ->
+                    listener.onAccessibilityStateChanged(isEnabled));
         }
     }
 
@@ -1079,12 +1165,11 @@
             listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners);
         }
 
-        int numListeners = listeners.size();
+        final int numListeners = listeners.size();
         for (int i = 0; i < numListeners; i++) {
-            final TouchExplorationStateChangeListener listener =
-                    mTouchExplorationStateChangeListeners.keyAt(i);
-            mTouchExplorationStateChangeListeners.valueAt(i)
-                    .post(() -> listener.onTouchExplorationStateChanged(isTouchExplorationEnabled));
+            final TouchExplorationStateChangeListener listener = listeners.keyAt(i);
+            listeners.valueAt(i).post(() ->
+                    listener.onTouchExplorationStateChanged(isTouchExplorationEnabled));
         }
     }
 
@@ -1102,12 +1187,11 @@
             listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners);
         }
 
-        int numListeners = listeners.size();
+        final int numListeners = listeners.size();
         for (int i = 0; i < numListeners; i++) {
-            final HighTextContrastChangeListener listener =
-                    mHighTextContrastStateChangeListeners.keyAt(i);
-            mHighTextContrastStateChangeListeners.valueAt(i)
-                    .post(() -> listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
+            final HighTextContrastChangeListener listener = listeners.keyAt(i);
+            listeners.valueAt(i).post(() ->
+                    listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
         }
     }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index dac1998..63a9990 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,10 +16,7 @@
 
 package android.view.autofill;
 
-import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
-import static android.view.autofill.Helper.sDebug;
-import static android.view.autofill.Helper.sVerbose;
-
+import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,6 +25,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.metrics.LogMaker;
 import android.os.Bundle;
@@ -41,13 +40,22 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.Choreographer;
 import android.view.View;
-
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.AccessibilityWindowInfo;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+import org.xmlpull.v1.XmlPullParserException;
+import sun.misc.Cleaner;
 
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -58,8 +66,11 @@
 import java.util.List;
 import java.util.Objects;
 
+import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.view.autofill.Helper.sDebug;
+import static android.view.autofill.Helper.sVerbose;
+
 // TODO: use java.lang.ref.Cleaner once Android supports Java 9
-import sun.misc.Cleaner;
 
 /**
  * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
@@ -178,7 +189,6 @@
     private static final String STATE_TAG = "android:state";
     private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
 
-
     /** @hide */ public static final int ACTION_START_SESSION = 1;
     /** @hide */ public static final int ACTION_VIEW_ENTERED =  2;
     /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
@@ -355,6 +365,10 @@
     @GuardedBy("mLock")
     private boolean mSaveOnFinish;
 
+    /** If compatibility mode is enabled - this is a bridge to interact with a11y */
+    @GuardedBy("mLock")
+    private CompatibilityBridge mCompatibilityBridge;
+
     /** @hide */
     public interface AutofillClient {
         /**
@@ -364,13 +378,13 @@
          * @param intent The authentication intent.
          * @param fillInIntent The authentication fill-in intent.
          */
-        void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
+        void autofillClientAuthenticate(int authenticationId, IntentSender intent,
                 Intent fillInIntent);
 
         /**
          * Tells the client this manager has state to be reset.
          */
-        void autofillCallbackResetableStateAvailable();
+        void autofillClientResetableStateAvailable();
 
         /**
          * Request showing the autofill UI.
@@ -382,7 +396,7 @@
          * @param presenter The presenter that controls the fill UI window.
          * @return Whether the UI was shown.
          */
-        boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
+        boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
                 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
 
         /**
@@ -390,21 +404,28 @@
          *
          * @return Whether the UI was hidden.
          */
-        boolean autofillCallbackRequestHideFillUi();
+        boolean autofillClientRequestHideFillUi();
+
+        /**
+         * Gets whether the fill UI is currenlty being shown.
+         *
+         * @return Whether the fill UI is currently being shown
+         */
+        boolean autofillClientIsFillUiShowing();
 
         /**
          * Checks if views are currently attached and visible.
          *
          * @return And array with {@code true} iff the view is attached or visible
          */
-        @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
+        @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
 
         /**
          * Checks is the client is currently visible as understood by autofill.
          *
          * @return {@code true} if the client is currently visible
          */
-        boolean isVisibleForAutofill();
+        boolean autofillClientIsVisibleForAutofill();
 
         /**
          * Client might disable enter/exit event e.g. when activity is paused.
@@ -414,30 +435,51 @@
         /**
          * Finds views by traversing the hierarchies of the client.
          *
-         * @param viewIds The autofill ids of the views to find
+         * @param autofillIds The autofill ids of the views to find
          *
          * @return And array containing the views (empty if no views found).
          */
-        @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
+        @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
+                @NonNull AutofillId[] autofillIds);
 
         /**
          * Finds a view by traversing the hierarchies of the client.
          *
-         * @param viewId The autofill id of the views to find
+         * @param autofillId The autofill id of the views to find
          *
          * @return The view, or {@code null} if not found
          */
-        @Nullable View findViewByAutofillIdTraversal(int viewId);
+        @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
+
+        /**
+         * Finds a view by a11y id in a given client window.
+         *
+         * @param viewId The accessibility id of the views to find
+         * @param windowId The accessibility window id where to search
+         *
+         * @return The view, or {@code null} if not found
+         */
+        @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
 
         /**
          * Runs the specified action on the UI thread.
          */
-        void runOnUiThread(Runnable action);
+        void autofillClientRunOnUiThread(Runnable action);
 
         /**
          * Gets the complete component name of this client.
          */
-        ComponentName getComponentName();
+        ComponentName autofillClientGetComponentName();
+
+        /**
+         * Gets the activity token
+         */
+        @Nullable IBinder autofillClientGetActivityToken();
+
+        /**
+          * @return Whether compatibility mode is enabled.
+          */
+        boolean autofillIsCompatibilityModeEnabled();
     }
 
     /**
@@ -449,6 +491,19 @@
     }
 
     /**
+     * @hide
+     */
+    public void enableCompatibilityMode() {
+        synchronized (mLock) {
+            // The accessibility manager is a singleton so we may need to plug
+            // different bridge based on which activity is currently focused
+            // in the current process. Since compat would be rarely used, just
+            // create and register a new instance every time.
+            mCompatibilityBridge = new CompatibilityBridge();
+        }
+    }
+
+    /**
      * Restore state after activity lifecycle
      *
      * @param savedInstanceState The state to be restored
@@ -477,7 +532,8 @@
                 if (client != null) {
                     try {
                         final boolean sessionWasRestored = mService.restoreSession(mSessionId,
-                                mContext.getActivityToken(), mServiceClient.asBinder());
+                                client.autofillClientGetActivityToken(),
+                                mServiceClient.asBinder());
 
                         if (!sessionWasRestored) {
                             Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -488,7 +544,7 @@
                                 Log.d(TAG, "session " + mSessionId + " was restored");
                             }
 
-                            client.autofillCallbackResetableStateAvailable();
+                            client.autofillClientResetableStateAvailable();
                         }
                     } catch (RemoteException e) {
                         Log.e(TAG, "Could not figure out if there was an autofill session", e);
@@ -501,22 +557,29 @@
     /**
      * Called once the client becomes visible.
      *
-     * @see AutofillClient#isVisibleForAutofill()
+     * @see AutofillClient#autofillClientIsVisibleForAutofill()
      *
      * {@hide}
      */
     public void onVisibleForAutofill() {
-        synchronized (mLock) {
-            if (mEnabled && isActiveLocked() && mTrackedViews != null) {
-                mTrackedViews.onVisibleForAutofillLocked();
+        // This gets called when the client just got visible at which point the visibility
+        // of the tracked views may not have been computed (due to a pending layout, etc).
+        // While generally we have no way to know when the UI has settled. We will evaluate
+        // the tracked views state at the end of next frame to guarantee that everything
+        // that may need to be laid out is laid out.
+        Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
+            synchronized (mLock) {
+                if (mEnabled && isActiveLocked() && mTrackedViews != null) {
+                    mTrackedViews.onVisibleForAutofillChangedLocked();
+                }
             }
-        }
+        }, null);
     }
 
     /**
      * Called once the client becomes invisible.
      *
-     * @see AutofillClient#isVisibleForAutofill()
+     * @see AutofillClient#autofillClientIsVisibleForAutofill()
      *
      * {@hide}
      */
@@ -551,6 +614,15 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean isCompatibilityModeEnabled() {
+        synchronized (mLock) {
+            return mCompatibilityBridge != null;
+        }
+    }
+
+    /**
      * Checks whether autofill is enabled for the current user.
      *
      * <p>Typically used to determine whether the option to explicitly request autofill should
@@ -653,7 +725,7 @@
 
     private boolean isClientVisibleForAutofillLocked() {
         final AutofillClient client = getClient();
-        return client != null && client.isVisibleForAutofill();
+        return client != null && client.autofillClientIsVisibleForAutofill();
     }
 
     private boolean isClientDisablingEnterExitEvent() {
@@ -1322,13 +1394,13 @@
             final AutofillClient client = getClient();
             if (client == null) return; // NOTE: getClient() already logd it..
 
-            mSessionId = mService.startSession(mContext.getActivityToken(),
+            mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
-                    mCallback != null, flags, client.getComponentName());
+                    mCallback != null, flags, client.autofillClientGetComponentName());
             if (mSessionId != NO_SESSION) {
                 mState = STATE_ACTIVE;
             }
-            client.autofillCallbackResetableStateAvailable();
+            client.autofillClientResetableStateAvailable();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1383,14 +1455,16 @@
                 final AutofillClient client = getClient();
                 if (client == null) return; // NOTE: getClient() already logd it..
 
-                final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
+                final int newId = mService.updateOrRestartSession(
+                        client.autofillClientGetActivityToken(),
                         mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
-                        mCallback != null, flags, client.getComponentName(), mSessionId, action);
+                        mCallback != null, flags, client.autofillClientGetComponentName(),
+                        mSessionId, action);
                 if (newId != mSessionId) {
                     if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
                     mSessionId = newId;
                     mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
-                    client.autofillCallbackResetableStateAvailable();
+                    client.autofillClientResetableStateAvailable();
                 }
             } else {
                 mService.updateSession(mSessionId, id, bounds, value, action, flags,
@@ -1489,7 +1563,7 @@
                 AutofillClient client = getClient();
 
                 if (client != null) {
-                    if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
+                    if (client.autofillClientRequestShowFillUi(anchor, width, height,
                             anchorBounds, presenter) && mCallback != null) {
                         callback = mCallback;
                     }
@@ -1516,7 +1590,7 @@
                     // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
                     // before onAuthenticationResult()
                     mOnInvisibleCalled = false;
-                    client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
+                    client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
                 }
             }
         }
@@ -1587,7 +1661,8 @@
             final int itemCount = ids.size();
             int numApplied = 0;
             ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
-            final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
+            final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
+                    Helper.toArray(ids));
 
             for (int i = 0; i < itemCount; i++) {
                 final AutofillId id = ids.get(i);
@@ -1758,7 +1833,7 @@
             //    service being uninstalled and the UI being dismissed.
             AutofillClient client = getClient();
             if (client != null) {
-                if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
+                if (client.autofillClientRequestHideFillUi() && mCallback != null) {
                     callback = mCallback;
                 }
             }
@@ -1807,35 +1882,6 @@
     }
 
     /**
-     * Get an array of viewIds from a List of {@link AutofillId}.
-     *
-     * @param autofillIds The autofill ids to convert
-     *
-     * @return The array of viewIds.
-     */
-    // TODO: move to Helper as static method
-    @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
-        final int numIds = autofillIds.length;
-        final int[] viewIds = new int[numIds];
-        for (int i = 0; i < numIds; i++) {
-            viewIds[i] = autofillIds[i].getViewId();
-        }
-
-        return viewIds;
-    }
-
-    // TODO: move to Helper as static method
-    @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
-        final int numIds = autofillIds.size();
-        final int[] viewIds = new int[numIds];
-        for (int i = 0; i < numIds; i++) {
-            viewIds[i] = autofillIds.get(i).getViewId();
-        }
-
-        return viewIds;
-    }
-
-    /**
      * Find a single view by its id.
      *
      * @param autofillId The autofill id of the view
@@ -1844,12 +1890,10 @@
      */
     private View findView(@NonNull AutofillId autofillId) {
         final AutofillClient client = getClient();
-
-        if (client == null) {
-            return null;
+        if (client != null) {
+            return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
         }
-
-        return client.findViewByAutofillIdTraversal(autofillId.getViewId());
+        return null;
     }
 
     /** @hide */
@@ -1895,6 +1939,7 @@
         pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
         pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
         pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
+        pw.print(pfx); pw.print("compat mode enabled: "); pw.println(isCompatibilityModeEnabled());
         pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
         pw.print(" verbose: "); pw.println(sVerbose);
     }
@@ -1934,7 +1979,218 @@
             if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
             return;
         }
-        client.runOnUiThread(runnable);
+        client.autofillClientRunOnUiThread(runnable);
+    }
+
+    /**
+     * Implementation of the accessibility based compatibility.
+     */
+    private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
+        @GuardedBy("mLock")
+        private final Rect mFocusedBounds = new Rect();
+        @GuardedBy("mLock")
+        private final Rect mTempBounds = new Rect();
+
+        @GuardedBy("mLock")
+        private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+        @GuardedBy("mLock")
+        private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
+
+        // Need to report a fake service in case a11y clients check the service list
+        @NonNull
+        @GuardedBy("mLock")
+        AccessibilityServiceInfo mCompatServiceInfo;
+
+        CompatibilityBridge() {
+            final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
+            am.setAccessibilityPolicy(this);
+        }
+
+        private AccessibilityServiceInfo getCompatServiceInfo() {
+            synchronized (mLock) {
+                if (mCompatServiceInfo != null) {
+                    return mCompatServiceInfo;
+                }
+                final Intent intent = new Intent();
+                intent.setComponent(new ComponentName("android",
+                        "com.android.server.autofill.AutofillCompatAccessibilityService"));
+                final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
+                        intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
+                try {
+                    mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
+                } catch (XmlPullParserException | IOException e) {
+                    Log.e(TAG, "Cannot find compat autofill service:" + intent);
+                    throw new IllegalStateException("Cannot find compat autofill service");
+                }
+                return mCompatServiceInfo;
+            }
+        }
+
+        @Override
+        public boolean isEnabled(boolean accessibilityEnabled) {
+            return true;
+        }
+
+        @Override
+        public int getRelevantEventTypes(int relevantEventTypes) {
+            return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
+                    | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+                    | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+        }
+
+        @Override
+        public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
+               List<AccessibilityServiceInfo> installedServices) {
+            if (installedServices == null) {
+                installedServices = new ArrayList<>();
+            }
+            installedServices.add(getCompatServiceInfo());
+            return installedServices;
+        }
+
+        @Override
+        public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+                int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
+            if (enabledService == null) {
+                enabledService = new ArrayList<>();
+            }
+            enabledService.add(getCompatServiceInfo());
+            return enabledService;
+        }
+
+        @Override
+        public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
+                boolean accessibilityEnabled, int relevantEventTypes) {
+            switch (event.getEventType()) {
+                case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
+                    synchronized (mLock) {
+                        if (mFocusedWindowId == event.getWindowId()
+                                && mFocusedNodeId == event.getSourceNodeId()) {
+                            return event;
+                        }
+                        if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
+                                && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
+                            notifyViewExited(mFocusedWindowId, mFocusedNodeId);
+                            mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+                            mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
+                            mFocusedBounds.set(0, 0, 0, 0);
+                        }
+                        final int windowId = event.getWindowId();
+                        final long nodeId = event.getSourceNodeId();
+                        if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
+                            mFocusedWindowId = windowId;
+                            mFocusedNodeId = nodeId;
+                        }
+                    }
+                } break;
+
+                case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
+                    synchronized (mLock) {
+                        if (mFocusedWindowId == event.getWindowId()
+                                && mFocusedNodeId == event.getSourceNodeId()) {
+                            notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
+                        }
+                    }
+                } break;
+
+                case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
+                    final AutofillClient client = getClient();
+                    if (client != null) {
+                        synchronized (mLock) {
+                            if (client.autofillClientIsFillUiShowing()) {
+                                notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
+                            }
+                            updateTrackedViewsLocked();
+                        }
+                    }
+                } break;
+            }
+
+            return accessibilityEnabled ? event : null;
+        }
+
+        private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
+            final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+            if (!isVirtualNode(virtualId)) {
+                return false;
+            }
+            final View view = findViewByAccessibilityId(windowId, nodeId);
+            if (view == null) {
+                return false;
+            }
+            final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
+            if (node == null) {
+                return false;
+            }
+            if (!node.isEditable()) {
+                return false;
+            }
+            final Rect newBounds = mTempBounds;
+            node.getBoundsInScreen(newBounds);
+            if (newBounds.equals(focusedBounds)) {
+                return false;
+            }
+            focusedBounds.set(newBounds);
+            AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
+            return true;
+        }
+
+        private void notifyViewExited(int windowId, long nodeId) {
+            final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+            if (!isVirtualNode(virtualId)) {
+                return;
+            }
+            final View view = findViewByAccessibilityId(windowId, nodeId);
+            if (view == null) {
+                return;
+            }
+            AutofillManager.this.notifyViewExited(view, virtualId);
+        }
+
+        private void notifyValueChanged(int windowId, long nodeId) {
+            final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+            if (!isVirtualNode(virtualId)) {
+                return;
+            }
+            final View view = findViewByAccessibilityId(windowId, nodeId);
+            if (view == null) {
+                return;
+            }
+            final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
+            if (node == null) {
+                return;
+            }
+            AutofillManager.this.notifyValueChanged(view, virtualId,
+                    AutofillValue.forText(node.getText()));
+        }
+
+        private void updateTrackedViewsLocked() {
+            if (mTrackedViews != null) {
+                mTrackedViews.onVisibleForAutofillChangedLocked();
+            }
+        }
+
+        private View findViewByAccessibilityId(int windowId, long nodeId) {
+            final AutofillClient client = getClient();
+            if (client == null) {
+                return null;
+            }
+            final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
+            return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
+        }
+
+        private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
+            final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+            if (provider == null) {
+                return null;
+            }
+            return provider.createAccessibilityNodeInfo(virtualId);
+        }
+
+        private boolean isVirtualNode(int nodeId) {
+            return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
+                    && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+        }
     }
 
     /**
@@ -2013,11 +2269,11 @@
          */
         TrackedViews(@Nullable AutofillId[] trackedIds) {
             final AutofillClient client = getClient();
-            if (trackedIds != null && client != null) {
+            if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
                 final boolean[] isVisible;
 
-                if (client.isVisibleForAutofill()) {
-                    isVisible = client.getViewVisibility(getViewIds(trackedIds));
+                if (client.autofillClientIsVisibleForAutofill()) {
+                    isVisible = client.autofillClientGetViewVisibility(trackedIds);
                 } else {
                     // All false
                     isVisible = new boolean[trackedIds.length];
@@ -2054,7 +2310,7 @@
          */
         void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
             if (sDebug) {
-                Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
+                Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
                         + isVisible);
             }
 
@@ -2083,9 +2339,9 @@
         /**
          * Called once the client becomes visible.
          *
-         * @see AutofillClient#isVisibleForAutofill()
+         * @see AutofillClient#autofillClientIsVisibleForAutofill()
          */
-        void onVisibleForAutofillLocked() {
+        void onVisibleForAutofillChangedLocked() {
             // The visibility of the views might have changed while the client was not be visible,
             // hence update the visibility state for all views.
             AutofillClient client = getClient();
@@ -2095,8 +2351,8 @@
                 if (mInvisibleTrackedIds != null) {
                     final ArrayList<AutofillId> orderedInvisibleIds =
                             new ArrayList<>(mInvisibleTrackedIds);
-                    final boolean[] isVisible = client.getViewVisibility(
-                            getViewIds(orderedInvisibleIds));
+                    final boolean[] isVisible = client.autofillClientGetViewVisibility(
+                            Helper.toArray(orderedInvisibleIds));
 
                     final int numInvisibleTrackedIds = orderedInvisibleIds.size();
                     for (int i = 0; i < numInvisibleTrackedIds; i++) {
@@ -2116,8 +2372,8 @@
                 if (mVisibleTrackedIds != null) {
                     final ArrayList<AutofillId> orderedVisibleIds =
                             new ArrayList<>(mVisibleTrackedIds);
-                    final boolean[] isVisible = client.getViewVisibility(
-                            getViewIds(orderedVisibleIds));
+                    final boolean[] isVisible = client.autofillClientGetViewVisibility(
+                            Helper.toArray(orderedVisibleIds));
 
                     final int numVisibleTrackedIds = orderedVisibleIds.size();
                     for (int i = 0; i < numVisibleTrackedIds; i++) {
diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java
index fc5d306..155fe72 100644
--- a/core/java/android/view/autofill/AutofillManagerInternal.java
+++ b/core/java/android/view/autofill/AutofillManagerInternal.java
@@ -15,6 +15,9 @@
  */
 package android.view.autofill;
 
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+
 /**
  * Autofill Manager local system service interface.
  *
@@ -26,4 +29,14 @@
      * Notifies the manager that the back key was pressed.
      */
     public abstract void onBackKeyPressed();
+
+    /**
+     * Gets whether compatibility mode is enabled for a package
+     *
+     * @param packageName The package for which to query.
+     * @param versionCode The package version code.
+     * @param userId The user id for which to query.
+     */
+    public abstract boolean isCompatibilityModeRequested(@NonNull String packageName,
+            long versionCode, @UserIdInt int userId);
 }
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
index 4b2c53c..48d0dbb 100644
--- a/core/java/android/view/autofill/Helper.java
+++ b/core/java/android/view/autofill/Helper.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import java.util.Collection;
+
 /** @hide */
 public final class Helper {
 
@@ -59,6 +61,20 @@
         builder.append(" ]");
     }
 
+    /**
+     * Convers a collaction of {@link AutofillId AutofillIds} to an array.
+     * @param collection The collection.
+     * @return The array.
+     */
+    public static @NonNull AutofillId[] toArray(Collection<AutofillId> collection) {
+        if (collection == null) {
+            return new AutofillId[0];
+        }
+        final AutofillId[] array = new AutofillId[collection.size()];
+        collection.toArray(array);
+        return array;
+    }
+
     private Helper() {
         throw new UnsupportedOperationException("contains static members only");
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d58b95a..c29b002 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4179,6 +4179,16 @@
         <service android:name="com.android.server.display.BrightnessIdleJob"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
+
+        <service
+                android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
+                android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+                android:exported="true">
+            <meta-data
+                    android:name="android.accessibilityservice"
+                    android:resource="@xml/autofill_compat_accessibility_service" />
+        </service>
+
 </application>
 
 </manifest>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 68dad87..7a0855f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7903,6 +7903,23 @@
         <attr name="settingsActivity" />
     </declare-styleable>
 
+    <!-- Use <code>compatibility-package</code> as a child tag of <code>autofill-service</code>
+         in the XML resource that describes an {@link android.service.autofill.AutofillService}
+         to specify a package and an optional max version code for which to request compatibility
+         mode. If no max version code is specified compatibility mode is requested for all package
+         versions. The max version code is useful to avoid requesting compatibility mode for newer
+         package versions that are known to natively support autofill.
+    -->
+    <declare-styleable name="AutofillService_CompatibilityPackage">
+        <!-- The package name for which compatibility mode is requested. -->
+        <attr name="name" />
+        <!-- The max version code of the package for which compatibility mode is
+             requested. This corresponds to the long value returned by {@link
+             android.content.pm.PackageInfo#getLongVersionCode()} for the target package.
+        -->
+        <attr name="maxLongVersionCode" format="string" />
+    </declare-styleable>
+
     <!-- =============================== -->
     <!-- Contacts meta-data attributes -->
     <!-- =============================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index dee880f..93f22f25 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2868,6 +2868,7 @@
       <public name="accessibilityHeading" />
       <public name="outlineSpotShadowColor" />
       <public name="outlineAmbientShadowColor" />
+      <public name="maxLongVersionCode" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/xml/autofill_compat_accessibility_service.xml b/core/res/res/xml/autofill_compat_accessibility_service.xml
new file mode 100644
index 0000000..327afbe
--- /dev/null
+++ b/core/res/res/xml/autofill_compat_accessibility_service.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accessibilityEventTypes="typeAllMask"
+    android:accessibilityFeedbackType="feedbackGeneric"
+    android:accessibilityFlags="flagReportViewIds" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index e4de526..77aaa2d 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -389,8 +389,8 @@
                 Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher,
                 IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
                 boolean b2, boolean b3, Configuration configuration,
-                CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1)
-                throws RemoteException {
+                CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1,
+                boolean autofillCompatEnabled) throws RemoteException {
         }
 
         @Override
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 0083b01..a29dd05 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -115,6 +115,7 @@
                     Settings.Global.APP_STANDBY_ENABLED,
                     Settings.Global.ASSISTED_GPS_ENABLED,
                     Settings.Global.AUDIO_SAFE_VOLUME_STATE,
+                    Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES,
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                     Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,