Merge "AudioService: fix disconnect/connect of A2DP device"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index e44e902..38bb75f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -25,7 +25,6 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.IUidObserver;
 import android.app.job.IJobScheduler;
@@ -95,7 +94,6 @@
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
-import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
 import com.android.server.job.controllers.BackgroundJobsController;
 import com.android.server.job.controllers.BatteryController;
 import com.android.server.job.controllers.ConnectivityController;
@@ -117,7 +115,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -240,18 +237,7 @@
     final SparseIntArray mBackingUpUids = new SparseIntArray();
 
     /**
-     * Count standby heartbeats, and keep track of which beat each bucket's jobs will
-     * next become runnable.  Index into this array is by normalized bucket:
-     * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }.  The ACTIVE and NEVER bucket
-     * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
-     * and NEVER apps don't get them at all.
-     */
-    final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
-    long mHeartbeat = 0;
-    long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
-
-    /**
-     * Named indices into the STANDBY_BEATS array, for clarity in referring to
+     * Named indices into standby bucket arrays, for clarity in referring to
      * specific buckets' bookkeeping.
      */
     public static final int ACTIVE_INDEX = 0;
@@ -260,21 +246,6 @@
     public static final int RARE_INDEX = 3;
     public static final int NEVER_INDEX = 4;
 
-    /**
-     * Bookkeeping about when jobs last run.  We keep our own record in heartbeat time,
-     * rather than rely on Usage Stats' timestamps, because heartbeat time can be
-     * manipulated for testing purposes and we need job runnability to track that rather
-     * than real time.
-     *
-     * Outer SparseArray slices by user handle; inner map of package name to heartbeat
-     * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
-     * and it will be accessed in a known-hot code path.
-     */
-    final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
-
-    static final String HEARTBEAT_TAG = "*job.heartbeat*";
-    final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
-
     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
 
     private class ConstantsObserver extends ContentObserver {
@@ -311,11 +282,6 @@
                     Slog.e(TAG, "Bad jobscheduler settings", e);
                 }
             }
-
-            if (mConstants.USE_HEARTBEATS) {
-                // Reset the heartbeat alarm based on the new heartbeat duration
-                setNextHeartbeatAlarm();
-            }
         }
     }
 
@@ -465,13 +431,15 @@
         private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
         private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
         private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
-        private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
-        private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
-        private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
-        private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
+        private static final String DEPRECATED_KEY_STANDBY_HEARTBEAT_TIME =
+                "standby_heartbeat_time";
+        private static final String DEPRECATED_KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
+        private static final String DEPRECATED_KEY_STANDBY_FREQUENT_BEATS =
+                "standby_frequent_beats";
+        private static final String DEPRECATED_KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
-        private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
+        private static final String DEPRECATED_KEY_USE_HEARTBEATS = "use_heartbeats";
 
         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -488,13 +456,8 @@
         private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
-        private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
-        private static final int DEFAULT_STANDBY_WORKING_BEATS = 11;  // ~ 2 hours, with 11min beats
-        private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
-        private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
-        private static final boolean DEFAULT_USE_HEARTBEATS = false;
 
         /**
          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
@@ -617,26 +580,7 @@
          * The minimum backoff time to allow for exponential backoff.
          */
         long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
-        /**
-         * How often we recalculate runnability based on apps' standby bucket assignment.
-         * This should be prime relative to common time interval lengths such as a quarter-
-         * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
-         */
-        long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
-        /**
-         * Mapping: standby bucket -> number of heartbeats between each sweep of that
-         * bucket's jobs.
-         *
-         * Bucket assignments as recorded in the JobStatus objects are normalized to be
-         * indices into this array, rather than the raw constants used
-         * by AppIdleHistory.
-         */
-        final int[] STANDBY_BEATS = {
-                0,
-                DEFAULT_STANDBY_WORKING_BEATS,
-                DEFAULT_STANDBY_FREQUENT_BEATS,
-                DEFAULT_STANDBY_RARE_BEATS
-        };
+
         /**
          * The fraction of a job's running window that must pass before we
          * consider running it when the network is congested.
@@ -647,11 +591,6 @@
          * we consider matching it against a metered network.
          */
         public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
-        /**
-         * Whether to use heartbeats or rolling window for quota management. True will use
-         * heartbeats, false will use a rolling window.
-         */
-        public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
 
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -709,19 +648,10 @@
                     DEFAULT_MIN_LINEAR_BACKOFF_TIME);
             MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
                     DEFAULT_MIN_EXP_BACKOFF_TIME);
-            STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
-                    DEFAULT_STANDBY_HEARTBEAT_TIME);
-            STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
-                    DEFAULT_STANDBY_WORKING_BEATS);
-            STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
-                    DEFAULT_STANDBY_FREQUENT_BEATS);
-            STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
-                    DEFAULT_STANDBY_RARE_BEATS);
             CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
                     DEFAULT_CONN_CONGESTION_DELAY_FRAC);
             CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
                     DEFAULT_CONN_PREFETCH_RELAX_FRAC);
-            USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
         }
 
         void dump(IndentingPrintWriter pw) {
@@ -757,17 +687,8 @@
             pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
             pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
             pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
-            pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
-            pw.print("standby_beats={");
-            pw.print(STANDBY_BEATS[0]);
-            for (int i = 1; i < STANDBY_BEATS.length; i++) {
-                pw.print(", ");
-                pw.print(STANDBY_BEATS[i]);
-            }
-            pw.println('}');
             pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
             pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
-            pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
 
             pw.decreaseIndent();
         }
@@ -797,13 +718,8 @@
             proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
             proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
             proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
-            proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
-            for (int period : STANDBY_BEATS) {
-                proto.write(ConstantsProto.STANDBY_BEATS, period);
-            }
             proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
             proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
-            proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
         }
     }
 
@@ -1441,9 +1357,6 @@
 
             mAppStateTracker = Preconditions.checkNotNull(
                     LocalServices.getService(AppStateTracker.class));
-            if (mConstants.USE_HEARTBEATS) {
-                setNextHeartbeatAlarm();
-            }
 
             // Register br for package removals and user removals.
             final IntentFilter filter = new IntentFilter();
@@ -1647,7 +1560,7 @@
         }
         delayMillis =
                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
-        JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
+        JobStatus newJob = new JobStatus(failureToReschedule,
                 elapsedNowMillis + delayMillis,
                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
                 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
@@ -1682,8 +1595,7 @@
      * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
      * to underscheduling at least, rather than if we had taken the last execution time to be the
      * start of the execution.
-     * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
-     * tracking as though the job were newly-scheduled.
+     *
      * @return A new job representing the execution criteria for this instantiation of the
      * recurring job.
      */
@@ -1736,7 +1648,7 @@
         if (newLatestRuntimeElapsed < elapsedNow) {
             Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: "
                     + newLatestRuntimeElapsed);
-            return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
+            return new JobStatus(periodicToReschedule,
                     elapsedNow + period - flex, elapsedNow + period,
                     0 /* backoffAttempt */,
                     sSystemClock.millis() /* lastSuccessfulRunTime */,
@@ -1751,64 +1663,13 @@
                     newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000
                     + "]s");
         }
-        return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
+        return new JobStatus(periodicToReschedule,
                 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
                 0 /* backoffAttempt */,
                 sSystemClock.millis() /* lastSuccessfulRunTime */,
                 periodicToReschedule.getLastFailedRunTime());
     }
 
-    /*
-     * We default to "long enough ago that every bucket's jobs are immediately runnable" to
-     * avoid starvation of apps in uncommon-use buckets that might arise from repeated
-     * reboot behavior.
-     */
-    long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
-        // The furthest back in pre-boot time that we need to bother with
-        long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
-        boolean cacheHit = false;
-        synchronized (mLock) {
-            HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
-            if (jobPackages != null) {
-                long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
-                if (cachedValue < Long.MAX_VALUE) {
-                    cacheHit = true;
-                    heartbeat = cachedValue;
-                }
-            }
-            if (!cacheHit) {
-                // We haven't seen it yet; ask usage stats about it
-                final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
-                if (timeSinceJob < Long.MAX_VALUE) {
-                    // Usage stats knows about it from before, so calculate back from that
-                    // and go from there.
-                    heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
-                }
-                // If usage stats returned its "not found" MAX_VALUE, we still have the
-                // negative default 'heartbeat' value we established above
-                setLastJobHeartbeatLocked(packageName, userId, heartbeat);
-            }
-        }
-        if (DEBUG_STANDBY) {
-            Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
-                    + packageName + "/" + userId);
-        }
-        return heartbeat;
-    }
-
-    long heartbeatWhenJobsLastRun(JobStatus job) {
-        return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
-    }
-
-    void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
-        HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
-        if (jobPackages == null) {
-            jobPackages = new HashMap<>();
-            mLastJobHeartbeats.put(userId, jobPackages);
-        }
-        jobPackages.put(packageName, heartbeat);
-    }
-
     // JobCompletedListener implementations.
 
     /**
@@ -2176,82 +2037,6 @@
         mMaybeQueueFunctor.postProcess();
     }
 
-    /**
-     * Heartbeat tracking.  The heartbeat alarm is intentionally non-wakeup.
-     */
-    class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
-
-        @Override
-        public void onAlarm() {
-            synchronized (mLock) {
-                final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
-                final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
-                if (beatsElapsed > 0) {
-                    mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
-                    advanceHeartbeatLocked(beatsElapsed);
-                }
-            }
-            setNextHeartbeatAlarm();
-        }
-    }
-
-    // Intentionally does not touch the alarm timing
-    void advanceHeartbeatLocked(long beatsElapsed) {
-        if (!mConstants.USE_HEARTBEATS) {
-            return;
-        }
-        mHeartbeat += beatsElapsed;
-        if (DEBUG_STANDBY) {
-            Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
-                    + " to " + mHeartbeat);
-        }
-        // Don't update ACTIVE or NEVER bucket milestones.  Note that mHeartbeat
-        // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
-        // new jobs scheduled by apps in that bucket will be permitted to run
-        // immediately.
-        boolean didAdvanceBucket = false;
-        for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
-            // Did we reach or cross a bucket boundary?
-            if (mHeartbeat >= mNextBucketHeartbeat[i]) {
-                didAdvanceBucket = true;
-            }
-            while (mHeartbeat > mNextBucketHeartbeat[i]) {
-                mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
-            }
-            if (DEBUG_STANDBY) {
-                Slog.v(TAG, "   Bucket " + i + " next heartbeat "
-                        + mNextBucketHeartbeat[i]);
-            }
-        }
-
-        if (didAdvanceBucket) {
-            if (DEBUG_STANDBY) {
-                Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
-            }
-            mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
-        }
-    }
-
-    void setNextHeartbeatAlarm() {
-        final long heartbeatLength;
-        synchronized (mLock) {
-            if (!mConstants.USE_HEARTBEATS) {
-                return;
-            }
-            heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
-        }
-        final long now = sElapsedRealtimeClock.millis();
-        final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
-        final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
-        if (DEBUG_STANDBY) {
-            Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
-                    + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
-        }
-        AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
-        am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
-                HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
-    }
-
     /** Returns true if both the calling and source users for the job are started. */
     private boolean areUsersStartedLocked(final JobStatus job) {
         boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
@@ -2323,54 +2108,6 @@
             return false;
         }
 
-        if (mConstants.USE_HEARTBEATS) {
-            // If the app is in a non-active standby bucket, make sure we've waited
-            // an appropriate amount of time since the last invocation.  During device-
-            // wide parole, standby bucketing is ignored.
-            //
-            // Jobs in 'active' apps are not subject to standby, nor are jobs that are
-            // specifically marked as exempt.
-            if (DEBUG_STANDBY) {
-                Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
-                        + " parole=" + mInParole + " active=" + job.uidActive
-                        + " exempt=" + job.getJob().isExemptedFromAppStandby());
-            }
-            if (!mInParole
-                    && !job.uidActive
-                    && !job.getJob().isExemptedFromAppStandby()) {
-                final int bucket = job.getStandbyBucket();
-                if (DEBUG_STANDBY) {
-                    Slog.v(TAG, "  bucket=" + bucket + " heartbeat=" + mHeartbeat
-                            + " next=" + mNextBucketHeartbeat[bucket]);
-                }
-                if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
-                    // Only skip this job if the app is still waiting for the end of its nominal
-                    // bucket interval.  Once it's waited that long, we let it go ahead and clear.
-                    // The final (NEVER) bucket is special; we never age those apps' jobs into
-                    // runnability.
-                    final long appLastRan = heartbeatWhenJobsLastRun(job);
-                    if (bucket >= mConstants.STANDBY_BEATS.length
-                            || (mHeartbeat > appLastRan
-                            && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
-                        if (job.getWhenStandbyDeferred() == 0) {
-                            if (DEBUG_STANDBY) {
-                                Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
-                                        + (appLastRan + mConstants.STANDBY_BEATS[bucket])
-                                        + " for " + job);
-                            }
-                            job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
-                        }
-                        return false;
-                    } else {
-                        if (DEBUG_STANDBY) {
-                            Slog.v(TAG, "Bucket deferred job aged into runnability at "
-                                    + mHeartbeat + " : " + job);
-                        }
-                    }
-                }
-            }
-        }
-
         // The expensive check: validate that the defined package+service is
         // still present & viable.
         return isComponentUsable(job);
@@ -2439,9 +2176,6 @@
 
         // Job pending/active doesn't affect the readiness of a job.
 
-        // Skipping the heartbeat check as this will only come into play when using the rolling
-        // window quota management system.
-
         // The expensive check: validate that the defined package+service is
         // still present & viable.
         return isComponentUsable(job);
@@ -2450,9 +2184,6 @@
     /** Returns the maximum amount of time this job could run for. */
     public long getMaxJobExecutionTimeMs(JobStatus job) {
         synchronized (mLock) {
-            if (mConstants.USE_HEARTBEATS) {
-                return JobServiceContext.EXECUTING_TIMESLICE_MILLIS;
-            }
             return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job),
                     JobServiceContext.EXECUTING_TIMESLICE_MILLIS);
         }
@@ -2498,56 +2229,6 @@
     final class LocalService implements JobSchedulerInternal {
 
         /**
-         * The current bucket heartbeat ordinal
-         */
-        public long currentHeartbeat() {
-            return getCurrentHeartbeat();
-        }
-
-        /**
-         * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
-         */
-        public long nextHeartbeatForBucket(int bucket) {
-            synchronized (mLock) {
-                return mNextBucketHeartbeat[bucket];
-            }
-        }
-
-        /**
-         * Heartbeat ordinal for the given app.  This is typically the heartbeat at which
-         * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
-         * jobs in a long time is immediately runnable even if the app is bucketed into
-         * an infrequent time allocation.
-         */
-        public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
-                final int appStandbyBucket) {
-            if (appStandbyBucket == 0 ||
-                    appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
-                // ACTIVE => everything can be run right away
-                // NEVER => we won't run them anyway, so let them go in the future
-                // as soon as the app enters normal use
-                if (DEBUG_STANDBY) {
-                    Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
-                            + packageName + "/" + userId);
-                }
-                return 0;
-            }
-
-            final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
-            if (DEBUG_STANDBY) {
-                Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
-                        + packageName + "/" + userId);
-            }
-            return baseHeartbeat;
-        }
-
-        public void noteJobStart(String packageName, int userId) {
-            synchronized (mLock) {
-                setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
-            }
-        }
-
-        /**
          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
          * jobs are always considered pending.
          */
@@ -3158,12 +2839,6 @@
         }
     }
 
-    long getCurrentHeartbeat() {
-        synchronized (mLock) {
-            return mHeartbeat;
-        }
-    }
-
     // Shell command infrastructure
     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
         try {
@@ -3249,21 +2924,6 @@
         return 0;
     }
 
-    // Shell command infrastructure
-    int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
-        if (numBeats < 1) {
-            pw.println(getCurrentHeartbeat());
-            return 0;
-        }
-
-        pw.print("Advancing standby heartbeat by ");
-        pw.println(numBeats);
-        synchronized (mLock) {
-            advanceHeartbeatLocked(numBeats);
-        }
-        return 0;
-    }
-
     void triggerDockState(boolean idleState) {
         final Intent dockIntent;
         if (idleState) {
@@ -3319,20 +2979,6 @@
             }
             pw.println();
 
-            pw.println("  Heartbeat:");
-            pw.print("    Current:    "); pw.println(mHeartbeat);
-            pw.println("    Next");
-            pw.print("      ACTIVE:   "); pw.println(mNextBucketHeartbeat[0]);
-            pw.print("      WORKING:  "); pw.println(mNextBucketHeartbeat[1]);
-            pw.print("      FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
-            pw.print("      RARE:     "); pw.println(mNextBucketHeartbeat[3]);
-            pw.print("    Last heartbeat: ");
-            TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
-            pw.println();
-            pw.print("    Next heartbeat: ");
-            TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
-                    nowElapsed, pw);
-            pw.println();
             pw.print("    In parole?: ");
             pw.print(mInParole);
             pw.println();
@@ -3358,9 +3004,6 @@
                     }
 
                     job.dump(pw, "    ", true, nowElapsed);
-                    pw.print("    Last run heartbeat: ");
-                    pw.print(heartbeatWhenJobsLastRun(job));
-                    pw.println();
 
                     pw.print("    Ready: ");
                     pw.print(isReadyToBeExecutedLocked(job));
@@ -3514,15 +3157,6 @@
             }
             proto.end(settingsToken);
 
-            proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
-            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
-            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
-            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
-            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
-            proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
-                    mLastHeartbeatTime - nowUptime);
-            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
-                    mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
             proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
             proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
 
@@ -3564,7 +3198,6 @@
                     }
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
                             componentPresent);
-                    proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
 
                     proto.end(rjToken);
                 }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index e361441..01d158ba 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -340,15 +340,8 @@
     private int doHeartbeat(PrintWriter pw) throws Exception {
         checkPermission("manipulate scheduler heartbeat");
 
-        final String arg = getNextArg();
-        final int numBeats = (arg != null) ? Integer.parseInt(arg) : 0;
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            return mInternal.executeHeartbeatCommand(pw, numBeats);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        pw.println("Heartbeat command is no longer supported");
+        return -1;
     }
 
     private int triggerDockState(PrintWriter pw) throws Exception {
@@ -401,8 +394,7 @@
         pw.println("      -u or --user: specify which user's job is to be run; the default is");
         pw.println("         the primary or system user");
         pw.println("  heartbeat [num]");
-        pw.println("    With no argument, prints the current standby heartbeat.  With a positive");
-        pw.println("    argument, advances the standby heartbeat by that number.");
+        pw.println("    No longer used.");
         pw.println("  monitor-battery [on|off]");
         pw.println("    Control monitoring of all battery changes.  Off by default.  Turning");
         pw.println("    on makes get-battery-seq useful.");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 7da128f..4d9f133 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -285,9 +285,6 @@
             UsageStatsManagerInternal usageStats =
                     LocalServices.getService(UsageStatsManagerInternal.class);
             usageStats.setLastJobRunTime(jobPackage, jobUserId, mExecutionStartTimeElapsed);
-            JobSchedulerInternal jobScheduler =
-                    LocalServices.getService(JobSchedulerInternal.class);
-            jobScheduler.noteJobStart(jobPackage, jobUserId);
             mAvailable = false;
             mStoppedReason = null;
             mStoppedTime = 0;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index d69faf3..4321fc7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -189,7 +189,7 @@
             if (utcTimes != null) {
                 Pair<Long, Long> elapsedRuntimes =
                         convertRtcBoundsToElapsed(utcTimes, elapsedNow);
-                JobStatus newJob = new JobStatus(job, job.getBaseHeartbeat(),
+                JobStatus newJob = new JobStatus(job,
                         elapsedRuntimes.first, elapsedRuntimes.second,
                         0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime());
                 newJob.prepareLocked(am);
@@ -944,10 +944,9 @@
             JobSchedulerInternal service = LocalServices.getService(JobSchedulerInternal.class);
             final int appBucket = JobSchedulerService.standbyBucketForPackage(sourcePackageName,
                     sourceUserId, elapsedNow);
-            long currentHeartbeat = service != null ? service.currentHeartbeat() : 0;
             JobStatus js = new JobStatus(
                     jobBuilder.build(), uid, sourcePackageName, sourceUserId,
-                    appBucket, currentHeartbeat, sourceTag,
+                    appBucket, sourceTag,
                     elapsedRuntimes.first, elapsedRuntimes.second,
                     lastSuccessfulRunTime, lastFailedRunTime,
                     (rtcIsGood) ? null : rtcRuntimes, internalFlags);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index f8cf6ae..a67aadf 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -47,7 +47,6 @@
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobSchedulerService.Constants;
-import com.android.server.job.JobServiceContext;
 import com.android.server.job.StateControllerProto;
 import com.android.server.net.NetworkPolicyManagerInternal;
 
@@ -88,8 +87,6 @@
     @GuardedBy("mLock")
     private final ArraySet<Network> mAvailableNetworks = new ArraySet<>();
 
-    private boolean mUseQuotaLimit;
-
     private static final int MSG_DATA_SAVER_TOGGLED = 0;
     private static final int MSG_UID_RULES_CHANGES = 1;
     private static final int MSG_REEVALUATE_JOBS = 2;
@@ -110,8 +107,6 @@
         mConnManager.registerNetworkCallback(request, mNetworkCallback);
 
         mNetPolicyManager.registerListener(mNetPolicyListener);
-
-        mUseQuotaLimit = !mConstants.USE_HEARTBEATS;
     }
 
     @GuardedBy("mLock")
@@ -142,24 +137,6 @@
         }
     }
 
-    @GuardedBy("mLock")
-    @Override
-    public void onConstantsUpdatedLocked() {
-        if (mConstants.USE_HEARTBEATS) {
-            // App idle exceptions are only requested for the rolling quota system.
-            if (DEBUG) Slog.i(TAG, "Revoking all standby exceptions");
-            for (int i = 0; i < mRequestedWhitelistJobs.size(); ++i) {
-                int uid = mRequestedWhitelistJobs.keyAt(i);
-                mNetPolicyManagerInternal.setAppIdleWhitelist(uid, false);
-            }
-            mRequestedWhitelistJobs.clear();
-        }
-        if (mUseQuotaLimit == mConstants.USE_HEARTBEATS) {
-            mUseQuotaLimit = !mConstants.USE_HEARTBEATS;
-            mHandler.obtainMessage(MSG_REEVALUATE_JOBS).sendToTarget();
-        }
-    }
-
     /**
      * Returns true if the job's requested network is available. This DOES NOT necesarilly mean
      * that the UID has been granted access to the network.
@@ -237,11 +214,6 @@
     @GuardedBy("mLock")
     @Override
     public void evaluateStateLocked(JobStatus jobStatus) {
-        if (mConstants.USE_HEARTBEATS) {
-            // This should only be used for the rolling quota system.
-            return;
-        }
-
         if (!jobStatus.hasConnectivityConstraint()) {
             return;
         }
@@ -263,9 +235,6 @@
     @GuardedBy("mLock")
     @Override
     public void reevaluateStateLocked(final int uid) {
-        if (mConstants.USE_HEARTBEATS) {
-            return;
-        }
         // Check if we still need a connectivity exception in case the JobService was disabled.
         ArraySet<JobStatus> jobs = mTrackedJobs.get(uid);
         if (jobs == null) {
@@ -329,9 +298,7 @@
      */
     private boolean isInsane(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
-        final long maxJobExecutionTimeMs = mUseQuotaLimit
-                ? mService.getMaxJobExecutionTimeMs(jobStatus)
-                : JobServiceContext.EXECUTING_TIMESLICE_MILLIS;
+        final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
 
         final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
         if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
@@ -617,7 +584,6 @@
     @Override
     public void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate) {
-        pw.print("mUseQuotaLimit="); pw.println(mUseQuotaLimit);
 
         if (mRequestedWhitelistJobs.size() > 0) {
             pw.print("Requested standby exceptions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 6f2b334..ae4abd6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -169,12 +169,6 @@
     private final int numFailures;
 
     /**
-     * Current standby heartbeat when this job was scheduled or last ran.  Used to
-     * pin the runnability check regardless of the job's app moving between buckets.
-     */
-    private final long baseHeartbeat;
-
-    /**
      * Which app standby bucket this job's app is in.  Updated when the app is moved to a
      * different bucket.
      */
@@ -350,8 +344,6 @@
      * @param standbyBucket The standby bucket that the source package is currently assigned to,
      *     cached here for speed of handling during runnability evaluations (and updated when bucket
      *     assignments are changed)
-     * @param heartbeat Timestamp of when the job was created, in the standby-related
-     *     timebase.
      * @param tag A string associated with the job for debugging/logging purposes.
      * @param numFailures Count of how many times this job has requested a reschedule because
      *     its work was not yet finished.
@@ -364,13 +356,12 @@
      * @param internalFlags Non-API property flags about this job
      */
     private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
-            int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures,
+            int sourceUserId, int standbyBucket, String tag, int numFailures,
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
             long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
         this.job = job;
         this.callingUid = callingUid;
         this.standbyBucket = standbyBucket;
-        this.baseHeartbeat = heartbeat;
 
         int tempSourceUid = -1;
         if (sourceUserId != -1 && sourcePackageName != null) {
@@ -440,7 +431,7 @@
     public JobStatus(JobStatus jobStatus) {
         this(jobStatus.getJob(), jobStatus.getUid(),
                 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
-                jobStatus.getStandbyBucket(), jobStatus.getBaseHeartbeat(),
+                jobStatus.getStandbyBucket(),
                 jobStatus.getSourceTag(), jobStatus.getNumFailures(),
                 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
                 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
@@ -462,13 +453,13 @@
      * standby bucket is whatever the OS thinks it should be at this moment.
      */
     public JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId,
-            int standbyBucket, long baseHeartbeat, String sourceTag,
+            int standbyBucket, String sourceTag,
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
             long lastSuccessfulRunTime, long lastFailedRunTime,
             Pair<Long, Long> persistedExecutionTimesUTC,
             int innerFlags) {
         this(job, callingUid, sourcePkgName, sourceUserId,
-                standbyBucket, baseHeartbeat,
+                standbyBucket,
                 sourceTag, 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
                 lastSuccessfulRunTime, lastFailedRunTime, innerFlags);
@@ -486,13 +477,13 @@
     }
 
     /** Create a new job to be rescheduled with the provided parameters. */
-    public JobStatus(JobStatus rescheduling, long newBaseHeartbeat,
+    public JobStatus(JobStatus rescheduling,
             long newEarliestRuntimeElapsedMillis,
             long newLatestRuntimeElapsedMillis, int backoffAttempt,
             long lastSuccessfulRunTime, long lastFailedRunTime) {
         this(rescheduling.job, rescheduling.getUid(),
                 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
-                rescheduling.getStandbyBucket(), newBaseHeartbeat,
+                rescheduling.getStandbyBucket(),
                 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
                 newLatestRuntimeElapsedMillis,
                 lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags());
@@ -529,11 +520,8 @@
         int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
                 sourceUserId, elapsedNow);
         JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
-        long currentHeartbeat = js != null
-                ? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket)
-                : 0;
         return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
-                standbyBucket, currentHeartbeat, tag, 0,
+                standbyBucket, tag, 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
                 /*innerFlags=*/ 0);
@@ -714,10 +702,6 @@
         return standbyBucket;
     }
 
-    public long getBaseHeartbeat() {
-        return baseHeartbeat;
-    }
-
     public void setStandbyBucket(int newBucket) {
         standbyBucket = newBucket;
     }
@@ -1631,10 +1615,6 @@
         }
         pw.print(prefix); pw.print("Standby bucket: ");
         pw.println(getBucketName());
-        if (standbyBucket > 0) {
-            pw.print(prefix); pw.print("Base heartbeat: ");
-            pw.println(baseHeartbeat);
-        }
         if (whenStandbyDeferred != 0) {
             pw.print(prefix); pw.print("  Deferred since: ");
             TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index b8cfac4..043cda4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -416,13 +416,6 @@
 
     private volatile boolean mInParole;
 
-    /**
-     * If the QuotaController should throttle apps based on their standby bucket and job activity.
-     * If false, all jobs will have their CONSTRAINT_WITHIN_QUOTA bit set to true immediately and
-     * indefinitely.
-     */
-    private boolean mShouldThrottle;
-
     /** How much time each app will have to run jobs within their standby bucket window. */
     private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
 
@@ -594,8 +587,6 @@
         } catch (RemoteException e) {
             // ignored; both services live in system_server
         }
-
-        mShouldThrottle = !mConstants.USE_HEARTBEATS;
     }
 
     @Override
@@ -607,8 +598,6 @@
     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
         final int userId = jobStatus.getSourceUserId();
         final String pkgName = jobStatus.getSourcePackageName();
-        // Still need to track jobs even if mShouldThrottle is false in case it's set to true at
-        // some point.
         ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName);
         if (jobs == null) {
             jobs = new ArraySet<>();
@@ -616,16 +605,10 @@
         }
         jobs.add(jobStatus);
         jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
-        if (mShouldThrottle) {
-            final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
-            setConstraintSatisfied(jobStatus, isWithinQuota);
-            if (!isWithinQuota) {
-                maybeScheduleStartAlarmLocked(userId, pkgName,
-                        getEffectiveStandbyBucket(jobStatus));
-            }
-        } else {
-            // QuotaController isn't throttling, so always set to true.
-            jobStatus.setQuotaConstraintSatisfied(true);
+        final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
+        setConstraintSatisfied(jobStatus, isWithinQuota);
+        if (!isWithinQuota) {
+            maybeScheduleStartAlarmLocked(userId, pkgName, getEffectiveStandbyBucket(jobStatus));
         }
     }
 
@@ -674,20 +657,6 @@
     }
 
     @Override
-    public void onConstantsUpdatedLocked() {
-        if (mShouldThrottle == mConstants.USE_HEARTBEATS) {
-            mShouldThrottle = !mConstants.USE_HEARTBEATS;
-
-            // Update job bookkeeping out of band.
-            BackgroundThread.getHandler().post(() -> {
-                synchronized (mLock) {
-                    maybeUpdateAllConstraintsLocked();
-                }
-            });
-        }
-    }
-
-    @Override
     public void onAppRemovedLocked(String packageName, int uid) {
         if (packageName == null) {
             Slog.wtf(TAG, "Told app removed but given null package name.");
@@ -780,8 +749,6 @@
     boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
             final int standbyBucket) {
         if (standbyBucket == NEVER_INDEX) return false;
-        // This check is needed in case the flag is toggled after a job has been registered.
-        if (!mShouldThrottle) return true;
 
         // Quota constraint is not enforced while charging or when parole is on.
         if (mChargeTracker.isCharging() || mInParole) {
@@ -1820,8 +1787,7 @@
                     if (timer != null && timer.isActive()) {
                         timer.rescheduleCutoff();
                     }
-                    if (!mShouldThrottle || maybeUpdateConstraintForPkgLocked(userId,
-                            packageName)) {
+                    if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
                         mStateChangedListener.onControllerStateChanged();
                     }
                 }
@@ -2396,7 +2362,7 @@
                     changed = true;
                 }
 
-                if (changed && mShouldThrottle) {
+                if (changed) {
                     // Update job bookkeeping out of band.
                     BackgroundThread.getHandler().post(() -> {
                         synchronized (mLock) {
@@ -2561,7 +2527,6 @@
     @Override
     public void dumpControllerStateLocked(final IndentingPrintWriter pw,
             final Predicate<JobStatus> predicate) {
-        pw.println("Is throttling: " + mShouldThrottle);
         pw.println("Is charging: " + mChargeTracker.isCharging());
         pw.println("In parole: " + mInParole);
         pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f6c72ea..bfbf667 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -56,6 +56,7 @@
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
 import "frameworks/base/core/proto/android/wifi/enums.proto";
+import "frameworks/base/core/proto/android/stats/textclassifier/textclassifier_enums.proto";
 
 /**
  * The master atom class. This message defines all of the available
@@ -319,6 +320,15 @@
             217 [(log_from_module) = "permissioncontroller"];
         PermissionAppsFragmentViewed permission_apps_fragment_viewed =
             218  [(log_from_module) = "permissioncontroller"];
+        TextSelectionEvent text_selection_event =
+            219  [(log_from_module) = "textclassifier"];
+        TextLinkifyEvent text_linkify_event =
+            220  [(log_from_module) = "textclassifier"];
+        ConversationActionsEvent conversation_actions_event =
+            221  [(log_from_module) = "textclassifier"];
+        LanguageDetectionEvent language_detection_event =
+            222  [(log_from_module) = "textclassifier"];
+
     }
 
     // Pulled events will start at field 10000.
@@ -6790,3 +6800,136 @@
     }
     optional Category category = 6;
 }
+
+/**
+ * Logs when there is a smart selection related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message TextSelectionEvent {
+    // A session ID.
+    optional string session_id = 1;
+
+    // Event type of this event.
+    optional android.stats.textclassifier.EventType event_type = 2;
+
+    // Name of the model that is involved in this event.
+    optional string model_name = 3;
+
+    // Type of widget that was involved in triggering this event.
+    optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+    // Index of this event in a session.
+    optional int32 event_index = 5;
+
+    // Entity type that is involved.
+    optional string entity_type = 6;
+
+    // Relative word index of the start of the selection.
+    optional int32 relative_word_start_index = 7;
+
+    // Relative word (exclusive) index of the end of the selection.
+    optional int32 relative_word_end_index = 8;
+
+    // Relative word index of the start of the smart selection.
+    optional int32 relative_suggested_word_start_index = 9;
+
+    // Relative word (exclusive) index of the end of the smart selection.
+    optional int32 relative_suggested_word_end_index = 10;
+}
+
+/**
+ * Logs when there is a smart linkify related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message TextLinkifyEvent {
+    // A session ID.
+    optional string session_id = 1;
+
+    // Event type of this event.
+    optional android.stats.textclassifier.EventType event_type = 2;
+
+    // Name of the model that is involved in this event.
+    optional string model_name = 3;
+
+    // Type of widget that was involved in triggering this event.
+    optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+    // Index of this event in a session.
+    optional int32 event_index = 5;
+
+    // Entity type that is involved.
+    optional string entity_type = 6;
+
+    // Number of links detected.
+    optional int32 num_links = 7;
+
+    // The total length of all links.
+    optional int32 linked_text_length = 8;
+
+    // Length of input text.
+    optional int32 text_length = 9;
+
+    // Time spent on generating links in ms.
+    optional int64 latency_millis = 10;
+}
+
+/**
+ * Logs when there is a conversation actions related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message ConversationActionsEvent {
+    // A session ID.
+    optional string session_id = 1;
+
+    // Event type of this event.
+    optional android.stats.textclassifier.EventType event_type = 2;
+
+    // Name of the model that is involved in this event.
+    optional string model_name = 3;
+
+    // Type of widget that was involved in triggering this event.
+    optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+    // The first entity type that is involved.
+    optional string first_entity_type = 5;
+
+    // The second entity type that is involved.
+    optional string second_entity_type = 6;
+
+    // The third entity type that is involved.
+    optional string third_entity_type = 7;
+
+    // The score of the first entity type.
+    optional float score = 8;
+}
+
+/**
+ * Logs when there is a language detection related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message LanguageDetectionEvent {
+    // A session ID.
+    optional string session_id = 1;
+
+    // Event type of this event.
+    optional android.stats.textclassifier.EventType event_type = 2;
+
+    // Name of the model that is involved in this event.
+    optional string model_name = 3;
+
+    // Type of widget that was involved in triggering this event.
+    optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+    // Detected language.
+    optional string language_tag = 5;
+
+    // Score of the detected language.
+    optional float score = 6;
+
+    // Position of this action.
+    optional int32 action_index = 7;
+}
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index c30a6f4..eb53b7c 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -121,8 +121,6 @@
 Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap;
 Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V
 Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
 Landroid/content/IClipboard$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard;
 Landroid/content/IContentService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -194,7 +192,6 @@
 Landroid/content/UndoManager;-><init>()V
 Landroid/database/IContentObserver$Stub;-><init>()V
 Landroid/database/IContentObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/database/IContentObserver;
-Landroid/database/IContentObserver;->onChange(ZLandroid/net/Uri;I)V
 Landroid/database/sqlite/SQLiteConnectionPool;->$assertionsDisabled:Z
 Landroid/database/sqlite/SQLiteDatabase;->$assertionsDisabled:Z
 Landroid/hardware/display/IDisplayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/display/IDisplayManager;
@@ -723,9 +720,6 @@
 Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
 Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
 Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)V
-Lcom/android/internal/location/ILocationProvider;->setLocationProviderManager(Lcom/android/internal/location/ILocationProviderManager;)V
-Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
 Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
 Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
 Lcom/android/internal/logging/MetricsLogger;-><init>()V
@@ -736,7 +730,6 @@
 Lcom/android/internal/os/IDropBoxManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/os/IDropBoxManagerService;
 Lcom/android/internal/policy/IKeyguardService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardService;
 Lcom/android/internal/policy/IKeyguardStateCallback$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardStateCallback;
-Lcom/android/internal/preference/YesNoPreference;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
 Lcom/android/internal/R$anim;->fade_in:I
 Lcom/android/internal/R$array;->config_autoBrightnessLcdBacklightValues:I
 Lcom/android/internal/R$array;->config_autoBrightnessLevels:I
diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
index 5f73e55..c9dc019 100644
--- a/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
+++ b/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
@@ -18,6 +18,8 @@
 
 /** @hide */
 interface ICompanionDeviceDiscoveryServiceCallback {
+    @UnsupportedAppUsage
     oneway void onDeviceSelected(String packageName, int userId, String deviceAddress);
+    @UnsupportedAppUsage
     oneway void onDeviceSelectionCancel();
 }
diff --git a/core/java/android/database/IContentObserver.aidl b/core/java/android/database/IContentObserver.aidl
index 22dc9fe..6235566 100644
--- a/core/java/android/database/IContentObserver.aidl
+++ b/core/java/android/database/IContentObserver.aidl
@@ -29,5 +29,6 @@
      * observed. selfUpdate is true if the update was caused by a call to
      * commit on the cursor that is being observed.
      */
+    @UnsupportedAppUsage
     oneway void onChange(boolean selfUpdate, in Uri uri, int userId);
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index cc8c182..68857da9 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -2337,6 +2337,12 @@
             final CaptureCallbackHolder holder =
                     CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
 
+            if (holder == null) {
+                Log.e(TAG, String.format("Receive capture error on unknown request ID %d",
+                        requestId));
+                return;
+            }
+
             final CaptureRequest request = holder.getRequest(subsequenceId);
 
             Runnable failureDispatch = null;
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index b377e8d..6165146 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -123,6 +123,7 @@
             IBinder binder = callback.asBinder();
             try {
                 Callback cb = new Callback(callback, cookie);
+                unregister(callback);
                 binder.linkToDeath(cb, 0);
                 mCallbacks.put(binder, cb);
                 return true;
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index e1a9898..a26243c 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -480,7 +480,7 @@
      * matter what user ID the calling process has.
      *
      * <p>Note: This field will be silently ignored when
-     * {@link android.view.inputmethod.InputMethodSystemProperty#MULTI_CLIENT_IME_ENABLED} is
+     * {@link com.android.server.inputmethod.InputMethodSystemProperty#MULTI_CLIENT_IME_ENABLED} is
      * {@code true}.</p>
      *
      * <p>Note also that pseudo handles such as {@link UserHandle#ALL} are not supported.</p>
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index a46580d..18d4d69 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -464,7 +464,9 @@
      * Note that the feature will continue to be supported on older versions of
      * Android as before.
      *
-     * This function does not have any effect.
+     * @deprecated In Android O and afterwards, this function does not have
+     * any effect, the form data will be saved to platform's autofill service
+     * if applicable.
      */
     @Deprecated
     public abstract  void setSaveFormData(boolean save);
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 5091eea..ad35633 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -42,6 +42,8 @@
 import android.view.animation.LinearInterpolator;
 import android.widget.RemoteViews.RemoteView;
 
+import com.android.internal.R;
+
 import java.lang.ref.WeakReference;
 
 @RemoteView
@@ -1241,14 +1243,40 @@
         info.setScrollable(getChildCount() > 1);
         if (isEnabled()) {
             if (getDisplayedChild() < getChildCount() - 1) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+                if (mStackMode == ITEMS_SLIDE_UP) {
+                    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_DOWN);
+                } else {
+                    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_UP);
+                }
             }
             if (getDisplayedChild() > 0) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+                if (mStackMode == ITEMS_SLIDE_UP) {
+                    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_UP);
+                } else {
+                    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_DOWN);
+                }
             }
         }
     }
 
+    private boolean goForward() {
+        if (getDisplayedChild() < getChildCount() - 1) {
+            showNext();
+            return true;
+        }
+        return false;
+    }
+
+    private boolean goBackward() {
+        if (getDisplayedChild() > 0) {
+            showPrevious();
+            return true;
+        }
+        return false;
+    }
+
     /** @hide */
     @Override
     public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
@@ -1260,17 +1288,25 @@
         }
         switch (action) {
             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                if (getDisplayedChild() < getChildCount() - 1) {
-                    showNext();
-                    return true;
-                }
-            } return false;
+                return goForward();
+            }
             case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                if (getDisplayedChild() > 0) {
-                    showPrevious();
-                    return true;
+                return goBackward();
+            }
+            case R.id.accessibilityActionPageUp: {
+                if (mStackMode == ITEMS_SLIDE_UP) {
+                    return goBackward();
+                } else {
+                    return goForward();
                 }
-            } return false;
+            }
+            case R.id.accessibilityActionPageDown: {
+                if (mStackMode == ITEMS_SLIDE_UP) {
+                    return goForward();
+                } else {
+                    return goBackward();
+                }
+            }
         }
         return false;
     }
diff --git a/core/java/com/android/internal/preference/YesNoPreference.java b/core/java/com/android/internal/preference/YesNoPreference.java
index 7abf416..46d14a1 100644
--- a/core/java/com/android/internal/preference/YesNoPreference.java
+++ b/core/java/com/android/internal/preference/YesNoPreference.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.preference;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Parcel;
@@ -40,6 +41,7 @@
         this(context, attrs, defStyleAttr, 0);
     }
 
+    @UnsupportedAppUsage
     public YesNoPreference(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.yesNoPreferenceStyle);
     }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c715577..cf0394d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -58,7 +58,7 @@
 
     void topAppWindowChanged(int displayId, boolean menuVisible);
     void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher);
+            boolean showImeSwitcher, boolean isMultiClientImeEnabled);
     void setWindowState(int display, int window, int state);
 
     void showRecentApps(boolean triggeredFromAltTab);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 598c391..85ae18e 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -49,7 +49,7 @@
     @UnsupportedAppUsage
     void removeIcon(String slot);
     void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher);
+            boolean showImeSwitcher, boolean isMultiClientImeEnabled);
     void expandSettingsPanel(String subPanel);
 
     // ---- Methods below are for use by the status bar policy services ----
diff --git a/core/java/com/android/internal/widget/MediaNotificationView.java b/core/java/com/android/internal/widget/MediaNotificationView.java
index 0d87afa..e7d240a 100644
--- a/core/java/com/android/internal/widget/MediaNotificationView.java
+++ b/core/java/com/android/internal/widget/MediaNotificationView.java
@@ -26,8 +26,6 @@
 import android.widget.ImageView;
 import android.widget.RemoteViews;
 
-import java.util.ArrayList;
-
 /**
  * A TextView that can float around an image on the end.
  *
@@ -44,7 +42,6 @@
     private View mMainColumn;
     private View mMediaContent;
     private int mImagePushIn;
-    private ArrayList<VisibilityChangeListener> mListeners;
 
     public MediaNotificationView(Context context) {
         this(context, null);
@@ -171,48 +168,4 @@
         mMainColumn = findViewById(com.android.internal.R.id.notification_main_column);
         mMediaContent = findViewById(com.android.internal.R.id.notification_media_content);
     }
-
-    @Override
-    public void onVisibilityAggregated(boolean isVisible) {
-        super.onVisibilityAggregated(isVisible);
-        for (int i = 0; i < mListeners.size(); i++) {
-            mListeners.get(i).onAggregatedVisibilityChanged(isVisible);
-        }
-    }
-
-    /**
-     * Add a listener to receive updates on the visibility of this view
-     *
-     * @param listener The listener to add.
-     */
-    public void addVisibilityListener(VisibilityChangeListener listener) {
-        if (mListeners == null) {
-            mListeners = new ArrayList<>();
-        }
-        if (!mListeners.contains(listener)) {
-            mListeners.add(listener);
-        }
-    }
-
-    /**
-     * Remove the specified listener
-     *
-     * @param listener The listener to remove.
-     */
-    public void removeVisibilityListener(VisibilityChangeListener listener) {
-        if (mListeners != null) {
-            mListeners.remove(listener);
-        }
-    }
-
-    /**
-     * Interface for receiving updates when the view's visibility changes
-     */
-    public interface VisibilityChangeListener {
-        /**
-         * Method called when the visibility of this view has changed
-         * @param isVisible true if the view is now visible
-         */
-        void onAggregatedVisibilityChanged(boolean isVisible);
-    }
 }
diff --git a/core/java/com/android/server/job/JobSchedulerInternal.java b/core/java/com/android/server/job/JobSchedulerInternal.java
index 425ec47..dbf3b98 100644
--- a/core/java/com/android/server/job/JobSchedulerInternal.java
+++ b/core/java/com/android/server/job/JobSchedulerInternal.java
@@ -16,7 +16,6 @@
 
 package com.android.server.job;
 
-import android.annotation.UserIdInt;
 import android.app.job.JobInfo;
 
 import java.util.List;
@@ -27,31 +26,6 @@
  */
 public interface JobSchedulerInternal {
 
-    // Bookkeeping about app standby bucket scheduling
-
-    /**
-     * The current bucket heartbeat ordinal
-     */
-    long currentHeartbeat();
-
-    /**
-     * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
-     */
-    long nextHeartbeatForBucket(int bucket);
-
-    /**
-     * Heartbeat ordinal for the given app.  This is typically the heartbeat at which
-     * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
-     * jobs in a long time is immediately runnable even if the app is bucketed into
-     * an infrequent time allocation.
-     */
-    public long baseHeartbeatForApp(String packageName, @UserIdInt int userId, int appBucket);
-
-    /**
-     * Tell the scheduler when a JobServiceContext starts running a job in an app
-     */
-    void noteJobStart(String packageName, int userId);
-
     /**
      * Returns a list of pending jobs scheduled by the system service.
      */
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index c534aa4..5d4be55 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -38,10 +38,10 @@
 
     optional ConstantsProto settings = 1;
 
-    optional int32 current_heartbeat = 14;
-    repeated int32 next_heartbeat = 15;
-    optional int64 last_heartbeat_time_millis = 16;
-    optional int64 next_heartbeat_time_millis = 17;
+    reserved 14; // current_heartbeat
+    reserved 15; // next_heartbeat
+    reserved 16; // last_heartbeat_time_millis
+    reserved 17; // next_heartbeat_time_millis
     optional bool in_parole = 18;
     optional bool in_thermal = 19;
 
@@ -64,7 +64,7 @@
         optional bool is_uid_backing_up = 7;
         optional bool is_component_present = 8;
 
-        optional int64 last_run_heartbeat = 9;
+        reserved 9; // last_run_heartbeat
     }
     repeated RegisteredJob registered_jobs = 3;
 
@@ -214,13 +214,13 @@
     // assignment. This should be prime relative to common time interval lengths
     // such as a quarter-hour or day, so that the heartbeat drifts relative to
     // wall-clock milestones.
-    optional int64 standby_heartbeat_time_ms = 19;
+    reserved 19; // standby_heartbeat_time_ms
     // Mapping: standby bucket -> number of heartbeats between each sweep of
     // that bucket's jobs.
     // Bucket assignments as recorded in the JobStatus objects are normalized to
     // be indices into this array, rather than the raw constants used by
     // AppIdleHistory.
-    repeated int32 standby_beats = 20;
+    reserved 20; // standby_beats
     // The fraction of a job's running window that must pass before we
     // consider running it when the network is congested.
     optional double conn_congestion_delay_frac = 21;
@@ -229,7 +229,7 @@
     optional double conn_prefetch_relax_frac = 22;
     // Whether to use heartbeats or rolling window for quota management. True
     // will use heartbeats, false will use a rolling window.
-    optional bool use_heartbeats = 23;
+    reserved 23; // use_heartbeats
 
     message QuotaController {
         option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/stats/textclassifier/textclassifier_enums.proto b/core/proto/android/stats/textclassifier/textclassifier_enums.proto
new file mode 100644
index 0000000..1885f19
--- /dev/null
+++ b/core/proto/android/stats/textclassifier/textclassifier_enums.proto
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto2";
+package android.stats.textclassifier;
+option java_multiple_files = true;
+
+enum EventType {
+    TYPE_UNKNOWN = 0;
+    // User started a new selection.
+    SELECTION_STARTED = 1;
+    // User modified an existing selection.
+    SELECTION_MODIFIED = 2;
+    // Smart selection triggered for a single token (word).
+    SMART_SELECTION_SINGLE = 3;
+    // Smart selection triggered spanning multiple tokens (words).
+    SMART_SELECTION_MULTI = 4;
+    // Something else other than user or the default TextClassifier triggered a selection.
+    AUTO_SELECTION = 5;
+    // Smart actions shown to the user.
+    ACTIONS_SHOWN = 6;
+    // User clicked a link.
+    LINK_CLICKED = 7;
+    // User typed over the selection.
+    OVERTYPE = 8;
+    // User clicked on Copy action.
+    COPY_ACTION = 9;
+    // User clicked on Paste action.
+    PASTE_ACTION = 10;
+    // User clicked on Cut action.
+    CUT_ACTION = 11;
+    // User clicked on Share action.
+    SHARE_ACTION = 12;
+    // User clicked on a Smart action.
+    SMART_ACTION = 13;
+    // User dragged+dropped the selection.
+    SELECTION_DRAG = 14;
+    // Selection is destroyed.
+    SELECTION_DESTROYED = 15;
+    // User clicked on a custom action.
+    OTHER_ACTION = 16;
+    // User clicked on Select All action
+    SELECT_ALL = 17;
+    // User reset the smart selection.
+    SELECTION_RESET = 18;
+    // User composed a reply.
+    MANUAL_REPLY = 19;
+    // TextClassifier generated some actions
+    ACTIONS_GENERATED = 20;
+}
+
+enum WidgetType {
+    WIDGET_TYPE_UNKNOWN = 0;
+    // Standard TextView
+    WIDGET_TYPE_TEXTVIEW = 1;
+    // EditText
+    WIDGET_TYPE_EDITTEXT = 2;
+    // Not selectable textview
+    WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = 3;
+    // Standard Webview
+    WIDGET_TYPE_WEBVIEW = 4;
+    // Editable TextView
+    WIDGET_TYPE_EDIT_WEBVIEW = 5;
+    // Custom text widget
+    WIDGET_TYPE_CUSTOM_TEXTVIEW = 6;
+    // Custom editable text widget.
+    WIDGET_TYPE_CUSTOM_EDITTEXT = 7;
+    // Non-selectable text widget.
+    WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = 8;
+    // Notification
+    WIDGET_TYPE_NOTIFICATION = 9;
+}
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 0eba2ed..46873b9 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -43,8 +43,8 @@
 
         // WorkSource can be updated.
         p.writeInterfaceToken(INTERFACE_TOKEN_1);
-        assertEquals(true, p.replaceCallingWorkSourceUid(WORK_SOURCE_1));
-        assertEquals(WORK_SOURCE_1, p.readCallingWorkSourceUid());
+        assertEquals(true, p.replaceCallingWorkSourceUid(WORK_SOURCE_2));
+        assertEquals(WORK_SOURCE_2, p.readCallingWorkSourceUid());
 
         // WorkSource can be updated to unset value.
         assertEquals(true, p.replaceCallingWorkSourceUid(Binder.UNSET_WORKSOURCE));
@@ -56,18 +56,16 @@
     @Test
     public void testCallingWorkSourceUidAfterEnforce() {
         Parcel p = Parcel.obtain();
-        // Write headers manually so that we do not invoke #writeInterfaceToken.
-        p.writeInt(1);  // strict mode header
-        p.writeInt(WORK_SOURCE_1);  // worksource header.
-        p.writeString(INTERFACE_TOKEN_1);  // interface token.
+        p.writeInterfaceToken(INTERFACE_TOKEN_1);
+        assertEquals(true, p.replaceCallingWorkSourceUid(WORK_SOURCE_1));
         p.setDataPosition(0);
 
         p.enforceInterface(INTERFACE_TOKEN_1);
         assertEquals(WORK_SOURCE_1, p.readCallingWorkSourceUid());
 
         // WorkSource can be updated.
-        assertEquals(true, p.replaceCallingWorkSourceUid(WORK_SOURCE_1));
-        assertEquals(WORK_SOURCE_1, p.readCallingWorkSourceUid());
+        assertEquals(true, p.replaceCallingWorkSourceUid(WORK_SOURCE_2));
+        assertEquals(WORK_SOURCE_2, p.readCallingWorkSourceUid());
 
         p.recycle();
     }
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index 3361fa2..1d294d5 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -27,7 +27,7 @@
 
 /**
  * RectF holds four float coordinates for a rectangle. The rectangle is
- * represented by the coordinates of its 4 edges (left, top, right bottom).
+ * represented by the coordinates of its 4 edges (left, top, right, bottom).
  * These fields can be accessed directly. Use width() and height() to retrieve
  * the rectangle's width and height. Note: most methods do not check to see that
  * the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index a571630..8ae972b 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -29,10 +29,13 @@
  */
 interface ILocationProvider {
 
+    @UnsupportedAppUsage
     oneway void setLocationProviderManager(in ILocationProviderManager manager);
 
+    @UnsupportedAppUsage
     oneway void setRequest(in ProviderRequest request, in WorkSource ws);
 
+    @UnsupportedAppUsage
     oneway void sendExtraCommand(String command, in Bundle extras);
 
     // --- deprecated and will be removed the future ---
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 59754e0..81d1ea5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1434,7 +1434,7 @@
 
     void update(@Nullable WifiConfiguration config) {
         mConfig = config;
-        if (mConfig != null) {
+        if (mConfig != null && !isPasspoint()) {
             ssid = removeDoubleQuotes(mConfig.SSID);
         }
         networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index a7b4444..2727880 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -366,8 +366,10 @@
             checkProgressUpdated(mInfo, (int) progress);
         }
 
-        // TODO(b/127431371): Add error code handling for bugreport API errors.
-        // Logging errors and removing progress notification for now.
+        /**
+         * Logs errors and stops the service on which this bugreport was running.
+         * Also stops progress notification (if any).
+         */
         @Override
         public void onError(@BugreportErrorCode int errorCode) {
             trackInfoWithId();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index df3f36e..3f598ff 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -95,6 +95,7 @@
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.phone.UnlockMethodCache;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.EmergencyDialerConstants;
@@ -1523,6 +1524,8 @@
         private boolean mShowing;
         private float mScrimAlpha;
         private ResetOrientationData mResetOrientationData;
+        private boolean mHadTopUi;
+        private final StatusBarWindowController mStatusBarWindowController;
 
         ActionsDialog(Context context, MyAdapter adapter,
                 GlobalActionsPanelPlugin.PanelViewController plugin) {
@@ -1531,6 +1534,7 @@
             mAdapter = adapter;
             mColorExtractor = Dependency.get(SysuiColorExtractor.class);
             mStatusBarService = Dependency.get(IStatusBarService.class);
+            mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
 
             // Window initialization
             Window window = getWindow();
@@ -1706,6 +1710,8 @@
         public void show() {
             super.show();
             mShowing = true;
+            mHadTopUi = mStatusBarWindowController.getForceHasTopUi();
+            mStatusBarWindowController.setForceHasTopUi(true);
             mBackgroundDrawable.setAlpha(0);
             mGlobalActionsLayout.setTranslationX(mGlobalActionsLayout.getAnimationOffsetX());
             mGlobalActionsLayout.setTranslationY(mGlobalActionsLayout.getAnimationOffsetY());
@@ -1738,7 +1744,7 @@
                     .translationX(mGlobalActionsLayout.getAnimationOffsetX())
                     .translationY(mGlobalActionsLayout.getAnimationOffsetY())
                     .setDuration(300)
-                    .withEndAction(super::dismiss)
+                    .withEndAction(this::completeDismiss)
                     .setInterpolator(new LogAccelerateInterpolator())
                     .setUpdateListener(animation -> {
                         int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
@@ -1751,10 +1757,15 @@
         }
 
         void dismissImmediately() {
-            super.dismiss();
             mShowing = false;
             dismissPanel();
             resetOrientation();
+            completeDismiss();
+        }
+
+        private void completeDismiss() {
+            mStatusBarWindowController.setForceHasTopUi(mHadTopUi);
+            super.dismiss();
         }
 
         private void dismissPanel() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6329af5..fa0fe13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -43,7 +43,6 @@
 import android.os.Message;
 import android.util.Pair;
 import android.util.SparseArray;
-import android.view.inputmethod.InputMethodSystemProperty;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -481,7 +480,7 @@
 
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher) {
+            boolean showImeSwitcher, boolean isMultiClientImeEnabled) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
             SomeArgs args = SomeArgs.obtain();
@@ -489,6 +488,7 @@
             args.argi2 = vis;
             args.argi3 = backDisposition;
             args.argi4 = showImeSwitcher ? 1 : 0;
+            args.argi5 = isMultiClientImeEnabled ? 1 : 0;
             args.arg1 = token;
             Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, args);
             m.sendToTarget();
@@ -801,11 +801,10 @@
     }
 
     private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher) {
+            boolean showImeSwitcher, boolean isMultiClientImeEnabled) {
         if (displayId == INVALID_DISPLAY) return;
 
-        if (!InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED
-                && mLastUpdatedImeDisplayId != displayId
+        if (!isMultiClientImeEnabled && mLastUpdatedImeDisplayId != displayId
                 && mLastUpdatedImeDisplayId != INVALID_DISPLAY) {
             // Set previous NavBar's IME window status as invisible when IME
             // window switched to another display for single-session IME case.
@@ -891,7 +890,8 @@
                     args = (SomeArgs) msg.obj;
                     handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
                             args.argi2 /* vis */, args.argi3 /* backDisposition */,
-                            args.argi4 != 0 /* showImeSwitcher */);
+                            args.argi4 != 0 /* showImeSwitcher */,
+                            args.argi5 != 0 /* isMultiClientImeEnabled */);
                     break;
                 case MSG_SHOW_RECENT_APPS:
                     for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 38f9bff..20e8b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -39,7 +39,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.widget.MediaNotificationView;
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.TransformableView;
@@ -68,7 +67,6 @@
     private View mSeekBarView;
     private Context mContext;
     private MetricsLogger mMetricsLogger;
-    private boolean mIsViewVisible;
 
     @VisibleForTesting
     protected SeekBar.OnSeekBarChangeListener mSeekListener =
@@ -90,33 +88,11 @@
         }
     };
 
-    MediaNotificationView.VisibilityChangeListener mVisibilityListener =
-            new MediaNotificationView.VisibilityChangeListener() {
-        @Override
-        public void onAggregatedVisibilityChanged(boolean isVisible) {
-            mIsViewVisible = isVisible;
-            if (isVisible) {
-                // Restart timer if we're currently playing and didn't already have one going
-                PlaybackState state = mMediaController.getPlaybackState();
-                if (state != null && state.getState() == PlaybackState.STATE_PLAYING
-                        && mSeekBarTimer == null && mSeekBarView != null
-                        && mSeekBarView.getVisibility() != View.GONE) {
-                    startTimer();
-                }
-            } else {
-                clearTimer();
-            }
-        }
-    };
-
     private MediaController.Callback mMediaCallback = new MediaController.Callback() {
         @Override
         public void onSessionDestroyed() {
             clearTimer();
             mMediaController.unregisterCallback(this);
-            if (mView instanceof MediaNotificationView) {
-                ((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener);
-            }
         }
 
         @Override
@@ -150,16 +126,10 @@
         mContext = ctx;
         mMediaManager = Dependency.get(NotificationMediaManager.class);
         mMetricsLogger = Dependency.get(MetricsLogger.class);
-
-        if (mView instanceof MediaNotificationView) {
-            MediaNotificationView mediaView = (MediaNotificationView) mView;
-            mediaView.addVisibilityListener(mVisibilityListener);
-        }
     }
 
     private void resolveViews() {
         mActions = mView.findViewById(com.android.internal.R.id.media_actions);
-        mIsViewVisible = mView.isShown();
 
         final MediaSession.Token token = mRow.getEntry().notification.getNotification().extras
                 .getParcelable(Notification.EXTRA_MEDIA_SESSION);
@@ -238,19 +208,18 @@
 
     private void startTimer() {
         clearTimer();
-        if (mIsViewVisible) {
-            mSeekBarTimer = new Timer(true /* isDaemon */);
-            mSeekBarTimer.schedule(new TimerTask() {
-                @Override
-                public void run() {
-                    mHandler.post(mOnUpdateTimerTick);
-                }
-            }, 0, PROGRESS_UPDATE_INTERVAL);
-        }
+        mSeekBarTimer = new Timer(true /* isDaemon */);
+        mSeekBarTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                mHandler.post(mOnUpdateTimerTick);
+            }
+        }, 0, PROGRESS_UPDATE_INTERVAL);
     }
 
     private void clearTimer() {
         if (mSeekBarTimer != null) {
+            // TODO: also trigger this when the notification panel is collapsed
             mSeekBarTimer.cancel();
             mSeekBarTimer.purge();
             mSeekBarTimer = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 70d3bff..832ea9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.content.pm.PackageManager
 import android.hardware.biometrics.BiometricSourceType
-import android.hardware.face.FaceManager
 import android.provider.Settings
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -34,6 +33,7 @@
 
     private val unlockMethodCache: UnlockMethodCache
     private val statusBarStateController: StatusBarStateController
+    private var hasFaceFeature: Boolean
 
     /**
      * The pending unlock type which is set if the bypass was blocked when it happened.
@@ -71,11 +71,8 @@
         unlockMethodCache = UnlockMethodCache.getInstance(context)
         this.statusBarStateController = statusBarStateController
 
-        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
-            return
-        }
-        val faceManager = context.getSystemService(FaceManager::class.java)
-        if (faceManager?.isHardwareDetected != true) {
+        hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)
+        if (!hasFaceFeature) {
             return
         }
 
@@ -165,7 +162,7 @@
         pw.print("  isPulseExpanding: "); pw.println(isPulseExpanding)
         pw.print("  launchingAffordance: "); pw.println(launchingAffordance)
         pw.print("  qSExpanded: "); pw.println(qSExpanded)
-        pw.print("  bouncerShowing: "); pw.println(bouncerShowing)
+        pw.print("  hasFaceFeature: "); pw.println(hasFaceFeature)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index c0a1b12..1d4d0bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -103,10 +103,11 @@
         final boolean userSwitcherEnabled = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.USER_SWITCHER_ENABLED, 0) != 0;
 
-        if (!UserManager.supportsMultipleUsers()
-                || mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)
+        // TODO(b/138661450) Move IPC calls to background
+        if (!userSwitcherEnabled
+                || !UserManager.supportsMultipleUsers()
                 || UserManager.isDeviceInDemoMode(mContext)
-                || !userSwitcherEnabled) {
+                || mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) {
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index d0626ed..9652992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -3098,10 +3098,8 @@
 
     /**
      * Whether the camera application can be launched for the camera launch gesture.
-     *
-     * @param keyguardIsShowing whether keyguard is being shown
      */
-    public boolean canCameraGestureBeLaunched(boolean keyguardIsShowing) {
+    public boolean canCameraGestureBeLaunched() {
         if (!mStatusBar.isCameraAllowedByAdmin()) {
             return false;
         }
@@ -3110,7 +3108,7 @@
         String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null)
                 ? null : resolveInfo.activityInfo.packageName;
         return packageToLaunch != null &&
-                (keyguardIsShowing || !isForegroundApp(packageToLaunch))
+                (mBarState != StatusBarState.SHADE || !isForegroundApp(packageToLaunch))
                 && !mAffordanceHelper.isSwipingInProgress();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index f15b601..17a3a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3785,10 +3785,8 @@
             mLaunchCameraOnFinishedGoingToSleep = true;
             return;
         }
-        if (!mNotificationPanel.canCameraGestureBeLaunched(
-                mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
-            if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now, mExpandedVisible: " +
-                    mExpandedVisible);
+        if (!mNotificationPanel.canCameraGestureBeLaunched()) {
+            if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now");
             return;
         }
         if (!mDeviceInteractive) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index ebac855..0ef981b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -76,13 +76,13 @@
     private final WindowManager mWindowManager;
     private final IActivityManager mActivityManager;
     private final DozeParameters mDozeParameters;
-    private final WindowManager.LayoutParams mLpChanged;
+    private final LayoutParams mLpChanged;
     private final boolean mKeyguardScreenRotation;
     private final long mLockScreenDisplayTimeout;
     private final Display.Mode mKeyguardDisplayMode;
     private final KeyguardBypassController mKeyguardBypassController;
     private ViewGroup mStatusBarView;
-    private WindowManager.LayoutParams mLp;
+    private LayoutParams mLp;
     private boolean mHasTopUi;
     private boolean mHasTopUiChanged;
     private int mBarHeight;
@@ -117,7 +117,7 @@
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
         mDozeParameters = dozeParameters;
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
-        mLpChanged = new WindowManager.LayoutParams();
+        mLpChanged = new LayoutParams();
         mKeyguardBypassController = keyguardBypassController;
         mLockScreenDisplayTimeout = context.getResources()
                 .getInteger(R.integer.config_lockScreenDisplayTimeout);
@@ -171,19 +171,19 @@
         // Now that the status bar window encompasses the sliding panel and its
         // translucent backdrop, the entire thing is made TRANSLUCENT and is
         // hardware-accelerated.
-        mLp = new WindowManager.LayoutParams(
+        mLp = new LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 barHeight,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+                LayoutParams.TYPE_STATUS_BAR,
+                LayoutParams.FLAG_NOT_FOCUSABLE
+                        | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+                        | LayoutParams.FLAG_SPLIT_TOUCH
+                        | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                 PixelFormat.TRANSLUCENT);
         mLp.token = new Binder();
         mLp.gravity = Gravity.TOP;
-        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+        mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
         mLp.setTitle("StatusBar");
         mLp.packageName = mContext.getPackageName();
         mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -216,9 +216,9 @@
 
     private void applyKeyguardFlags(State state) {
         if (state.keyguardShowing) {
-            mLpChanged.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+            mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_KEYGUARD;
         } else {
-            mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+            mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_KEYGUARD;
         }
 
         final boolean scrimsOccludingWallpaper =
@@ -226,9 +226,9 @@
         final boolean keyguardOrAod = state.keyguardShowing
                 || (state.dozing && mDozeParameters.getAlwaysOn());
         if (keyguardOrAod && !state.backdropShowing && !scrimsOccludingWallpaper) {
-            mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+            mLpChanged.flags |= LayoutParams.FLAG_SHOW_WALLPAPER;
         } else {
-            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+            mLpChanged.flags &= ~LayoutParams.FLAG_SHOW_WALLPAPER;
         }
 
         if (state.dozing) {
@@ -267,17 +267,17 @@
         if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
                 || ENABLE_REMOTE_INPUT && state.remoteInputActive
                 || state.bubbleExpanded) {
-            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+            mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
+            mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
-            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-            mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+            mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
+            mLpChanged.flags |= LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else {
-            mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+            mLpChanged.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
+            mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         }
 
-        mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+        mLpChanged.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
     }
 
     private void applyForceShowNavigationFlag(State state) {
@@ -335,19 +335,19 @@
                 && state.statusBarState == StatusBarState.KEYGUARD
                 && !state.qsExpanded && !state.forceUserActivity) {
             mLpChanged.inputFeatures |=
-                    WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+                    LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
         } else {
             mLpChanged.inputFeatures &=
-                    ~WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+                    ~LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
         }
     }
 
     private void applyStatusBarColorSpaceAgnosticFlag(State state) {
         if (!isExpanded(state)) {
-            mLpChanged.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+            mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
         } else {
             mLpChanged.privateFlags &=
-                    ~WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+                    ~LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
         }
     }
 
@@ -396,16 +396,16 @@
             mLpChanged.privateFlags |= WindowManager
                     .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
         } else {
-            mLpChanged.privateFlags &= ~WindowManager
-                    .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+            mLpChanged.privateFlags
+                    &= ~LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
         }
     }
 
     private void applyModalFlag(State state) {
         if (state.headsUpShowing) {
-            mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+            mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCH_MODAL;
         } else {
-            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+            mLpChanged.flags &= ~LayoutParams.FLAG_NOT_TOUCH_MODAL;
         }
     }
 
@@ -413,12 +413,12 @@
         if (state.forceDozeBrightness) {
             mLpChanged.screenBrightness = mScreenBrightnessDoze;
         } else {
-            mLpChanged.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
+            mLpChanged.screenBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
         }
     }
 
     private void applyHasTopUi(State state) {
-        mHasTopUiChanged = isExpanded(state);
+        mHasTopUiChanged = state.forceHasTopUi || isExpanded(state);
     }
 
     private void applyNotTouchable(State state) {
@@ -642,6 +642,15 @@
         apply(mCurrentState);
     }
 
+    public boolean getForceHasTopUi() {
+        return mCurrentState.forceHasTopUi;
+    }
+
+    public void setForceHasTopUi(boolean forceHasTopUi) {
+        mCurrentState.forceHasTopUi = forceHasTopUi;
+        apply(mCurrentState);
+    }
+
     private static class State {
         boolean keyguardShowing;
         boolean keyguardOccluded;
@@ -663,6 +672,7 @@
         boolean notTouchable;
         boolean bubblesShowing;
         boolean bubbleExpanded;
+        boolean forceHasTopUi;
 
         /**
          * The {@link StatusBar} state from the status bar.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 84a7d4f..1e1f2156 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -129,7 +129,7 @@
 
     @Test
     public void testShowImeButton() {
-        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
+        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true, false);
         waitForIdleSync();
         verify(mCallbacks).setImeWindowStatus(
                 eq(DEFAULT_DISPLAY), eq(null), eq(1), eq(2), eq(true));
@@ -137,7 +137,7 @@
 
     @Test
     public void testShowImeButtonForSecondaryDisplay() {
-        mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true);
+        mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true, false);
         waitForIdleSync();
         verify(mCallbacks).setImeWindowStatus(
                 eq(SECONDARY_DISPLAY), eq(null), eq(1), eq(2), eq(true));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 3da9a4b..db8af39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -183,7 +183,7 @@
 
         // Set IME window status for default NavBar.
         mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
-                BACK_DISPOSITION_DEFAULT, true);
+                BACK_DISPOSITION_DEFAULT, true, false);
         Handler.getMain().runWithScissors(() -> { }, 500);
 
         // Verify IME window state will be updated in default NavBar & external NavBar state reset.
@@ -194,7 +194,7 @@
 
         // Set IME window status for external NavBar.
         mCommandQueue.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null,
-                IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true);
+                IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true, false);
         Handler.getMain().runWithScissors(() -> { }, 500);
 
         // Verify IME window state will be updated in external NavBar & default NavBar state reset.
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index d923bed..6b88f5a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -398,7 +398,8 @@
             if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
                 return null;
             }
-            AccessibilityWindowInfo window = mA11yWindowManager.findA11yWindowInfoById(windowId);
+            AccessibilityWindowInfo window =
+                    mA11yWindowManager.findA11yWindowInfoByIdLocked(windowId);
             if (window != null) {
                 AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window);
                 windowClone.setConnectionId(mId);
@@ -1362,11 +1363,11 @@
                     || (action == ACTION_CLEAR_ACCESSIBILITY_FOCUS);
             if (!isA11yFocusAction) {
                 final WindowInfo windowInfo =
-                        mA11yWindowManager.findWindowInfoById(resolvedWindowId);
+                        mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId);
                 if (windowInfo != null) activityToken = windowInfo.activityToken;
             }
             final AccessibilityWindowInfo a11yWindowInfo =
-                    mA11yWindowManager.findA11yWindowInfoById(resolvedWindowId);
+                    mA11yWindowManager.findA11yWindowInfoByIdLocked(resolvedWindowId);
             if (a11yWindowInfo != null && a11yWindowInfo.isInPictureInPictureMode()
                     && mA11yWindowManager.getPictureInPictureActionReplacingConnection() != null
                     && !isA11yFocusAction) {
@@ -1419,11 +1420,13 @@
             int interactionId, int interrogatingPid, long interrogatingTid) {
         final RemoteAccessibilityConnection pipActionReplacingConnection =
                 mA11yWindowManager.getPictureInPictureActionReplacingConnection();
-        final AccessibilityWindowInfo windowInfo =
-                mA11yWindowManager.findA11yWindowInfoById(resolvedWindowId);
-        if ((windowInfo == null) || !windowInfo.isInPictureInPictureMode()
+        synchronized (mLock) {
+            final AccessibilityWindowInfo windowInfo =
+                    mA11yWindowManager.findA11yWindowInfoByIdLocked(resolvedWindowId);
+            if ((windowInfo == null) || !windowInfo.isInPictureInPictureMode()
                 || (pipActionReplacingConnection == null)) {
-            return originalCallback;
+                return originalCallback;
+            }
         }
         return new ActionReplacingCallback(originalCallback,
                 pipActionReplacingConnection.getRemote(), interactionId,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 81485369..5ba777c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -542,7 +542,7 @@
             if (event.getWindowId() ==
                 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID) {
                 // The replacer window isn't shown to services. Move its events into the pip.
-                AccessibilityWindowInfo pip = mA11yWindowManager.getPictureInPictureWindow();
+                AccessibilityWindowInfo pip = mA11yWindowManager.getPictureInPictureWindowLocked();
                 if (pip != null) {
                     int pipId = pip.getId();
                     event.setWindowId(pipId);
@@ -771,7 +771,7 @@
             if (resolvedUserId != mCurrentUserId) {
                 return null;
             }
-            if (mA11yWindowManager.findA11yWindowInfoById(windowId) == null) {
+            if (mA11yWindowManager.findA11yWindowInfoByIdLocked(windowId) == null) {
                 return null;
             }
             return mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(userId, windowId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 1e224cf..315d6fa 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -429,7 +429,7 @@
         if (windowId == mAccessibilityWindowManager.getActiveWindowId(userId)) {
             return true;
         }
-        return mAccessibilityWindowManager.findA11yWindowInfoById(windowId) != null;
+        return mAccessibilityWindowManager.findA11yWindowInfoByIdLocked(windowId) != null;
     }
 
     private boolean isShellAllowedToRetrieveWindowLocked(int userId, int windowId) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index c129291..9687098 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -52,13 +52,10 @@
 import java.util.List;
 
 /**
- * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to receive
- * {@link WindowInfo}s from window manager when there's an accessibility change in window. It also
- * provides APIs for accessibility manager to manage {@link AccessibilityWindowInfo}s and
+ * This class provides APIs for accessibility manager to manage {@link AccessibilityWindowInfo}s and
  * {@link WindowInfo}s.
  */
-public class AccessibilityWindowManager
-        implements WindowManagerInternal.WindowsForAccessibilityCallback {
+public class AccessibilityWindowManager {
     private static final String LOG_TAG = "AccessibilityWindowManager";
     private static final boolean DEBUG = false;
 
@@ -71,9 +68,6 @@
     private final AccessibilitySecurityPolicy mSecurityPolicy;
     private final AccessibilityUserManager mAccessibilityUserManager;
 
-    private final SparseArray<AccessibilityWindowInfo> mA11yWindowInfoById = new SparseArray<>();
-    private final SparseArray<WindowInfo> mWindowInfoById = new SparseArray<>();
-
     // Connections and window tokens for cross-user windows
     private final SparseArray<RemoteAccessibilityConnection>
             mGlobalInteractionConnections = new SparseArray<>();
@@ -84,9 +78,6 @@
             mInteractionConnections = new SparseArray<>();
     private final SparseArray<SparseArray<IBinder>> mWindowTokens = new SparseArray<>();
 
-    private final List<WindowInfo> mCachedWindowInfos = new ArrayList<>();
-    private List<AccessibilityWindowInfo> mWindows;
-
     private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection;
 
     private int mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -95,15 +86,641 @@
     private long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
 
     private boolean mTouchInteractionInProgress;
-    private boolean mHasWatchOutsideTouchWindow;
-    private boolean mTrackingWindows = false;
 
+    // TO-DO [Multi-Display] : make DisplayWindowObserver to plural
+    private DisplayWindowsObserver mDisplayWindowsObserver;
+
+    /**
+     * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to
+     * receive {@link WindowInfo}s from window manager when there's an accessibility change in
+     * window and holds window lists information per display.
+     */
+    private final class DisplayWindowsObserver implements
+            WindowManagerInternal.WindowsForAccessibilityCallback {
+
+        private final int mDisplayId;
+        private final SparseArray<AccessibilityWindowInfo> mA11yWindowInfoById =
+                new SparseArray<>();
+        private final SparseArray<WindowInfo> mWindowInfoById = new SparseArray<>();
+        private final List<WindowInfo> mCachedWindowInfos = new ArrayList<>();
+        private List<AccessibilityWindowInfo> mWindows;
+        private boolean mTrackingWindows = false;
+        private boolean mHasWatchOutsideTouchWindow;
+
+        /**
+         * Constructor for DisplayWindowsObserver.
+         */
+        DisplayWindowsObserver(int displayId) {
+            mDisplayId = displayId;
+        }
+
+        /**
+         * Starts tracking windows changes from window manager by registering callback.
+         *
+         * @return true if callback registers successful.
+         */
+        boolean startTrackingWindowsLocked() {
+            boolean result = true;
+
+            if (!mTrackingWindows) {
+                // Turns on the flag before setup the callback.
+                // In some cases, onWindowsForAccessibilityChanged will be called immediately in
+                // setWindowsForAccessibilityCallback. We'll lost windows if flag is false.
+                mTrackingWindows = true;
+                result = mWindowManagerInternal.setWindowsForAccessibilityCallback(
+                        mDisplayId, this);
+                if (!result) {
+                    mTrackingWindows = false;
+                    Slog.w(LOG_TAG, "set windowsObserver callbacks fail, displayId:"
+                            + mDisplayId);
+                }
+            }
+            return result;
+        }
+
+        /**
+         * Stops tracking windows changes from window manager, and clear all windows info.
+         */
+        void stopTrackingWindowsLocked() {
+            if (mTrackingWindows) {
+                mWindowManagerInternal.setWindowsForAccessibilityCallback(
+                        mDisplayId, null);
+                mTrackingWindows = false;
+                clearWindowsLocked();
+            }
+        }
+
+        /**
+         * Returns true if windows changes tracking.
+         *
+         * @return true if windows changes tracking
+         */
+        boolean isTrackingWindowsLocked() {
+            return mTrackingWindows;
+        }
+
+        /**
+         * Returns accessibility windows.
+         * @return accessibility windows.
+         */
+        @Nullable
+        List<AccessibilityWindowInfo> getWindowListLocked() {
+            return mWindows;
+        }
+
+        /**
+         * Returns accessibility window info according to given windowId.
+         *
+         * @param windowId The windowId
+         * @return The accessibility window info
+         */
+        @Nullable
+        AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) {
+            return mA11yWindowInfoById.get(windowId);
+        }
+
+        /**
+         * Returns the window info according to given windowId.
+         *
+         * @param windowId The windowId
+         * @return The window info
+         */
+        @Nullable
+        WindowInfo findWindowInfoByIdLocked(int windowId) {
+            return mWindowInfoById.get(windowId);
+        }
+
+        /**
+         * Returns {@link AccessibilityWindowInfo} of PIP window.
+         *
+         * @return PIP accessibility window info
+         */
+        @Nullable
+        AccessibilityWindowInfo getPictureInPictureWindowLocked() {
+            if (mWindows != null) {
+                final int windowCount = mWindows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    final AccessibilityWindowInfo window = mWindows.get(i);
+                    if (window.isInPictureInPictureMode()) {
+                        return window;
+                    }
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Sets the active flag of the window according to given windowId, others set to inactive.
+         *
+         * @param windowId The windowId
+         */
+        void setActiveWindowLocked(int windowId) {
+            if (mWindows != null) {
+                final int windowCount = mWindows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    AccessibilityWindowInfo window = mWindows.get(i);
+                    if (window.getId() == windowId) {
+                        window.setActive(true);
+                        mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
+                                AccessibilityEvent.obtainWindowsChangedEvent(windowId,
+                                        AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
+                    } else {
+                        window.setActive(false);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Sets the window accessibility focused according to given windowId, others set
+         * unfocused.
+         *
+         * @param windowId The windowId
+         */
+        void setAccessibilityFocusedWindowLocked(int windowId) {
+            if (mWindows != null) {
+                final int windowCount = mWindows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    AccessibilityWindowInfo window = mWindows.get(i);
+                    if (window.getId() == windowId) {
+                        window.setAccessibilityFocused(true);
+                        mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
+                                AccessibilityEvent.obtainWindowsChangedEvent(
+                                        windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+
+                    } else {
+                        window.setAccessibilityFocused(false);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Computes partial interactive region of given windowId.
+         *
+         * @param windowId The windowId
+         * @param outRegion The output to which to write the bounds.
+         * @return true if outRegion is not empty.
+         */
+        boolean computePartialInteractiveRegionForWindowLocked(int windowId,
+                @NonNull Region outRegion) {
+            if (mWindows == null) {
+                return false;
+            }
+
+            // Windows are ordered in z order so start from the bottom and find
+            // the window of interest. After that all windows that cover it should
+            // be subtracted from the resulting region. Note that for accessibility
+            // we are returning only interactive windows.
+            Region windowInteractiveRegion = null;
+            boolean windowInteractiveRegionChanged = false;
+
+            final int windowCount = mWindows.size();
+            final Region currentWindowRegions = new Region();
+            for (int i = windowCount - 1; i >= 0; i--) {
+                AccessibilityWindowInfo currentWindow = mWindows.get(i);
+                if (windowInteractiveRegion == null) {
+                    if (currentWindow.getId() == windowId) {
+                        currentWindow.getRegionInScreen(currentWindowRegions);
+                        outRegion.set(currentWindowRegions);
+                        windowInteractiveRegion = outRegion;
+                        continue;
+                    }
+                } else if (currentWindow.getType()
+                        != AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY) {
+                    currentWindow.getRegionInScreen(currentWindowRegions);
+                    if (windowInteractiveRegion.op(currentWindowRegions, Region.Op.DIFFERENCE)) {
+                        windowInteractiveRegionChanged = true;
+                    }
+                }
+            }
+
+            return windowInteractiveRegionChanged;
+        }
+
+        List<Integer> getWatchOutsideTouchWindowIdLocked(int targetWindowId) {
+            final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId);
+            if (targetWindow != null && mHasWatchOutsideTouchWindow) {
+                final List<Integer> outsideWindowsId = new ArrayList<>();
+                for (int i = 0; i < mWindowInfoById.size(); i++) {
+                    final WindowInfo window = mWindowInfoById.valueAt(i);
+                    if (window != null && window.layer < targetWindow.layer
+                            && window.hasFlagWatchOutsideTouch) {
+                        outsideWindowsId.add(mWindowInfoById.keyAt(i));
+                    }
+                }
+                return outsideWindowsId;
+            }
+            return Collections.emptyList();
+        }
+
+        /**
+         * Callbacks from window manager when there's an accessibility change in windows.
+         *
+         * @param forceSend Send the windows for accessibility even if they haven't changed.
+         * @param windows The windows for accessibility.
+         */
+        @Override
+        public void onWindowsForAccessibilityChanged(boolean forceSend,
+                @NonNull List<WindowInfo> windows) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Display Id = " + mDisplayId);
+                    Slog.i(LOG_TAG, "Windows changed: " + windows);
+                }
+                if (shouldUpdateWindowsLocked(forceSend, windows)) {
+                    cacheWindows(windows);
+                    // Lets the policy update the focused and active windows.
+                    updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(),
+                            windows);
+                    // Someone may be waiting for the windows - advertise it.
+                    mLock.notifyAll();
+                }
+            }
+        }
+
+        private boolean shouldUpdateWindowsLocked(boolean forceSend,
+                @NonNull List<WindowInfo> windows) {
+            if (forceSend) {
+                return true;
+            }
+
+            final int windowCount = windows.size();
+            // We computed the windows and if they changed notify the client.
+            if (mCachedWindowInfos.size() != windowCount) {
+                // Different size means something changed.
+                return true;
+            } else if (!mCachedWindowInfos.isEmpty() || !windows.isEmpty()) {
+                // Since we always traverse windows from high to low layer
+                // the old and new windows at the same index should be the
+                // same, otherwise something changed.
+                for (int i = 0; i < windowCount; i++) {
+                    WindowInfo oldWindow = mCachedWindowInfos.get(i);
+                    WindowInfo newWindow = windows.get(i);
+                    // We do not care for layer changes given the window
+                    // order does not change. This brings no new information
+                    // to the clients.
+                    if (windowChangedNoLayer(oldWindow, newWindow)) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        private void cacheWindows(List<WindowInfo> windows) {
+            final int oldWindowCount = mCachedWindowInfos.size();
+            for (int i = oldWindowCount - 1; i >= 0; i--) {
+                mCachedWindowInfos.remove(i).recycle();
+            }
+            final int newWindowCount = windows.size();
+            for (int i = 0; i < newWindowCount; i++) {
+                WindowInfo newWindow = windows.get(i);
+                mCachedWindowInfos.add(WindowInfo.obtain(newWindow));
+            }
+        }
+
+        private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
+            if (oldWindow == newWindow) {
+                return false;
+            }
+            if (oldWindow == null) {
+                return true;
+            }
+            if (newWindow == null) {
+                return true;
+            }
+            if (oldWindow.type != newWindow.type) {
+                return true;
+            }
+            if (oldWindow.focused != newWindow.focused) {
+                return true;
+            }
+            if (oldWindow.token == null) {
+                if (newWindow.token != null) {
+                    return true;
+                }
+            } else if (!oldWindow.token.equals(newWindow.token)) {
+                return true;
+            }
+            if (oldWindow.parentToken == null) {
+                if (newWindow.parentToken != null) {
+                    return true;
+                }
+            } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
+                return true;
+            }
+            if (oldWindow.activityToken == null) {
+                if (newWindow.activityToken != null) {
+                    return true;
+                }
+            } else if (!oldWindow.activityToken.equals(newWindow.activityToken)) {
+                return true;
+            }
+            if (!oldWindow.regionInScreen.equals(newWindow.regionInScreen)) {
+                return true;
+            }
+            if (oldWindow.childTokens != null && newWindow.childTokens != null
+                    && !oldWindow.childTokens.equals(newWindow.childTokens)) {
+                return true;
+            }
+            if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
+                return true;
+            }
+            if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
+                return true;
+            }
+            if (oldWindow.inPictureInPicture != newWindow.inPictureInPicture) {
+                return true;
+            }
+            if (oldWindow.hasFlagWatchOutsideTouch != newWindow.hasFlagWatchOutsideTouch) {
+                return true;
+            }
+            if (oldWindow.displayId != newWindow.displayId) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Clears all {@link AccessibilityWindowInfo}s and {@link WindowInfo}s.
+         */
+        private void clearWindowsLocked() {
+            final List<WindowInfo> windows = Collections.emptyList();
+            final int activeWindowId = mActiveWindowId;
+            // UserId is useless in updateWindowsLocked, when we update a empty window list.
+            // Just pass current userId here.
+            updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows);
+            // Do not reset mActiveWindowId here. mActiveWindowId will be clear after accessibility
+            // interaction connection removed.
+            mActiveWindowId = activeWindowId;
+            mWindows = null;
+        }
+
+        /**
+         * Updates windows info according to specified userId and windows.
+         *
+         * @param userId The userId to update
+         * @param windows The windows to update
+         */
+        private void updateWindowsLocked(int userId, @NonNull List<WindowInfo> windows) {
+            if (mWindows == null) {
+                mWindows = new ArrayList<>();
+            }
+
+            final List<AccessibilityWindowInfo> oldWindowList = new ArrayList<>(mWindows);
+            final SparseArray<AccessibilityWindowInfo> oldWindowsById = mA11yWindowInfoById.clone();
+
+            mWindows.clear();
+            mA11yWindowInfoById.clear();
+
+            for (int i = 0; i < mWindowInfoById.size(); i++) {
+                mWindowInfoById.valueAt(i).recycle();
+            }
+            mWindowInfoById.clear();
+            mHasWatchOutsideTouchWindow = false;
+            mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+            if (!mTouchInteractionInProgress) {
+                mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+            }
+
+            // If the active window goes away while the user is touch exploring we
+            // reset the active window id and wait for the next hover event from
+            // under the user's finger to determine which one is the new one. It
+            // is possible that the finger is not moving and the input system
+            // filters out such events.
+            boolean activeWindowGone = true;
+
+            final int windowCount = windows.size();
+
+            // We'll clear accessibility focus if the window with focus is no longer visible to
+            // accessibility services
+            boolean shouldClearAccessibilityFocus =
+                    mAccessibilityFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+            if (windowCount > 0) {
+                for (int i = 0; i < windowCount; i++) {
+                    final WindowInfo windowInfo = windows.get(i);
+                    final AccessibilityWindowInfo window;
+                    if (isTrackingWindowsLocked()) {
+                        window = populateReportedWindowLocked(userId, windowInfo);
+                    } else {
+                        window = null;
+                    }
+                    if (window != null) {
+
+                        // Flip layers in list to be consistent with AccessibilityService#getWindows
+                        window.setLayer(windowCount - 1 - window.getLayer());
+
+                        final int windowId = window.getId();
+                        if (window.isFocused()) {
+                            mFocusedWindowId = windowId;
+                            if (!mTouchInteractionInProgress) {
+                                mActiveWindowId = windowId;
+                                window.setActive(true);
+                            } else if (windowId == mActiveWindowId) {
+                                activeWindowGone = false;
+                            }
+                        }
+                        if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) {
+                            mHasWatchOutsideTouchWindow = true;
+                        }
+                        mWindows.add(window);
+                        mA11yWindowInfoById.put(windowId, window);
+                        mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo));
+                    }
+                }
+
+                if (mTouchInteractionInProgress && activeWindowGone) {
+                    mActiveWindowId = mFocusedWindowId;
+                }
+
+                // Focused window may change the active one, so set the
+                // active window once we decided which it is.
+                final int accessibilityWindowCount = mWindows.size();
+                for (int i = 0; i < accessibilityWindowCount; i++) {
+                    final AccessibilityWindowInfo window = mWindows.get(i);
+                    if (window.getId() == mActiveWindowId) {
+                        window.setActive(true);
+                    }
+                    if (window.getId() == mAccessibilityFocusedWindowId) {
+                        window.setAccessibilityFocused(true);
+                        shouldClearAccessibilityFocus = false;
+                    }
+                }
+            }
+
+            sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById);
+
+            final int oldWindowCount = oldWindowList.size();
+            for (int i = oldWindowCount - 1; i >= 0; i--) {
+                oldWindowList.remove(i).recycle();
+            }
+
+            if (shouldClearAccessibilityFocus) {
+                clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId);
+            }
+        }
+
+        private void sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows,
+                SparseArray<AccessibilityWindowInfo> oldWindowsById) {
+            List<AccessibilityEvent> events = new ArrayList<>();
+            // Sends events for all removed windows.
+            final int oldWindowsCount = oldWindows.size();
+            for (int i = 0; i < oldWindowsCount; i++) {
+                final AccessibilityWindowInfo window = oldWindows.get(i);
+                if (mA11yWindowInfoById.get(window.getId()) == null) {
+                    events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+                            window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED));
+                }
+            }
+
+            // Looks for other changes.
+            final int newWindowCount = mWindows.size();
+            for (int i = 0; i < newWindowCount; i++) {
+                final AccessibilityWindowInfo newWindow = mWindows.get(i);
+                final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId());
+                if (oldWindow == null) {
+                    events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+                            newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED));
+                } else {
+                    int changes = newWindow.differenceFrom(oldWindow);
+                    if (changes !=  0) {
+                        events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+                                newWindow.getId(), changes));
+                    }
+                }
+            }
+
+            final int numEvents = events.size();
+            for (int i = 0; i < numEvents; i++) {
+                mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(events.get(i));
+            }
+        }
+
+        private AccessibilityWindowInfo populateReportedWindowLocked(int userId,
+                WindowInfo window) {
+            final int windowId = findWindowIdLocked(userId, window.token);
+            if (windowId < 0) {
+                return null;
+            }
+
+            final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
+
+            reportedWindow.setId(windowId);
+            reportedWindow.setType(getTypeForWindowManagerWindowType(window.type));
+            reportedWindow.setLayer(window.layer);
+            reportedWindow.setFocused(window.focused);
+            reportedWindow.setRegionInScreen(window.regionInScreen);
+            reportedWindow.setTitle(window.title);
+            reportedWindow.setAnchorId(window.accessibilityIdOfAnchor);
+            reportedWindow.setPictureInPicture(window.inPictureInPicture);
+            reportedWindow.setDisplayId(window.displayId);
+
+            final int parentId = findWindowIdLocked(userId, window.parentToken);
+            if (parentId >= 0) {
+                reportedWindow.setParentId(parentId);
+            }
+
+            if (window.childTokens != null) {
+                final int childCount = window.childTokens.size();
+                for (int i = 0; i < childCount; i++) {
+                    final IBinder childToken = window.childTokens.get(i);
+                    final int childId = findWindowIdLocked(userId, childToken);
+                    if (childId >= 0) {
+                        reportedWindow.addChild(childId);
+                    }
+                }
+            }
+
+            return reportedWindow;
+        }
+
+        private int getTypeForWindowManagerWindowType(int windowType) {
+            switch (windowType) {
+                case WindowManager.LayoutParams.TYPE_APPLICATION:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
+                case WindowManager.LayoutParams.TYPE_BASE_APPLICATION:
+                case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
+                case WindowManager.LayoutParams.TYPE_PHONE:
+                case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+                case WindowManager.LayoutParams.TYPE_TOAST:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: {
+                    return AccessibilityWindowInfo.TYPE_APPLICATION;
+                }
+
+                case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
+                case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: {
+                    return AccessibilityWindowInfo.TYPE_INPUT_METHOD;
+                }
+
+                case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+                case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
+                case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+                case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+                case WindowManager.LayoutParams.TYPE_STATUS_BAR:
+                case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL:
+                case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL:
+                case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+                case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+                case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+                case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+                case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
+                case WindowManager.LayoutParams.TYPE_SCREENSHOT: {
+                    return AccessibilityWindowInfo.TYPE_SYSTEM;
+                }
+
+                case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: {
+                    return AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER;
+                }
+
+                case TYPE_ACCESSIBILITY_OVERLAY: {
+                    return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY;
+                }
+
+                default: {
+                    return -1;
+                }
+            }
+        }
+
+        /**
+         * Dumps all {@link AccessibilityWindowInfo}s here.
+         */
+        void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) {
+            if (mWindows != null) {
+                final int windowCount = mWindows.size();
+                for (int j = 0; j < windowCount; j++) {
+                    if (j == 0) {
+                        pw.append("Display[");
+                        pw.append(Integer.toString(mDisplayId));
+                        pw.append("] : ");
+                        pw.println();
+                    }
+                    if (j > 0) {
+                        pw.append(',');
+                        pw.println();
+                    }
+                    pw.append("Window[");
+                    AccessibilityWindowInfo window = mWindows.get(j);
+                    pw.append(window.toString());
+                    pw.append(']');
+                }
+                pw.println();
+            }
+        }
+    }
     /**
      * Interface to send {@link AccessibilityEvent}.
      */
     public interface AccessibilityEventSender {
         /**
-         * Send {@link AccessibilityEvent} for current user.
+         * Sends {@link AccessibilityEvent} for current user.
          */
         void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event);
     }
@@ -171,166 +788,24 @@
         mAccessibilityEventSender = accessibilityEventSender;
         mSecurityPolicy = securityPolicy;
         mAccessibilityUserManager = accessibilityUserManager;
-
+        mDisplayWindowsObserver = new DisplayWindowsObserver(Display.DEFAULT_DISPLAY);
     }
 
     /**
-     * Callbacks from window manager when there's an accessibility change in windows.
-     *
-     * @param forceSend Send the windows for accessibility even if they haven't changed.
-     * @param windows The windows of current display for accessibility.
-     */
-    @Override
-    public void onWindowsForAccessibilityChanged(boolean forceSend,
-            @NonNull List<WindowInfo> windows) {
-        synchronized (mLock) {
-            if (DEBUG) {
-                Slog.i(LOG_TAG, "Windows changed: " + windows);
-            }
-
-            if (shouldUpdateWindowsLocked(forceSend, windows)) {
-                cacheWindows(windows);
-                // Let the policy update the focused and active windows.
-                updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows);
-                // Someone may be waiting for the windows - advertise it.
-                mLock.notifyAll();
-            }
-        }
-    }
-
-    private boolean shouldUpdateWindowsLocked(boolean forceSend,
-            @NonNull List<WindowInfo> windows) {
-        if (forceSend) {
-            return true;
-        }
-
-        final int windowCount = windows.size();
-        // We computed the windows and if they changed notify the client.
-        if (mCachedWindowInfos.size() != windowCount) {
-            // Different size means something changed.
-            return true;
-        } else if (!mCachedWindowInfos.isEmpty() || !windows.isEmpty()) {
-            // Since we always traverse windows from high to low layer
-            // the old and new windows at the same index should be the
-            // same, otherwise something changed.
-            for (int i = 0; i < windowCount; i++) {
-                WindowInfo oldWindow = mCachedWindowInfos.get(i);
-                WindowInfo newWindow = windows.get(i);
-                // We do not care for layer changes given the window
-                // order does not change. This brings no new information
-                // to the clients.
-                if (windowChangedNoLayer(oldWindow, newWindow)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    private void cacheWindows(List<WindowInfo> windows) {
-        final int oldWindowCount = mCachedWindowInfos.size();
-        for (int i = oldWindowCount - 1; i >= 0; i--) {
-            mCachedWindowInfos.remove(i).recycle();
-        }
-        final int newWindowCount = windows.size();
-        for (int i = 0; i < newWindowCount; i++) {
-            WindowInfo newWindow = windows.get(i);
-            mCachedWindowInfos.add(WindowInfo.obtain(newWindow));
-        }
-    }
-
-    private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
-        if (oldWindow == newWindow) {
-            return false;
-        }
-        if (oldWindow == null) {
-            return true;
-        }
-        if (newWindow == null) {
-            return true;
-        }
-        if (oldWindow.type != newWindow.type) {
-            return true;
-        }
-        if (oldWindow.focused != newWindow.focused) {
-            return true;
-        }
-        if (oldWindow.token == null) {
-            if (newWindow.token != null) {
-                return true;
-            }
-        } else if (!oldWindow.token.equals(newWindow.token)) {
-            return true;
-        }
-        if (oldWindow.parentToken == null) {
-            if (newWindow.parentToken != null) {
-                return true;
-            }
-        } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
-            return true;
-        }
-        if (oldWindow.activityToken == null) {
-            if (newWindow.activityToken != null) {
-                return true;
-            }
-        } else if (!oldWindow.activityToken.equals(newWindow.activityToken)) {
-            return true;
-        }
-        if (!oldWindow.regionInScreen.equals(newWindow.regionInScreen)) {
-            return true;
-        }
-        if (oldWindow.childTokens != null && newWindow.childTokens != null
-                && !oldWindow.childTokens.equals(newWindow.childTokens)) {
-            return true;
-        }
-        if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
-            return true;
-        }
-        if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
-            return true;
-        }
-        if (oldWindow.inPictureInPicture != newWindow.inPictureInPicture) {
-            return true;
-        }
-        if (oldWindow.hasFlagWatchOutsideTouch != newWindow.hasFlagWatchOutsideTouch) {
-            return true;
-        }
-        if (oldWindow.displayId != newWindow.displayId) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Start tracking windows changes from window manager.
+     * Starts tracking windows changes from window manager.
      */
     public void startTrackingWindows() {
         synchronized (mLock) {
-            if (!mTrackingWindows) {
-                // Turn on the flag before setup the callback.
-                // In some cases, onWindowsForAccessibilityChanged will be called immediately in
-                // setWindowsForAccessibilityCallback. We'll lost windows if flag is false.
-                mTrackingWindows = true;
-                // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY
-                mWindowManagerInternal.setWindowsForAccessibilityCallback(Display.DEFAULT_DISPLAY,
-                        this);
-            }
+            mDisplayWindowsObserver.startTrackingWindowsLocked();
         }
     }
 
     /**
-     * stop tracking windows changes from window manager, and clear all windows info.
+     * Stops tracking windows changes from window manager, and clear all windows info.
      */
     public void stopTrackingWindows() {
         synchronized (mLock) {
-            if (mTrackingWindows) {
-                // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY
-                mWindowManagerInternal.setWindowsForAccessibilityCallback(Display.DEFAULT_DISPLAY,
-                        null);
-                mTrackingWindows = false;
-                clearWindowsLocked();
-            }
+            mDisplayWindowsObserver.stopTrackingWindowsLocked();
         }
     }
 
@@ -340,127 +815,7 @@
      * @return true if windows changes tracking
      */
     public boolean isTrackingWindowsLocked() {
-        return mTrackingWindows;
-    }
-
-    /**
-     * Clears all {@link AccessibilityWindowInfo}s and {@link WindowInfo}s.
-     */
-    private void clearWindowsLocked() {
-        final List<WindowInfo> windows = Collections.emptyList();
-        final int activeWindowId = mActiveWindowId;
-        // userId is useless in updateWindowsLocked, when we update a empty window list. Just pass
-        // current userId here.
-        updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows);
-        // Do not reset mActiveWindowId here. mActiveWindowId will be clear after accessibility
-        // interaction connection removed.
-        mActiveWindowId = activeWindowId;
-        mWindows = null;
-    }
-
-    /**
-     * Update windows info according to specified userId and windows.
-     *
-     * @param userId The userId to update
-     * @param windows The windows to update
-     */
-    private void updateWindowsLocked(int userId, @NonNull List<WindowInfo> windows) {
-        if (mWindows == null) {
-            mWindows = new ArrayList<>();
-        }
-
-        final List<AccessibilityWindowInfo> oldWindowList = new ArrayList<>(mWindows);
-        final SparseArray<AccessibilityWindowInfo> oldWindowsById = mA11yWindowInfoById.clone();
-
-        mWindows.clear();
-        mA11yWindowInfoById.clear();
-
-        for (int i = 0; i < mWindowInfoById.size(); i++) {
-            mWindowInfoById.valueAt(i).recycle();
-        }
-        mWindowInfoById.clear();
-        mHasWatchOutsideTouchWindow = false;
-
-        mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
-        if (!mTouchInteractionInProgress) {
-            mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
-        }
-
-        // If the active window goes away while the user is touch exploring we
-        // reset the active window id and wait for the next hover event from
-        // under the user's finger to determine which one is the new one. It
-        // is possible that the finger is not moving and the input system
-        // filters out such events.
-        boolean activeWindowGone = true;
-
-        final int windowCount = windows.size();
-
-        // We'll clear accessibility focus if the window with focus is no longer visible to
-        // accessibility services
-        boolean shouldClearAccessibilityFocus =
-                mAccessibilityFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
-        if (windowCount > 0) {
-            for (int i = 0; i < windowCount; i++) {
-                final WindowInfo windowInfo = windows.get(i);
-                final AccessibilityWindowInfo window;
-                if (isTrackingWindowsLocked()) {
-                    window = populateReportedWindowLocked(userId, windowInfo);
-                } else {
-                    window = null;
-                }
-                if (window != null) {
-
-                    // Flip layers in list to be consistent with AccessibilityService#getWindows
-                    window.setLayer(windowCount - 1 - window.getLayer());
-
-                    final int windowId = window.getId();
-                    if (window.isFocused()) {
-                        mFocusedWindowId = windowId;
-                        if (!mTouchInteractionInProgress) {
-                            mActiveWindowId = windowId;
-                            window.setActive(true);
-                        } else if (windowId == mActiveWindowId) {
-                            activeWindowGone = false;
-                        }
-                    }
-                    if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) {
-                        mHasWatchOutsideTouchWindow = true;
-                    }
-                    mWindows.add(window);
-                    mA11yWindowInfoById.put(windowId, window);
-                    mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo));
-                }
-            }
-
-            if (mTouchInteractionInProgress && activeWindowGone) {
-                mActiveWindowId = mFocusedWindowId;
-            }
-
-            // Focused window may change the active one, so set the
-            // active window once we decided which it is.
-            final int accessibilityWindowCount = mWindows.size();
-            for (int i = 0; i < accessibilityWindowCount; i++) {
-                final AccessibilityWindowInfo window = mWindows.get(i);
-                if (window.getId() == mActiveWindowId) {
-                    window.setActive(true);
-                }
-                if (window.getId() == mAccessibilityFocusedWindowId) {
-                    window.setAccessibilityFocused(true);
-                    shouldClearAccessibilityFocus = false;
-                }
-            }
-        }
-
-        sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById);
-
-        final int oldWindowCount = oldWindowList.size();
-        for (int i = oldWindowCount - 1; i >= 0; i--) {
-            oldWindowList.remove(i).recycle();
-        }
-
-        if (shouldClearAccessibilityFocus) {
-            clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId);
-        }
+        return mDisplayWindowsObserver.isTrackingWindowsLocked();
     }
 
     /**
@@ -468,7 +823,7 @@
      */
     @Nullable
     public List<AccessibilityWindowInfo> getWindowListLocked() {
-        return mWindows;
+        return mDisplayWindowsObserver.getWindowListLocked();
     }
 
     /**
@@ -494,7 +849,7 @@
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
             final int resolvedUid = UserHandle.getUid(resolvedUserId, UserHandle.getCallingAppId());
 
-            // Make sure the reported package is one the caller has access to.
+            // Makes sure the reported package is one the caller has access to.
             packageName = mSecurityPolicy.resolveValidReportedPackageLocked(
                     packageName, UserHandle.getCallingAppId(), resolvedUserId);
 
@@ -576,7 +931,7 @@
     }
 
     /**
-     * Resolve a connection wrapper for a window id
+     * Resolves a connection wrapper for a window id.
      *
      * @param userId The user id for any user-specific windows
      * @param windowId The id of the window of interest
@@ -672,7 +1027,7 @@
     }
 
     /**
-     * Return the userId that owns the given window token, {@link UserHandle#USER_NULL}
+     * Returns the userId that owns the given window token, {@link UserHandle#USER_NULL}
      * if not found.
      *
      * @param windowToken The winodw token
@@ -703,42 +1058,6 @@
         return -1;
     }
 
-    private void sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows,
-            SparseArray<AccessibilityWindowInfo> oldWindowsById) {
-        List<AccessibilityEvent> events = new ArrayList<>();
-        // Send events for all removed windows
-        final int oldWindowsCount = oldWindows.size();
-        for (int i = 0; i < oldWindowsCount; i++) {
-            final AccessibilityWindowInfo window = oldWindows.get(i);
-            if (mA11yWindowInfoById.get(window.getId()) == null) {
-                events.add(AccessibilityEvent.obtainWindowsChangedEvent(
-                        window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED));
-            }
-        }
-
-        // Look for other changes
-        final int newWindowCount = mWindows.size();
-        for (int i = 0; i < newWindowCount; i++) {
-            final AccessibilityWindowInfo newWindow = mWindows.get(i);
-            final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId());
-            if (oldWindow == null) {
-                events.add(AccessibilityEvent.obtainWindowsChangedEvent(
-                        newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED));
-            } else {
-                int changes = newWindow.differenceFrom(oldWindow);
-                if (changes !=  0) {
-                    events.add(AccessibilityEvent.obtainWindowsChangedEvent(
-                            newWindow.getId(), changes));
-                }
-            }
-        }
-
-        final int numEvents = events.size();
-        for (int i = 0; i < numEvents; i++) {
-            mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(events.get(i));
-        }
-    }
-
     /**
      * Computes partial interactive region of given windowId.
      *
@@ -748,38 +1067,8 @@
      */
     public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
             @NonNull Region outRegion) {
-        if (mWindows == null) {
-            return false;
-        }
-
-        // Windows are ordered in z order so start from the bottom and find
-        // the window of interest. After that all windows that cover it should
-        // be subtracted from the resulting region. Note that for accessibility
-        // we are returning only interactive windows.
-        Region windowInteractiveRegion = null;
-        boolean windowInteractiveRegionChanged = false;
-
-        final int windowCount = mWindows.size();
-        final Region currentWindowRegions = new Region();
-        for (int i = windowCount - 1; i >= 0; i--) {
-            AccessibilityWindowInfo currentWindow = mWindows.get(i);
-            if (windowInteractiveRegion == null) {
-                if (currentWindow.getId() == windowId) {
-                    currentWindow.getRegionInScreen(currentWindowRegions);
-                    outRegion.set(currentWindowRegions);
-                    windowInteractiveRegion = outRegion;
-                    continue;
-                }
-            } else if (currentWindow.getType()
-                    != AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY) {
-                currentWindow.getRegionInScreen(currentWindowRegions);
-                if (windowInteractiveRegion.op(currentWindowRegions, Region.Op.DIFFERENCE)) {
-                    windowInteractiveRegionChanged = true;
-                }
-            }
-        }
-
-        return windowInteractiveRegionChanged;
+        return mDisplayWindowsObserver.computePartialInteractiveRegionForWindowLocked(windowId,
+            outRegion);
     }
 
     /**
@@ -846,7 +1135,7 @@
                         mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
                     }
                     // Clear the window with focus if it no longer has focus and we aren't
-                    // just moving focus from one view to the other in the same window
+                    // just moving focus from one view to the other in the same window.
                     if ((mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
                             && (mAccessibilityFocusedWindowId == windowId)
                             && (eventAction != AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)) {
@@ -904,7 +1193,7 @@
     }
 
     /**
-     * Get the id of the current active window.
+     * Gets the id of the current active window.
      *
      * @return The userId
      */
@@ -923,20 +1212,7 @@
                             mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
 
             mActiveWindowId = windowId;
-            if (mWindows != null) {
-                final int windowCount = mWindows.size();
-                for (int i = 0; i < windowCount; i++) {
-                    AccessibilityWindowInfo window = mWindows.get(i);
-                    if (window.getId() == windowId) {
-                        window.setActive(true);
-                        mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
-                                AccessibilityEvent.obtainWindowsChangedEvent(windowId,
-                                        AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
-                    } else {
-                        window.setActive(false);
-                    }
-                }
-            }
+            mDisplayWindowsObserver.setActiveWindowLocked(windowId);
         }
     }
 
@@ -948,21 +1224,7 @@
                             WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
 
             mAccessibilityFocusedWindowId = windowId;
-            if (mWindows != null) {
-                final int windowCount = mWindows.size();
-                for (int i = 0; i < windowCount; i++) {
-                    AccessibilityWindowInfo window = mWindows.get(i);
-                    if (window.getId() == windowId) {
-                        window.setAccessibilityFocused(true);
-                        mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
-                                AccessibilityEvent.obtainWindowsChangedEvent(
-                                        windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
-
-                    } else {
-                        window.setAccessibilityFocused(false);
-                    }
-                }
-            }
+            mDisplayWindowsObserver.setAccessibilityFocusedWindowLocked(windowId);
         }
     }
 
@@ -973,8 +1235,8 @@
      * @return The accessibility window info
      */
     @Nullable
-    public AccessibilityWindowInfo findA11yWindowInfoById(int windowId) {
-        return mA11yWindowInfoById.get(windowId);
+    public AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) {
+        return mDisplayWindowsObserver.findA11yWindowInfoByIdLocked(windowId);
     }
 
     /**
@@ -984,8 +1246,8 @@
      * @return The window info
      */
     @Nullable
-    public WindowInfo findWindowInfoById(int windowId) {
-        return mWindowInfoById.get(windowId);
+    public WindowInfo findWindowInfoByIdLocked(int windowId) {
+        return mDisplayWindowsObserver.findWindowInfoByIdLocked(windowId);
     }
 
     /**
@@ -1010,21 +1272,12 @@
      * @return PIP accessibility window info
      */
     @Nullable
-    public AccessibilityWindowInfo getPictureInPictureWindow() {
-        if (mWindows != null) {
-            final int windowCount = mWindows.size();
-            for (int i = 0; i < windowCount; i++) {
-                final AccessibilityWindowInfo window = mWindows.get(i);
-                if (window.isInPictureInPictureMode()) {
-                    return window;
-                }
-            }
-        }
-        return null;
+    public AccessibilityWindowInfo getPictureInPictureWindowLocked() {
+        return mDisplayWindowsObserver.getPictureInPictureWindowLocked();
     }
 
     /**
-     * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture
+     * Sets an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture
      * window.
      */
     public void setPictureInPictureActionReplacingConnection(
@@ -1060,7 +1313,8 @@
         final List<Integer> outsideWindowsIds;
         final List<RemoteAccessibilityConnection> connectionList = new ArrayList<>();
         synchronized (mLock) {
-            outsideWindowsIds = getWatchOutsideTouchWindowIdLocked(targetWindowId);
+            outsideWindowsIds =
+                mDisplayWindowsObserver.getWatchOutsideTouchWindowIdLocked(targetWindowId);
             for (int i = 0; i < outsideWindowsIds.size(); i++) {
                 connectionList.add(getConnectionLocked(userId, outsideWindowsIds.get(i)));
             }
@@ -1079,22 +1333,6 @@
         }
     }
 
-    private List<Integer> getWatchOutsideTouchWindowIdLocked(int targetWindowId) {
-        final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId);
-        if (targetWindow != null && mHasWatchOutsideTouchWindow) {
-            final List<Integer> outsideWindowsId = new ArrayList<>();
-            for (int i = 0; i < mWindowInfoById.size(); i++) {
-                final WindowInfo window = mWindowInfoById.valueAt(i);
-                if (window != null && window.layer < targetWindow.layer
-                        && window.hasFlagWatchOutsideTouch) {
-                    outsideWindowsId.add(mWindowInfoById.keyAt(i));
-                }
-            }
-            return outsideWindowsId;
-        }
-        return Collections.emptyList();
-    }
-
     /**
      * Gets current input focused window token from window manager, and returns its windowId.
      *
@@ -1108,96 +1346,6 @@
         }
     }
 
-    private AccessibilityWindowInfo populateReportedWindowLocked(int userId, WindowInfo window) {
-        final int windowId = findWindowIdLocked(userId, window.token);
-        if (windowId < 0) {
-            return null;
-        }
-
-        final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
-
-        reportedWindow.setId(windowId);
-        reportedWindow.setType(getTypeForWindowManagerWindowType(window.type));
-        reportedWindow.setLayer(window.layer);
-        reportedWindow.setFocused(window.focused);
-        reportedWindow.setRegionInScreen(window.regionInScreen);
-        reportedWindow.setTitle(window.title);
-        reportedWindow.setAnchorId(window.accessibilityIdOfAnchor);
-        reportedWindow.setPictureInPicture(window.inPictureInPicture);
-        reportedWindow.setDisplayId(window.displayId);
-
-        final int parentId = findWindowIdLocked(userId, window.parentToken);
-        if (parentId >= 0) {
-            reportedWindow.setParentId(parentId);
-        }
-
-        if (window.childTokens != null) {
-            final int childCount = window.childTokens.size();
-            for (int i = 0; i < childCount; i++) {
-                final IBinder childToken = window.childTokens.get(i);
-                final int childId = findWindowIdLocked(userId, childToken);
-                if (childId >= 0) {
-                    reportedWindow.addChild(childId);
-                }
-            }
-        }
-
-        return reportedWindow;
-    }
-
-    private int getTypeForWindowManagerWindowType(int windowType) {
-        switch (windowType) {
-            case WindowManager.LayoutParams.TYPE_APPLICATION:
-            case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
-            case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
-            case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING:
-            case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
-            case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
-            case WindowManager.LayoutParams.TYPE_BASE_APPLICATION:
-            case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
-            case WindowManager.LayoutParams.TYPE_PHONE:
-            case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
-            case WindowManager.LayoutParams.TYPE_TOAST:
-            case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: {
-                return AccessibilityWindowInfo.TYPE_APPLICATION;
-            }
-
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: {
-                return AccessibilityWindowInfo.TYPE_INPUT_METHOD;
-            }
-
-            case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
-            case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
-            case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
-            case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
-            case WindowManager.LayoutParams.TYPE_STATUS_BAR:
-            case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL:
-            case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL:
-            case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
-            case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
-            case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
-            case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
-            case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
-            case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
-            case WindowManager.LayoutParams.TYPE_SCREENSHOT: {
-                return AccessibilityWindowInfo.TYPE_SYSTEM;
-            }
-
-            case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: {
-                return AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER;
-            }
-
-            case TYPE_ACCESSIBILITY_OVERLAY: {
-                return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY;
-            }
-
-            default: {
-                return -1;
-            }
-        }
-    }
-
     private boolean isValidUserForInteractionConnectionsLocked(int userId) {
         return mInteractionConnections.indexOfKey(userId) >= 0;
     }
@@ -1251,22 +1399,9 @@
     }
 
     /**
-     * Dump all {@link AccessibilityWindowInfo}s here.
+     * Dumps all {@link AccessibilityWindowInfo}s here.
      */
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
-        if (mWindows != null) {
-            final int windowCount = mWindows.size();
-            for (int j = 0; j < windowCount; j++) {
-                if (j > 0) {
-                    pw.append(',');
-                    pw.println();
-                }
-                pw.append("Window[");
-                AccessibilityWindowInfo window = mWindows.get(j);
-                pw.append(window.toString());
-                pw.append(']');
-            }
-            pw.println();
-        }
+        mDisplayWindowsObserver.dumpLocked(fd, pw, args);
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0dd7199..a0900b6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -528,12 +528,12 @@
                 options.setLockTaskEnabled(true);
             }
             if (mWaitOption) {
-                result = mInternal.startActivityAndWait(null, null, intent, mimeType,
+                result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, intent, mimeType,
                         null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
                 res = result.result;
             } else {
-                res = mInternal.startActivityAsUser(null, null, intent, mimeType,
+                res = mInternal.startActivityAsUser(null, SHELL_PACKAGE_NAME, intent, mimeType,
                         null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
             }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 4957eed..73d160d 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -772,7 +772,6 @@
                     case WifiManager.WIFI_AP_STATE_FAILED:
                     default:
                         disableWifiIpServingLocked(ifname, curState);
-                        mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
                         break;
                 }
             }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 09e9375..f20003a2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2551,7 +2551,7 @@
             final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
             if (mStatusBar != null) {
                 mStatusBar.setImeWindowStatus(mCurTokenDisplayId, mCurToken, vis, backDisposition,
-                        needsToShowImeSwitcher);
+                        needsToShowImeSwitcher, false /*isMultiClientImeEnabled*/);
             }
             final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
             if (imi != null && needsToShowImeSwitcher) {
diff --git a/core/java/android/view/inputmethod/InputMethodSystemProperty.java b/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
similarity index 81%
rename from core/java/android/view/inputmethod/InputMethodSystemProperty.java
rename to services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
index e20c2fd..a6a6893 100644
--- a/core/java/android/view/inputmethod/InputMethodSystemProperty.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.view.inputmethod;
+package com.android.server.inputmethod;
 
 import android.annotation.Nullable;
 import android.content.ComponentName;
@@ -23,8 +23,6 @@
 
 /**
  * Various (pseudo) constants about IME behaviors.
- *
- * @hide
  */
 public class InputMethodSystemProperty {
     /**
@@ -58,23 +56,12 @@
 
     /**
      * {@link ComponentName} of multi-client IME to be used.
-     *
-     * <p>TODO: Move this back to MultiClientInputMethodManagerService once
-     * {@link #PER_PROFILE_IME_ENABLED} always becomes {@code true}.</p>
-     *
-     * @hide
      */
     @Nullable
-    public static final ComponentName sMultiClientImeComponentName =
-            getMultiClientImeComponentName();
+    static final ComponentName sMultiClientImeComponentName = getMultiClientImeComponentName();
 
     /**
      * {@code true} when multi-client IME is enabled.
-     *
-     * <p>TODO: Move this back to MultiClientInputMethodManagerService once
-     * {@link #PER_PROFILE_IME_ENABLED} always becomes {@code true}.</p>
-     *
-     * @hide
      */
     public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null);
 }
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 3dd7304..02e29e0 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -67,7 +67,6 @@
 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
-import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 984f22f..c712431 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -281,20 +281,7 @@
         mAbortIdleOptimization.set(false);
 
         long lowStorageThreshold = getLowStorageThreshold(context);
-        // Optimize primary apks.
-        int result = optimizePackages(pm, pkgs, lowStorageThreshold,
-            /*isForPrimaryDex=*/ true);
-        if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
-            return result;
-        }
-        if (supportSecondaryDex()) {
-            result = reconcileSecondaryDexFiles(pm.getDexManager());
-            if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
-                return result;
-            }
-            result = optimizePackages(pm, pkgs, lowStorageThreshold,
-                /*isForPrimaryDex=*/ false);
-        }
+        int result = idleOptimizePackages(pm, pkgs, lowStorageThreshold);
         return result;
     }
 
@@ -342,11 +329,20 @@
         return 0;
     }
 
-    private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
-            long lowStorageThreshold, boolean isForPrimaryDex) {
+    private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+            long lowStorageThreshold) {
         ArraySet<String> updatedPackages = new ArraySet<>();
 
         try {
+            final boolean supportSecondaryDex = supportSecondaryDex();
+
+            if (supportSecondaryDex) {
+                int result = reconcileSecondaryDexFiles(pm.getDexManager());
+                if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
+                    return result;
+                }
+            }
+
             // Only downgrade apps when space is low on device.
             // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
             // up disk before user hits the actual lowStorageThreshold.
@@ -359,43 +355,61 @@
                         pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
                 Log.d(TAG, "Unsused Packages " +  String.join(",", unusedPackages));
 
-                for (String pkg : unusedPackages) {
-                    int abortCode = abortIdleOptimizations(/*lowStorageThreshold*/ -1);
-                    if (abortCode != OPTIMIZE_CONTINUE) {
-                        // Should be aborted by the scheduler.
-                        return abortCode;
-                    }
-                    if (downgradePackage(pm, pkg, isForPrimaryDex)) {
-                        updatedPackages.add(pkg);
-                    }
-                }
-
                 if (!unusedPackages.isEmpty()) {
+                    for (String pkg : unusedPackages) {
+                        int abortCode = abortIdleOptimizations(/*lowStorageThreshold*/ -1);
+                        if (abortCode != OPTIMIZE_CONTINUE) {
+                            // Should be aborted by the scheduler.
+                            return abortCode;
+                        }
+                        if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) {
+                            updatedPackages.add(pkg);
+                        }
+                        if (supportSecondaryDex) {
+                            downgradePackage(pm, pkg, /*isForPrimaryDex*/ false);
+                        }
+                    }
+
                     pkgs = new ArraySet<>(pkgs);
                     pkgs.removeAll(unusedPackages);
                 }
             }
 
-            for (String pkg : pkgs) {
-                int abortCode = abortIdleOptimizations(lowStorageThreshold);
-                if (abortCode != OPTIMIZE_CONTINUE) {
-                    // Either aborted by the scheduler or no space left.
-                    return abortCode;
-                }
-
-                boolean dexOptPerformed = optimizePackage(pm, pkg, isForPrimaryDex);
-                if (dexOptPerformed) {
-                    updatedPackages.add(pkg);
-                }
+            int primaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
+                    /*isForPrimaryDex*/ true, updatedPackages);
+            if (primaryResult != OPTIMIZE_PROCESSED) {
+                return primaryResult;
             }
 
-            return OPTIMIZE_PROCESSED;
+            if (!supportSecondaryDex) {
+                return OPTIMIZE_PROCESSED;
+            }
+
+            int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
+                    /*isForPrimaryDex*/ false, updatedPackages);
+            return secondaryResult;
         } finally {
             // Always let the pinner service know about changes.
             notifyPinService(updatedPackages);
         }
     }
 
+    private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+            long lowStorageThreshold, boolean isForPrimaryDex, ArraySet<String> updatedPackages) {
+        for (String pkg : pkgs) {
+            int abortCode = abortIdleOptimizations(lowStorageThreshold);
+            if (abortCode != OPTIMIZE_CONTINUE) {
+                // Either aborted by the scheduler or no space left.
+                return abortCode;
+            }
+
+            boolean dexOptPerformed = optimizePackage(pm, pkg, isForPrimaryDex);
+            if (dexOptPerformed) {
+                updatedPackages.add(pkg);
+            }
+        }
+        return OPTIMIZE_PROCESSED;
+    }
 
     /**
      * Try to downgrade the package to a smaller compilation filter.
diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java
index a374e14..231168e 100644
--- a/services/core/java/com/android/server/pm/ProtectedPackages.java
+++ b/services/core/java/com/android/server/pm/ProtectedPackages.java
@@ -92,6 +92,9 @@
         if (mDeviceOwnerUserId == userId) {
             return mDeviceOwnerPackage;
         }
+        if (mProfileOwnerPackages == null) {
+            return null;
+        }
         return mProfileOwnerPackages.get(userId);
     }
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 828f790..d67048f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -841,7 +841,8 @@
 
     @Override
     public void setImeWindowStatus(int displayId, final IBinder token, final int vis,
-            final int backDisposition, final boolean showImeSwitcher) {
+            final int backDisposition, final boolean showImeSwitcher,
+            boolean isMultiClientImeEnabled) {
         enforceStatusBar();
 
         if (SPEW) {
@@ -858,7 +859,8 @@
                 if (mBar == null) return;
                 try {
                     mBar.setImeWindowStatus(
-                            displayId, token, vis, backDisposition, showImeSwitcher);
+                            displayId, token, vis, backDisposition, showImeSwitcher,
+                            isMultiClientImeEnabled);
                 } catch (RemoteException ex) { }
             });
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f231af9..20ccfa8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -225,7 +225,6 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
-import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -262,6 +261,7 @@
 import com.android.server.am.UserState;
 import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
+import com.android.server.inputmethod.InputMethodSystemProperty;
 import com.android.server.pm.UserManagerService;
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 26e37d6..8fb23a4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2006,6 +2006,7 @@
 
             int attrChanges = 0;
             int flagChanges = 0;
+            int privateFlagChanges = 0;
             if (attrs != null) {
                 displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
                 // if they don't have the permission, mask out the status bar bits
@@ -2033,7 +2034,8 @@
                     attrs.height = win.mAttrs.height;
                 }
 
-                flagChanges = win.mAttrs.flags ^= attrs.flags;
+                flagChanges = win.mAttrs.flags ^ attrs.flags;
+                privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
                 attrChanges = win.mAttrs.copyFrom(attrs);
                 if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
                         | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
@@ -2050,7 +2052,7 @@
                             win.getDisplayContent().getDisplayId());
                 }
 
-                if ((flagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
+                if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
                     updateNonSystemOverlayWindowsVisibilityIfNeeded(
                             win, win.mWinAnimator.getShown());
                 }
@@ -7543,7 +7545,7 @@
             return;
         }
         final boolean systemAlertWindowsHidden = !mHidingNonSystemOverlayWindows.isEmpty();
-        if (surfaceShown) {
+        if (surfaceShown && win.hideNonSystemOverlayWindowsWhenVisible()) {
             if (!mHidingNonSystemOverlayWindows.contains(win)) {
                 mHidingNonSystemOverlayWindows.add(win);
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 16007d7..6e0d834 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -67,7 +67,6 @@
 import android.util.Slog;
 import android.view.WindowManager;
 import android.view.contentcapture.ContentCaptureManager;
-import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
@@ -101,6 +100,7 @@
 import com.android.server.incident.IncidentCompanionService;
 import com.android.server.input.InputManagerService;
 import com.android.server.inputmethod.InputMethodManagerService;
+import com.android.server.inputmethod.InputMethodSystemProperty;
 import com.android.server.inputmethod.MultiClientInputMethodManagerService;
 import com.android.server.lights.LightsService;
 import com.android.server.media.MediaResourceMonitorService;
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 328e8f4..1c88c40 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -400,22 +400,7 @@
     }
 
     @Test
-    public void testEvaluateStateLocked_HeartbeatsOn() {
-        mConstants.USE_HEARTBEATS = true;
-        final ConnectivityController controller = new ConnectivityController(mService);
-        final JobStatus red = createJobStatus(createJob()
-                .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
-                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
-
-        controller.evaluateStateLocked(red);
-        assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
-        verify(mNetPolicyManagerInternal, never())
-                .setAppIdleWhitelist(eq(UID_RED), anyBoolean());
-    }
-
-    @Test
     public void testEvaluateStateLocked_JobWithoutConnectivity() {
-        mConstants.USE_HEARTBEATS = false;
         final ConnectivityController controller = new ConnectivityController(mService);
         final JobStatus red = createJobStatus(createJob().setMinimumLatency(1));
 
@@ -427,7 +412,6 @@
 
     @Test
     public void testEvaluateStateLocked_JobWouldBeReady() {
-        mConstants.USE_HEARTBEATS = false;
         final ConnectivityController controller = spy(new ConnectivityController(mService));
         doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(any());
         final JobStatus red = createJobStatus(createJob()
@@ -466,7 +450,6 @@
 
     @Test
     public void testEvaluateStateLocked_JobWouldNotBeReady() {
-        mConstants.USE_HEARTBEATS = false;
         final ConnectivityController controller = spy(new ConnectivityController(mService));
         doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(any());
         final JobStatus red = createJobStatus(createJob()
@@ -502,7 +485,6 @@
 
     @Test
     public void testReevaluateStateLocked() {
-        mConstants.USE_HEARTBEATS = false;
         final ConnectivityController controller = spy(new ConnectivityController(mService));
         final JobStatus redOne = createJobStatus(createJob(1)
                 .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
@@ -625,7 +607,7 @@
 
     private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
-        return new JobStatus(job.build(), uid, null, -1, 0, 0, null,
+        return new JobStatus(job.build(), uid, null, -1, 0, null,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 94e02d3b..64da6f6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -573,7 +573,7 @@
             long latestRunTimeElapsedMillis) {
         final JobInfo job = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
-        return new JobStatus(job, 0, null, -1, 0, 0, null, earliestRunTimeElapsedMillis,
+        return new JobStatus(job, 0, null, -1, 0, null, earliestRunTimeElapsedMillis,
                 latestRunTimeElapsedMillis, 0, 0, null, 0);
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 2d70231..8863d5a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -107,7 +107,6 @@
     private static final int SOURCE_USER_ID = 0;
 
     private BroadcastReceiver mChargingReceiver;
-    private Constants mJsConstants;
     private QuotaController mQuotaController;
     private QuotaController.QcConstants mQcConstants;
     private int mSourceUid;
@@ -134,14 +133,11 @@
                 .strictness(Strictness.LENIENT)
                 .mockStatic(LocalServices.class)
                 .startMocking();
-        // Make sure constants turn on QuotaController.
-        mJsConstants = new Constants();
-        mJsConstants.USE_HEARTBEATS = false;
 
         // Called in StateController constructor.
         when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
         when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
-        when(mJobSchedulerService.getConstants()).thenReturn(mJsConstants);
+        when(mJobSchedulerService.getConstants()).thenReturn(mock(Constants.class));
         // Called in QuotaController constructor.
         IActivityManager activityManager = ActivityManager.getService();
         spyOn(activityManager);
@@ -1809,30 +1805,6 @@
                 .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
     }
 
-    /** Tests that QuotaController doesn't throttle if throttling is turned off. */
-    @Test
-    public void testThrottleToggling() throws Exception {
-        setDischarging();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(
-                        JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
-                        10 * MINUTE_IN_MILLIS, 4));
-        JobStatus jobStatus = createJobStatus("testThrottleToggling", 1);
-        setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
-        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
-        assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
-
-        mJsConstants.USE_HEARTBEATS = true;
-        mQuotaController.onConstantsUpdatedLocked();
-        Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background.
-        assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
-
-        mJsConstants.USE_HEARTBEATS = false;
-        mQuotaController.onConstantsUpdatedLocked();
-        Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background.
-        assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
-    }
-
     @Test
     public void testConstantsUpdating_ValidValues() {
         mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index a4267b8..1084d62 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -179,9 +179,9 @@
         addA11yWindowInfo(mA11yWindowInfos, WINDOWID, false);
         addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true);
         when(mMockA11yWindowManager.getWindowListLocked()).thenReturn(mA11yWindowInfos);
-        when(mMockA11yWindowManager.findA11yWindowInfoById(WINDOWID))
+        when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
                 .thenReturn(mA11yWindowInfos.get(0));
-        when(mMockA11yWindowManager.findA11yWindowInfoById(PIP_WINDOWID))
+        when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(PIP_WINDOWID))
                 .thenReturn(mA11yWindowInfos.get(1));
         final RemoteAccessibilityConnection conn = getRemoteA11yConnection(
                 WINDOWID, mMockIA11yInteractionConnection, PACKAGE_NAME1);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index bb35776..04ac7fe 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -166,7 +166,7 @@
     public void canDispatchAccessibilityEvent_otherEvents_windowIdExist_returnTrue() {
         when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM))
                 .thenReturn(WINDOWID2);
-        when(mMockA11yWindowManager.findA11yWindowInfoById(WINDOWID))
+        when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
                 .thenReturn(AccessibilityWindowInfo.obtain());
         for (int i = 0; i < OTHER_EVENTS.length; i++) {
             final AccessibilityEvent event = AccessibilityEvent.obtain(OTHER_EVENTS[i]);
@@ -287,7 +287,7 @@
                 .thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
         when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM))
                 .thenReturn(WINDOWID2);
-        when(mMockA11yWindowManager.findA11yWindowInfoById(WINDOWID))
+        when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
                 .thenReturn(AccessibilityWindowInfo.obtain());
 
         assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(UserHandle.USER_SYSTEM,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 7e64caf..7887d5b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -34,6 +34,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -53,6 +54,7 @@
 
 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
 import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
 
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
@@ -75,9 +77,12 @@
     private static final boolean FORCE_SEND = true;
     private static final boolean SEND_ON_WINDOW_CHANGES = false;
     private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM;
+    // TO-DO [Multi-Display] : change the display count to 2
+    private static final int DISPLAY_COUNT = 1;
     private static final int NUM_GLOBAL_WINDOWS = 4;
     private static final int NUM_APP_WINDOWS = 4;
-    private static final int NUM_OF_WINDOWS = NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS;
+    private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS)
+            * DISPLAY_COUNT;
     private static final int DEFAULT_FOCUSED_INDEX = 1;
     private static final int SCREEN_WIDTH = 1080;
     private static final int SCREEN_HEIGHT = 1920;
@@ -86,7 +91,13 @@
 
     // List of window token, mapping from windowId -> window token.
     private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>();
-    private final ArrayList<WindowInfo> mWindowInfos = new ArrayList<>();
+    // List of window info lists, mapping from displayId -> window info lists.
+    private final SparseArray<ArrayList<WindowInfo>> mWindowInfos =
+            new SparseArray<>();
+    // List of callback, mapping from displayId -> callback.
+    private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows =
+            new SparseArray<>();
+
     private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
 
     @Mock private WindowManagerInternal mMockWindowManagerInternal;
@@ -109,29 +120,14 @@
                 mMockA11ySecurityPolicy,
                 mMockA11yUserManager);
 
-        // Add RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
-        // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos
-        // for the test.
-        int layer = 0;
-        for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) {
-            final IWindow token = addAccessibilityInteractionConnection(true);
-            addWindowInfo(token, layer++);
-
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            when(mMockWindowManagerInternal.setWindowsForAccessibilityCallback(eq(i), any()))
+                    .thenReturn(true);
+            startTrackingPerDisplay(i);
         }
-        for (int i = 0; i < NUM_APP_WINDOWS; i++) {
-            final IWindow token = addAccessibilityInteractionConnection(false);
-            addWindowInfo(token, layer++);
-        }
-        // setup default focus
-        mWindowInfos.get(DEFAULT_FOCUSED_INDEX).focused = true;
-        // Turn on windows tracking, and update window info
-        mA11yWindowManager.startTrackingWindows();
-        mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos);
-        assertEquals(mA11yWindowManager.getWindowListLocked().size(),
-                mWindowInfos.size());
 
         // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged.
-        // Reset it for mockito verify of further test case.
+        // Resets it for mockito verify of further test case.
         Mockito.reset(mMockA11yEventSender);
     }
 
@@ -142,11 +138,12 @@
 
     @Test
     public void startTrackingWindows_shouldEnableWindowManagerCallback() {
-        // AccessibilityWindowManager#startTrackingWindows already invoked in setup
+        // AccessibilityWindowManager#startTrackingWindows already invoked in setup.
         assertTrue(mA11yWindowManager.isTrackingWindowsLocked());
-        // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY
+        final WindowsForAccessibilityCallback callbacks =
+                mCallbackOfWindows.get(Display.DEFAULT_DISPLAY);
         verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(
-                eq(Display.DEFAULT_DISPLAY), any());
+                eq(Display.DEFAULT_DISPLAY), eq(callbacks));
     }
 
     @Test
@@ -156,9 +153,9 @@
 
         mA11yWindowManager.stopTrackingWindows();
         assertFalse(mA11yWindowManager.isTrackingWindowsLocked());
-        // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY
         verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(
-                eq(Display.DEFAULT_DISPLAY), any());
+                eq(Display.DEFAULT_DISPLAY), isNull());
+
     }
 
     @Test
@@ -177,87 +174,102 @@
     @Test
     public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() {
         final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
-        WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX);
+        WindowInfo focusedWindowInfo =
+                mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX);
         assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked(
                 USER_SYSTEM_ID, focusedWindowInfo.token));
 
         focusedWindowInfo.focused = false;
-        focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1);
+        focusedWindowInfo =
+                mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1);
         focusedWindowInfo.focused = true;
 
         mA11yWindowManager.onTouchInteractionStart();
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+        setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1);
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
         assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
     }
 
     @Test
     public void onWindowsChanged_shouldReportCorrectLayer() {
-        // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup
-        List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
+        // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
+        List<AccessibilityWindowInfo> a11yWindows =
+                mA11yWindowManager.getWindowListLocked();
         for (int i = 0; i < a11yWindows.size(); i++) {
             final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
-            final WindowInfo windowInfo = mWindowInfos.get(i);
-            assertThat(mWindowInfos.size() - windowInfo.layer - 1,
+            final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
+            assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1,
                     is(a11yWindow.getLayer()));
         }
     }
 
     @Test
     public void onWindowsChanged_shouldReportCorrectOrder() {
-        // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup
-        List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
+        // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
+        List<AccessibilityWindowInfo> a11yWindows =
+                mA11yWindowManager.getWindowListLocked();
         for (int i = 0; i < a11yWindows.size(); i++) {
             final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
             final IBinder windowToken = mA11yWindowManager
                     .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId());
-            final WindowInfo windowInfo = mWindowInfos.get(i);
+            final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
             assertThat(windowToken, is(windowInfo.token));
         }
     }
 
     @Test
     public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
-        final WindowInfo windowInfo = mWindowInfos.get(0);
-        final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer();
+        final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+        final int correctLayer =
+                mA11yWindowManager.getWindowListLocked().get(0).getLayer();
         windowInfo.layer += 1;
 
-        mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos);
-        assertNotEquals(correctLayer, mA11yWindowManager.getWindowListLocked().get(0).getLayer());
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+        assertNotEquals(correctLayer,
+                mA11yWindowManager.getWindowListLocked().get(0).getLayer());
     }
 
     @Test
     public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() {
-        final WindowInfo windowInfo = mWindowInfos.get(0);
-        final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer();
+        final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+        final int correctLayer =
+                mA11yWindowManager.getWindowListLocked().get(0).getLayer();
         windowInfo.layer += 1;
 
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
-        assertEquals(correctLayer, mA11yWindowManager.getWindowListLocked().get(0).getLayer());
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+        assertEquals(correctLayer,
+                mA11yWindowManager.getWindowListLocked().get(0).getLayer());
     }
 
     @Test
     public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows()
             throws RemoteException {
-        final AccessibilityWindowInfo oldWindow = mA11yWindowManager.getWindowListLocked().get(0);
-        final IWindow token = addAccessibilityInteractionConnection(true);
+        final AccessibilityWindowInfo oldWindow =
+                mA11yWindowManager.getWindowListLocked().get(0);
+        final IWindow token =
+                addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, true);
         final WindowInfo windowInfo = WindowInfo.obtain();
         windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
         windowInfo.token = token.asBinder();
         windowInfo.layer = 0;
         windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
-        mWindowInfos.set(0, windowInfo);
+        mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo);
 
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
-        assertNotEquals(oldWindow, mA11yWindowManager.getWindowListLocked().get(0));
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+        assertNotEquals(oldWindow,
+                mA11yWindowManager.getWindowListLocked().get(0));
     }
 
     @Test
     public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() {
-        final WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX);
-        final WindowInfo windowInfo = mWindowInfos.get(0);
+        final WindowInfo focusedWindowInfo =
+                mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX);
+        final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
         focusedWindowInfo.focused = false;
         windowInfo.focused = true;
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
         assertTrue(mA11yWindowManager.getWindowListLocked().get(0).isFocused());
     }
 
@@ -288,7 +300,8 @@
 
     @Test
     public void getWindowTokenForUserAndWindowId_shouldNotNull() {
-        final List<AccessibilityWindowInfo> windows = mA11yWindowManager.getWindowListLocked();
+        final List<AccessibilityWindowInfo> windows =
+                mA11yWindowManager.getWindowListLocked();
         for (int i = 0; i < windows.size(); i++) {
             final int windowId = windows.get(i).getId();
 
@@ -299,7 +312,8 @@
 
     @Test
     public void findWindowId() {
-        final List<AccessibilityWindowInfo> windows = mA11yWindowManager.getWindowListLocked();
+        final List<AccessibilityWindowInfo> windows =
+                mA11yWindowManager.getWindowListLocked();
         for (int i = 0; i < windows.size(); i++) {
             final int windowId = windows.get(i).getId();
             final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
@@ -313,13 +327,15 @@
     @Test
     public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() {
         // Updates top 2 z-order WindowInfo are whole visible.
-        WindowInfo windowInfo = mWindowInfos.get(0);
+        WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
         windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
-        windowInfo = mWindowInfos.get(1);
-        windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT);
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+        windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1);
+        windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2,
+                SCREEN_WIDTH, SCREEN_HEIGHT);
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
 
-        final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
+        final List<AccessibilityWindowInfo> a11yWindows =
+                mA11yWindowManager.getWindowListLocked();
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(0).getId();
 
@@ -336,12 +352,13 @@
 
     @Test
     public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() {
-        // Updates z-order #1 WindowInfo is half visible
-        WindowInfo windowInfo = mWindowInfos.get(0);
+        // Updates z-order #1 WindowInfo is half visible.
+        WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
         windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
 
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
-        final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+        final List<AccessibilityWindowInfo> a11yWindows =
+                mA11yWindowManager.getWindowListLocked();
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(1).getId();
 
@@ -353,7 +370,8 @@
     @Test
     public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() {
         // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible.
-        final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
+        final List<AccessibilityWindowInfo> a11yWindows =
+                mA11yWindowManager.getWindowListLocked();
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(1).getId();
 
@@ -366,11 +384,12 @@
         // Updates z-order #0 WindowInfo to have two interact-able areas.
         Region region = new Region(0, 0, SCREEN_WIDTH, 200);
         region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION);
-        WindowInfo windowInfo = mWindowInfos.get(0);
+        WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
         windowInfo.regionInScreen.set(region);
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
 
-        final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked();
+        final List<AccessibilityWindowInfo> a11yWindows =
+                mA11yWindowManager.getWindowListLocked();
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(1).getId();
 
@@ -382,7 +401,8 @@
 
     @Test
     public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() {
-        final IBinder eventWindowToken = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1).token;
+        final IBinder eventWindowToken =
+                mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token;
         final int eventWindowId = mA11yWindowManager.findWindowIdLocked(
                 USER_SYSTEM_ID, eventWindowToken);
         when(mMockWindowManagerInternal.getFocusedWindowToken())
@@ -402,7 +422,8 @@
 
     @Test
     public void updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow() {
-        final int eventWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX + 1);
+        final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+                DEFAULT_FOCUSED_INDEX + 1);
         final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
         assertThat(currentActiveWindowId, is(not(eventWindowId)));
 
@@ -428,7 +449,8 @@
 
     @Test
     public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus() {
-        final int eventWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX);
+        final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+                DEFAULT_FOCUSED_INDEX);
         final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId(
                 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
         assertThat(currentA11yFocusedWindowId, is(not(eventWindowId)));
@@ -457,7 +479,8 @@
 
     @Test
     public void updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus() {
-        final int eventWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX);
+        final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+                DEFAULT_FOCUSED_INDEX);
         final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId(
                 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
         assertThat(currentA11yFocusedWindowId, is(not(eventWindowId)));
@@ -482,7 +505,8 @@
 
     @Test
     public void onTouchInteractionEnd_shouldRollbackActiveWindow() {
-        final int eventWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX + 1);
+        final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+                DEFAULT_FOCUSED_INDEX + 1);
         final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
         assertThat(currentActiveWindowId, is(not(eventWindowId)));
 
@@ -513,12 +537,14 @@
     @Test
     public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus()
             throws RemoteException {
-        final IBinder defaultFocusWinToken = mWindowInfos.get(DEFAULT_FOCUSED_INDEX).token;
+        final IBinder defaultFocusWinToken =
+                mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token;
         final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked(
                 USER_SYSTEM_ID, defaultFocusWinToken);
         when(mMockWindowManagerInternal.getFocusedWindowToken())
                 .thenReturn(defaultFocusWinToken);
-        final int newFocusWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX + 1);
+        final int newFocusWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
+                DEFAULT_FOCUSED_INDEX + 1);
         final IAccessibilityInteractionConnection mockNewFocusConnection =
                 mA11yWindowManager.getConnectionLocked(
                         USER_SYSTEM_ID, newFocusWindowId).getRemote();
@@ -556,28 +582,72 @@
 
     @Test
     public void getPictureInPictureWindow_shouldNotNull() {
-        assertNull(mA11yWindowManager.getPictureInPictureWindow());
-        mWindowInfos.get(1).inPictureInPicture = true;
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+        assertNull(mA11yWindowManager.getPictureInPictureWindowLocked());
+        mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true;
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
 
-        assertNotNull(mA11yWindowManager.getPictureInPictureWindow());
+        assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked());
     }
 
     @Test
     public void notifyOutsideTouch() throws RemoteException {
-        final int targetWindowId = getWindowIdFromWindowInfos(1);
-        final int outsideWindowId = getWindowIdFromWindowInfos(0);
+        final int targetWindowId =
+                getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 1);
+        final int outsideWindowId =
+                getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
         final IAccessibilityInteractionConnection mockRemoteConnection =
                 mA11yWindowManager.getConnectionLocked(
                         USER_SYSTEM_ID, outsideWindowId).getRemote();
-        mWindowInfos.get(0).hasFlagWatchOutsideTouch = true;
-        mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos);
+        mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true;
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
 
         mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId);
         verify(mockRemoteConnection).notifyOutsideTouch();
     }
 
-    private IWindow addAccessibilityInteractionConnection(boolean bGlobal)
+    private void startTrackingPerDisplay(int displayId) throws RemoteException {
+        ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
+        // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
+        // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos
+        // for the test.
+        int layer = 0;
+        for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) {
+            final IWindow token = addAccessibilityInteractionConnection(displayId, true);
+            addWindowInfo(windowInfosForDisplay, token, layer++);
+
+        }
+        for (int i = 0; i < NUM_APP_WINDOWS; i++) {
+            final IWindow token = addAccessibilityInteractionConnection(displayId, false);
+            addWindowInfo(windowInfosForDisplay, token, layer++);
+        }
+        // Setups default focus.
+        windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true;
+        // Turns on windows tracking, and update window info.
+        mA11yWindowManager.startTrackingWindows();
+        // Puts window lists into array.
+        mWindowInfos.put(displayId, windowInfosForDisplay);
+        // Sets the default display as the top focused display.
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX);
+        }
+        // Invokes callback for sending window lists to A11y framework.
+        onWindowsForAccessibilityChanged(displayId, FORCE_SEND);
+
+        assertEquals(mA11yWindowManager.getWindowListLocked().size(),
+                windowInfosForDisplay.size());
+    }
+
+    private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) {
+        ArgumentCaptor<WindowsForAccessibilityCallback> windowsForAccessibilityCallbacksCaptor =
+                ArgumentCaptor.forClass(
+                        WindowManagerInternal.WindowsForAccessibilityCallback.class);
+        verify(mMockWindowManagerInternal)
+                .setWindowsForAccessibilityCallback(eq(displayId),
+                        windowsForAccessibilityCallbacksCaptor.capture());
+        return windowsForAccessibilityCallbacksCaptor.getValue();
+    }
+
+    private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal)
             throws RemoteException {
         final IWindow mockWindowToken = Mockito.mock(IWindow.class);
         final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
@@ -588,6 +658,8 @@
         when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
         when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(USER_SYSTEM_ID))
                 .thenReturn(bGlobal);
+        when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+                .thenReturn(displayId);
 
         int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
                 mockWindowToken, mockA11yConnection, PACKAGE_NAME, USER_SYSTEM_ID);
@@ -595,21 +667,40 @@
         return mockWindowToken;
     }
 
-    private void addWindowInfo(IWindow windowToken, int layer) {
+    private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) {
         final WindowInfo windowInfo = WindowInfo.obtain();
         windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
         windowInfo.token = windowToken.asBinder();
         windowInfo.layer = layer;
         windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
-        mWindowInfos.add(windowInfo);
+        windowInfos.add(windowInfo);
     }
 
-    private int getWindowIdFromWindowInfos(int index) {
-        final IBinder windowToken = mWindowInfos.get(index).token;
+    private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) {
+        final IBinder windowToken = mWindowInfos.get(displayId).get(index).token;
         return mA11yWindowManager.findWindowIdLocked(
                 USER_SYSTEM_ID, windowToken);
     }
 
+    private void setTopFocusedWindowAndDisplay(int displayId, int index) {
+        // Sets the top focus window.
+        final IBinder eventWindowToken = mWindowInfos.get(displayId).get(index).token;
+        when(mMockWindowManagerInternal.getFocusedWindowToken())
+                .thenReturn(eventWindowToken);
+        // Sets the top focused display.
+        when(mMockWindowManagerInternal.getDisplayIdForWindow(eventWindowToken))
+                .thenReturn(displayId);
+    }
+
+    private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) {
+        WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId);
+        if (callbacks == null) {
+            callbacks = getWindowsForAccessibilityCallbacks(displayId);
+            mCallbackOfWindows.put(displayId, callbacks);
+        }
+        callbacks.onWindowsForAccessibilityChanged(forceSend, mWindowInfos.get(displayId));
+    }
+
     static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
         private int mWindowId;
 
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 0309dbe..cae7b57 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -273,7 +273,7 @@
                 invalidLateRuntimeElapsedMillis - TWO_HOURS;  // Early is (late - period).
         final Pair<Long, Long> persistedExecutionTimesUTC = new Pair<>(rtcNow, rtcNow + ONE_HOUR);
         final JobStatus js = new JobStatus(b.build(), SOME_UID, "somePackage",
-                0 /* sourceUserId */, 0, 0, "someTag",
+                0 /* sourceUserId */, 0, "someTag",
                 invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis,
                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
                 persistedExecutionTimesUTC, 0 /* innerFlagg */);
diff --git a/startop/scripts/app_startup/app_startup_runner.py b/startop/scripts/app_startup/app_startup_runner.py
index 7b3bf33..eb582f9 100755
--- a/startop/scripts/app_startup/app_startup_runner.py
+++ b/startop/scripts/app_startup/app_startup_runner.py
@@ -32,21 +32,25 @@
 import os
 import sys
 import tempfile
+from datetime import timedelta
 from typing import Any, Callable, Iterable, List, NamedTuple, TextIO, Tuple, \
     TypeVar, Union, Optional
 
 # local import
 DIR = os.path.abspath(os.path.dirname(__file__))
 sys.path.append(os.path.dirname(DIR))
+import lib.cmd_utils as cmd_utils
+import lib.print_utils as print_utils
+import iorap.compiler as compiler
 from app_startup.run_app_with_prefetch import PrefetchAppRunner
 import app_startup.lib.args_utils as args_utils
 from app_startup.lib.data_frame import DataFrame
-import lib.cmd_utils as cmd_utils
-import lib.print_utils as print_utils
+from app_startup.lib.perfetto_trace_collector import PerfettoTraceCollector
 
 # The following command line options participate in the combinatorial generation.
 # All other arguments have a global effect.
-_COMBINATORIAL_OPTIONS = ['package', 'readahead', 'compiler_filter', 'activity']
+_COMBINATORIAL_OPTIONS = ['package', 'readahead', 'compiler_filter',
+                          'activity', 'trace_duration']
 _TRACING_READAHEADS = ['mlock', 'fadvise']
 _FORWARD_OPTIONS = {'loop_count': '--count'}
 _RUN_SCRIPT = os.path.join(os.path.dirname(os.path.realpath(__file__)),
@@ -54,9 +58,8 @@
 
 CollectorPackageInfo = NamedTuple('CollectorPackageInfo',
                                   [('package', str), ('compiler_filter', str)])
-_COLLECTOR_SCRIPT = os.path.join(os.path.dirname(os.path.realpath(__file__)),
-                                 '../iorap/collector')
-_COLLECTOR_TIMEOUT_MULTIPLIER = 10  # take the regular --timeout and multiply
+_COMPILER_SCRIPT = os.path.join(os.path.dirname(os.path.dirname(
+    os.path.realpath(__file__))), 'iorap/compiler.py')
 # by 2; systrace starts up slowly.
 
 _UNLOCK_SCREEN_SCRIPT = os.path.join(
@@ -70,11 +73,14 @@
                              ('timeout', Optional[int]),
                              ('debug', bool),
                              ('simulate', bool),
-                             ('input', Optional[str])])
+                             ('input', Optional[str]),
+                             ('trace_duration', Optional[timedelta])])
 
 # This must be the only mutable global variable. All other global variables are constants to avoid magic literals.
 _debug = False  # See -d/--debug flag.
 _DEBUG_FORCE = None  # Ignore -d/--debug if this is not none.
+_PERFETTO_TRACE_DURATION_MS = 5000 # milliseconds
+_PERFETTO_TRACE_DURATION = timedelta(milliseconds=_PERFETTO_TRACE_DURATION_MS)
 
 # Type hinting names.
 T = TypeVar('T')
@@ -123,26 +129,15 @@
   optional_named.add_argument('-in', '--inodes', dest='inodes', type=str,
                               action='store',
                               help='Path to inodes file (system/extras/pagecache/pagecache.py -d inodes)')
+  optional_named.add_argument('--compiler-trace-duration-ms',
+                              dest='trace_duration',
+                              type=lambda ms_str: timedelta(milliseconds=int(ms_str)),
+                              action='append',
+                              help='The trace duration (milliseconds) in '
+                                   'compilation')
 
   return parser.parse_args(argv)
 
-def make_script_command_with_temp_output(script: str,
-                                         args: List[str],
-                                         **kwargs) -> Tuple[str, TextIO]:
-  """
-  Create a command to run a script given the args.
-  Appends --count <loop_count> --output <tmp-file-name>.
-  Returns a tuple (cmd, tmp_file)
-  """
-  tmp_output_file = tempfile.NamedTemporaryFile(mode='r')
-  cmd = [script] + args
-  for key, value in kwargs.items():
-    cmd += ['--%s' % (key), "%s" % (value)]
-  if _debug:
-    cmd += ['--verbose']
-  cmd = cmd + ["--output", tmp_output_file.name]
-  return cmd, tmp_output_file
-
 def key_to_cmdline_flag(key: str) -> str:
   """Convert key into a command line flag, e.g. 'foo-bars' -> '--foo-bar' """
   if key.endswith("s"):
@@ -163,26 +158,26 @@
     args.append(value)
   return args
 
-def run_collector_script(collector_info: CollectorPackageInfo,
-                         inodes_path: str,
-                         timeout: int,
-                         simulate: bool) -> Tuple[bool, TextIO]:
-  """Run collector to collect prefetching trace. """
-  # collector_args = ["--package", package_name]
-  collector_args = as_run_command(collector_info)
-  # TODO: forward --wait_time for how long systrace runs?
-  # TODO: forward --trace_buffer_size for size of systrace buffer size?
-  collector_cmd, collector_tmp_output_file = make_script_command_with_temp_output(
-      _COLLECTOR_SCRIPT, collector_args, inodes=inodes_path)
+def run_perfetto_collector(collector_info: CollectorPackageInfo,
+                           timeout: int,
+                           simulate: bool) -> Tuple[bool, TextIO]:
+  """Run collector to collect prefetching trace.
 
-  collector_timeout = timeout and _COLLECTOR_TIMEOUT_MULTIPLIER * timeout
-  (collector_passed, collector_script_output) = \
-    cmd_utils.execute_arbitrary_command(collector_cmd,
-                                        collector_timeout,
-                                        shell=False,
-                                        simulate=simulate)
+  Returns:
+    A tuple of whether the collection succeeds and the generated trace file.
+  """
+  tmp_output_file = tempfile.NamedTemporaryFile()
 
-  return collector_passed, collector_tmp_output_file
+  collector = PerfettoTraceCollector(package=collector_info.package,
+                                     activity=None,
+                                     compiler_filter=collector_info.compiler_filter,
+                                     timeout=timeout,
+                                     simulate=simulate,
+                                     trace_duration=_PERFETTO_TRACE_DURATION,
+                                     save_destination_file_path=tmp_output_file.name)
+  result = collector.run()
+
+  return result is not None, tmp_output_file
 
 def parse_run_script_csv_file(csv_file: TextIO) -> DataFrame:
   """Parse a CSV file full of integers into a DataFrame."""
@@ -216,6 +211,52 @@
 
   return DataFrame(d)
 
+def compile_perfetto_trace(inodes_path: str,
+                           perfetto_trace_file: str,
+                           trace_duration: Optional[timedelta]) -> TextIO:
+  compiler_trace_file = tempfile.NamedTemporaryFile()
+  argv = [_COMPILER_SCRIPT, '-i', inodes_path, '--perfetto-trace',
+          perfetto_trace_file, '-o', compiler_trace_file.name]
+
+  if trace_duration is not None:
+    argv += ['--duration', str(int(trace_duration.total_seconds()
+                               * PerfettoTraceCollector.MS_PER_SEC))]
+
+  print_utils.debug_print(argv)
+  compiler.main(argv)
+  return compiler_trace_file
+
+def execute_run_using_perfetto_trace(collector_info,
+                                     run_combos: Iterable[RunCommandArgs],
+                                     simulate: bool,
+                                     inodes_path: str,
+                                     timeout: int) -> DataFrame:
+  """ Executes run based on perfetto trace. """
+  passed, perfetto_trace_file = run_perfetto_collector(collector_info,
+                                                       timeout,
+                                                       simulate)
+  if not passed:
+    raise RuntimeError('Cannot run perfetto collector!')
+
+  with perfetto_trace_file:
+    for combos in run_combos:
+      if combos.readahead in _TRACING_READAHEADS:
+        if simulate:
+          compiler_trace_file = tempfile.NamedTemporaryFile()
+        else:
+          compiler_trace_file = compile_perfetto_trace(inodes_path,
+                                                       perfetto_trace_file.name,
+                                                       combos.trace_duration)
+        with compiler_trace_file:
+          combos = combos._replace(input=compiler_trace_file.name)
+          print_utils.debug_print(combos)
+          output = PrefetchAppRunner(**combos._asdict()).run()
+      else:
+        print_utils.debug_print(combos)
+        output = PrefetchAppRunner(**combos._asdict()).run()
+
+      yield DataFrame(dict((x, [y]) for x, y in output)) if output else None
+
 def execute_run_combos(
     grouped_run_combos: Iterable[Tuple[CollectorPackageInfo, Iterable[RunCommandArgs]]],
     simulate: bool,
@@ -228,19 +269,11 @@
                                       shell=False)
 
   for collector_info, run_combos in grouped_run_combos:
-    for combos in run_combos:
-      args = as_run_command(combos)
-      if combos.readahead in _TRACING_READAHEADS:
-        passed, collector_tmp_output_file = run_collector_script(collector_info,
-                                                                 inodes_path,
-                                                                 timeout,
-                                                                 simulate)
-        combos = combos._replace(input=collector_tmp_output_file.name)
-
-      print_utils.debug_print(combos)
-      output = PrefetchAppRunner(**combos._asdict()).run()
-
-      yield DataFrame(dict((x, [y]) for x, y in output)) if output else None
+    yield from execute_run_using_perfetto_trace(collector_info,
+                                                run_combos,
+                                                simulate,
+                                                inodes_path,
+                                                timeout)
 
 def gather_results(commands: Iterable[Tuple[DataFrame]],
                    key_list: List[str], value_list: List[Tuple[str, ...]]):
diff --git a/startop/scripts/app_startup/app_startup_runner_test.py b/startop/scripts/app_startup/app_startup_runner_test.py
index 9aa7014..42ea5f0 100755
--- a/startop/scripts/app_startup/app_startup_runner_test.py
+++ b/startop/scripts/app_startup/app_startup_runner_test.py
@@ -91,7 +91,8 @@
   # Combine it with all of the "optional" parameters' default values.
   """
   d = {'compiler_filters': None, 'simulate': False, 'debug': False,
-       'output': None, 'timeout': 10, 'loop_count': 1, 'inodes': None}
+       'output': None, 'timeout': 10, 'loop_count': 1, 'inodes': None,
+       'trace_duration': None}
   d.update(kwargs)
   return d
 
@@ -159,19 +160,6 @@
   assert asr.key_to_cmdline_flag("ba_r") == "--ba-r"
   assert asr.key_to_cmdline_flag("ba_zs") == "--ba-z"
 
-def test_make_script_command_with_temp_output():
-  cmd_str, tmp_file = asr.make_script_command_with_temp_output("fake_script",
-                                                               args=[], count=1)
-  with tmp_file:
-    assert cmd_str == ["fake_script", "--count", "1", "--output", tmp_file.name]
-
-  cmd_str, tmp_file = asr.make_script_command_with_temp_output("fake_script",
-                                                               args=['a', 'b'],
-                                                               count=2)
-  with tmp_file:
-    assert cmd_str == ["fake_script", "a", "b", "--count", "2", "--output",
-                       tmp_file.name]
-
 def test_parse_run_script_csv_file():
   # empty file -> empty list
   f = io.StringIO("")
diff --git a/startop/scripts/app_startup/lib/app_runner.py b/startop/scripts/app_startup/lib/app_runner.py
index a8afd6a..78873fa 100644
--- a/startop/scripts/app_startup/lib/app_runner.py
+++ b/startop/scripts/app_startup/lib/app_runner.py
@@ -51,6 +51,7 @@
       returns:
         a string in the format of "<metric>=<value>\n<metric>=<value>\n..."
         for further parsing. For example "TotalTime=123\nDisplayedTime=121".
+        Return an empty string if no metrics need to be parsed further.
         """
     pass
 
@@ -61,7 +62,7 @@
   APP_STARTUP_DIR = os.path.dirname(DIR)
   IORAP_COMMON_BASH_SCRIPT = os.path.realpath(os.path.join(DIR,
                                                            '../../iorap/common'))
-  DEFAULT_TIMEOUT = 30
+  DEFAULT_TIMEOUT = 30 # seconds
 
   def __init__(self,
                package: str,
diff --git a/startop/scripts/app_startup/lib/perfetto_trace_collector.py b/startop/scripts/app_startup/lib/perfetto_trace_collector.py
index 20a6c52..9ffb349 100644
--- a/startop/scripts/app_startup/lib/perfetto_trace_collector.py
+++ b/startop/scripts/app_startup/lib/perfetto_trace_collector.py
@@ -14,11 +14,11 @@
 
 """Class to collector perfetto trace."""
 import datetime
-from datetime import timedelta
 import os
 import re
 import sys
 import time
+from datetime import timedelta
 from typing import Optional, List, Tuple
 
 # global variables
@@ -40,7 +40,9 @@
   """
   TRACE_FILE_SUFFIX = 'perfetto_trace.pb'
   TRACE_DURATION_PROP = 'iorapd.perfetto.trace_duration_ms'
-  SECONDS_TO_MILLISECONDS = 1000
+  MS_PER_SEC  = 1000
+  DEFAULT_TRACE_DURATION = timedelta(milliseconds=5000) # 5 seconds
+  _COLLECTOR_TIMEOUT_MULTIPLIER = 10  # take the regular timeout and multiply
 
   def __init__(self,
                package: str,
@@ -48,7 +50,7 @@
                compiler_filter: Optional[str],
                timeout: Optional[int],
                simulate: bool,
-               trace_duration: timedelta = timedelta(milliseconds=5000),
+               trace_duration: timedelta = DEFAULT_TRACE_DURATION,
                save_destination_file_path: Optional[str] = None):
     """ Initialize the perfetto trace collector. """
     self.app_runner = AppRunner(package,
@@ -89,7 +91,7 @@
     # Set perfetto trace duration prop to milliseconds.
     adb_utils.set_prop(PerfettoTraceCollector.TRACE_DURATION_PROP,
                        int(self.trace_duration.total_seconds()*
-                           PerfettoTraceCollector.SECONDS_TO_MILLISECONDS))
+                           PerfettoTraceCollector.MS_PER_SEC))
 
     if not iorapd_utils.stop_iorapd():
       raise RuntimeError('Cannot stop iorapd!')
@@ -122,7 +124,7 @@
     manner until all metrics have been found".
 
     Returns:
-      An empty string.
+      An empty string because the metric needs no further parsing.
     """
     if not self._wait_for_perfetto_trace(pre_launch_timestamp):
       raise RuntimeError('Could not save perfetto app trace file!')
@@ -143,14 +145,19 @@
                          format(self._get_remote_path()))
 
     # The pre_launch_timestamp is longer than what the datetime can parse. Trim
-    # last three digits to make them align.
+    # last three digits to make them align. For example:
+    # 2019-07-02 23:20:06.972674825999 -> 2019-07-02 23:20:06.972674825
+    assert len(pre_launch_timestamp) == len('2019-07-02 23:20:06.972674825')
     timestamp = datetime.datetime.strptime(pre_launch_timestamp[:-3],
                                            '%Y-%m-%d %H:%M:%S.%f')
-    timeout_dt = timestamp + datetime.timedelta(0, self.app_runner.timeout)
+
+    # The timeout of perfetto trace is longer than the normal app run timeout.
+    timeout_dt = self.app_runner.timeout * PerfettoTraceCollector._COLLECTOR_TIMEOUT_MULTIPLIER
+    timeout_end = timestamp + datetime.timedelta(seconds=timeout_dt)
 
     return logcat_utils.blocking_wait_for_logcat_pattern(timestamp,
                                                          pattern,
-                                                         timeout_dt)
+                                                         timeout_end)
 
   def _get_remote_path(self):
     # For example: android.music%2Fmusic.TopLevelActivity.perfetto_trace.pb
diff --git a/startop/scripts/iorap/compiler.py b/startop/scripts/iorap/compiler.py
index 9527e28..ef9b870 100755
--- a/startop/scripts/iorap/compiler.py
+++ b/startop/scripts/iorap/compiler.py
@@ -31,8 +31,10 @@
 from pathlib import Path
 from typing import Iterable, Optional, List
 
-from generated.TraceFile_pb2 import *
-from lib.inode2filename import Inode2Filename
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR))
+from iorap.generated.TraceFile_pb2 import *
+from iorap.lib.inode2filename import Inode2Filename
 
 parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
 sys.path.append(parent_dir_name)
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
index c68793d..8febfb7 100644
--- a/startop/view_compiler/dex_layout_compiler.cc
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -23,25 +23,6 @@
 
 using android::base::StringPrintf;
 
-void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
-  if (0 == name.compare(u"merge")) {
-    message_ = "Merge tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"include")) {
-    message_ = "Include tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"view")) {
-    message_ = "View tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"fragment")) {
-    message_ = "Fragment tags are not supported";
-    can_compile_ = false;
-  }
-}
-
 DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
     : method_{method},
       context_{dex::Value::Parameter(0)},
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cd79f37..ce21112 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2328,6 +2328,13 @@
     public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
 
     /**
+     * Indicates if the TTY HCO and VCO options should be hidden in the accessibility menu
+     * if the device is capable of RTT.
+     * @hide
+     */
+    public static final String KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL = "hide_tty_hco_vco_with_rtt";
+
+    /**
      * The flag to disable the popup dialog which warns the user of data charges.
      * @hide
      */
@@ -3406,6 +3413,7 @@
         sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
         sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index cdf4c93..aa7e21a 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -340,6 +340,19 @@
      */
     public static final int MEDIA_TIMEOUT = 77;
 
+    /**
+     * Indicates that an emergency call cannot be placed over WFC because the service is not
+     * available in the current location.
+     * @hide
+     */
+    public static final int EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE = 78;
+
+    /**
+     * Indicates that WiFi calling service is not available in the current location.
+     * @hide
+     */
+    public static final int WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 79;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Update toString() with the newly added disconnect type.
@@ -510,6 +523,10 @@
             return "OTASP_PROVISIONING_IN_PROCESS";
         case MEDIA_TIMEOUT:
             return "MEDIA_TIMEOUT";
+        case EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
+            return "EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE";
+        case WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
+            return "WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION";
         default:
             return "INVALID: " + cause;
         }
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index a478606..20aba4d 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -871,6 +871,19 @@
      */
     public static final int CODE_REJECT_ONGOING_CS_CALL = 1621;
 
+    /**
+     * An attempt was made to place an emergency call over WFC when emergency services is not
+     * currently available in the current location.
+     * @hide
+     */
+    public static final int CODE_EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE = 1622;
+
+    /**
+     * Indicates that WiFi calling service is not available in the current location.
+     * @hide
+     */
+    public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
+
     /*
      * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
      * would be replaced by ERROR_UNSPECIFIED.
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 98f52cb..6df5457 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -158,7 +158,7 @@
                 ApplicationInfo appInfo;
                 try {
                     appInfo = pm.getApplicationInfoAsUser(mPackageName, 0,
-                            UserHandle.getUserId(mUid));
+                            UserHandle.getUserHandleForUid(mUid));
                 } catch (NameNotFoundException e) {
                     return null;
                 }
@@ -300,7 +300,7 @@
                 Uri.fromParts(SCHEME_SMSTO, "", null));
         List<ResolveInfo> respondServices = packageManager.queryIntentServicesAsUser(intent,
                 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                userId);
+                UserHandle.getUserHandleForUid(userId));
         for (ResolveInfo resolveInfo : respondServices) {
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
             if (serviceInfo == null) {
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 72a6c9d..67103bf 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -367,7 +367,7 @@
         ApplicationInfo callingPackageInfo = null;
         try {
             callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
-                    callingPackage, 0, UserHandle.getUserId(uid));
+                    callingPackage, 0, UserHandle.getUserHandleForUid(uid));
             if (callingPackageInfo != null) {
                 if (callingPackageInfo.isSystemApp()) {
                     isPreinstalled = true;