Merge "Compare installer against rule providers"
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
index 02a79a1..d07ef4b 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
@@ -25,6 +25,7 @@
 import com.google.android.icing.proto.DocumentProto;
 import com.google.android.icing.proto.PropertyProto;
 import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.StatusProto;
 
 import java.util.Locale;
 import java.util.Map;
@@ -97,10 +98,12 @@
     public SearchResultProto query(@NonNull String term) {
         String normTerm = normalizeString(term);
         Set<Integer> docIds = mIndex.get(normTerm);
+        SearchResultProto.Builder results = SearchResultProto.newBuilder()
+                .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK));
         if (docIds == null || docIds.isEmpty()) {
-            return SearchResultProto.getDefaultInstance();
+            return results.build();
         }
-        SearchResultProto.Builder results = SearchResultProto.newBuilder();
+
         for (int docId : docIds) {
             DocumentProto document = mDocStore.get(docId);
             if (document != null) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index ef1351e..b96161a 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -48,6 +48,13 @@
     public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
     /** @hide */
     public static final int REASON_DEVICE_THERMAL = JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5.
+    /**
+     * The job is in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
+     * bucket.
+     *
+     * @hide
+     */
+    public static final int REASON_RESTRAINED = JobProtoEnums.STOP_REASON_RESTRAINED; // 6.
 
     /**
      * All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -65,6 +72,7 @@
             REASON_TIMEOUT,
             REASON_DEVICE_IDLE,
             REASON_DEVICE_THERMAL,
+            REASON_RESTRAINED,
     };
 
     /**
@@ -80,6 +88,7 @@
             case REASON_TIMEOUT: return "timeout";
             case REASON_DEVICE_IDLE: return "device_idle";
             case REASON_DEVICE_THERMAL: return "thermal";
+            case REASON_RESTRAINED: return "restrained";
             default: return "unknown:" + reason;
         }
     }
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 6109b71..d2d942a 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -103,6 +103,8 @@
     /**
      * Changes an app's standby bucket to the provided value. The caller can only set the standby
      * bucket for a different app than itself.
+     * If attempting to automatically place an app in the RESTRICTED bucket, use
+     * {@link #restrictApp(String, int, int)} instead.
      */
     void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, int callingUid,
             int callingPid);
@@ -113,6 +115,17 @@
     void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId, int callingUid,
             int callingPid);
 
+    /**
+     * Put the specified app in the
+     * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
+     * bucket. If it has been used by the user recently, the restriction will delayed until an
+     * appropriate time.
+     *
+     * @param restrictReason The restrictReason for restricting the app. Should be one of the
+     *                       UsageStatsManager.REASON_SUB_RESTRICT_* reasons.
+     */
+    void restrictApp(@NonNull String packageName, int userId, int restrictReason);
+
     void addActiveDeviceAdmin(String adminPkg, int userId);
 
     void setActiveAdminApps(Set<String> adminPkgs, int userId);
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 102e848..ed5626a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -101,6 +101,7 @@
 import com.android.server.job.controllers.IdleController;
 import com.android.server.job.controllers.JobStatus;
 import com.android.server.job.controllers.QuotaController;
+import com.android.server.job.controllers.RestrictingController;
 import com.android.server.job.controllers.StateController;
 import com.android.server.job.controllers.StorageController;
 import com.android.server.job.controllers.TimeController;
@@ -241,6 +242,11 @@
 
     /** List of controllers that will notify this service of updates to jobs. */
     final List<StateController> mControllers;
+    /**
+     * List of controllers that will apply to all jobs in the RESTRICTED bucket. This is a subset of
+     * {@link #mControllers}.
+     */
+    private final List<RestrictingController> mRestrictiveControllers;
     /** Need direct access to this for testing. */
     private final BatteryController mBatteryController;
     /** Need direct access to this for testing. */
@@ -277,6 +283,7 @@
     DeviceIdleInternal mLocalDeviceIdleController;
     AppStateTracker mAppStateTracker;
     final UsageStatsManagerInternal mUsageStats;
+    private final AppStandbyInternal mAppStandbyInternal;
 
     /**
      * Set to true once we are allowed to run third party apps.
@@ -312,6 +319,9 @@
     public static final int FREQUENT_INDEX = 2;
     public static final int RARE_INDEX = 3;
     public static final int NEVER_INDEX = 4;
+    // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping
+    // (ScheduledJobStateChanged and JobStatusDumpProto).
+    public static final int RESTRICTED_INDEX = 5;
 
     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
 
@@ -1062,7 +1072,8 @@
                     packageName == null ? job.getService().getPackageName() : packageName;
             if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
                 Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
-                // TODO(b/145551233): attempt to restrict app
+                mAppStandbyInternal.restrictApp(
+                        pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY);
                 if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION
                         && mPlatformCompat.isChangeEnabledByPackageName(
                                 CRASH_ON_EXCEEDED_LIMIT, pkg, userId)) {
@@ -1365,6 +1376,40 @@
         }
     }
 
+    @Override
+    public void onRestrictedBucketChanged(List<JobStatus> jobs) {
+        final int len = jobs.size();
+        if (len == 0) {
+            Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs");
+            return;
+        }
+        synchronized (mLock) {
+            for (int i = 0; i < len; ++i) {
+                JobStatus js = jobs.get(i);
+                for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) {
+                    // Effective standby bucket can change after this in some situations so use
+                    // the real bucket so that the job is tracked by the controllers.
+                    if (js.getStandbyBucket() == RESTRICTED_INDEX) {
+                        js.addDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
+                        js.addDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
+                        js.addDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
+                        js.addDynamicConstraint(JobStatus.CONSTRAINT_IDLE);
+
+                        mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js);
+                    } else {
+                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
+                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
+                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
+                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_IDLE);
+
+                        mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js);
+                    }
+                }
+            }
+        }
+        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+    }
+
     void reportActiveLocked() {
         // active is true if pending queue contains jobs OR some job is running.
         boolean active = mPendingJobs.size() > 0;
@@ -1430,8 +1475,8 @@
                 mConstants.API_QUOTA_SCHEDULE_COUNT,
                 mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
 
-        AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
-        appStandby.addListener(mStandbyTracker);
+        mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
+        mAppStandbyInternal.addListener(mStandbyTracker);
 
         // The job store needs to call back
         publishLocalService(JobSchedulerInternal.class, new LocalService());
@@ -1441,9 +1486,11 @@
 
         // Create the controllers.
         mControllers = new ArrayList<StateController>();
-        mControllers.add(new ConnectivityController(this));
+        final ConnectivityController connectivityController = new ConnectivityController(this);
+        mControllers.add(connectivityController);
         mControllers.add(new TimeController(this));
-        mControllers.add(new IdleController(this));
+        final IdleController idleController = new IdleController(this);
+        mControllers.add(idleController);
         mBatteryController = new BatteryController(this);
         mControllers.add(mBatteryController);
         mStorageController = new StorageController(this);
@@ -1455,6 +1502,11 @@
         mQuotaController = new QuotaController(this);
         mControllers.add(mQuotaController);
 
+        mRestrictiveControllers = new ArrayList<>();
+        mRestrictiveControllers.add(mBatteryController);
+        mRestrictiveControllers.add(connectivityController);
+        mRestrictiveControllers.add(idleController);
+
         // Create restrictions
         mJobRestrictions = new ArrayList<>();
         mJobRestrictions.add(new ThermalStatusRestriction(this));
@@ -2125,11 +2177,13 @@
                     }
                 } catch (RemoteException e) {
                 }
-                if (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
+                // Restricted jobs must always be batched
+                if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+                        || (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
                         && job.getEffectiveStandbyBucket() != ACTIVE_INDEX
                         && (job.getFirstForceBatchedTimeElapsed() == 0
                         || sElapsedRealtimeClock.millis() - job.getFirstForceBatchedTimeElapsed()
-                                < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS)) {
+                                < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS))) {
                     // Force batching non-ACTIVE jobs. Don't include them in the other counts.
                     forceBatchedCount++;
                     if (job.getFirstForceBatchedTimeElapsed() == 0) {
@@ -2536,11 +2590,19 @@
 
     public static int standbyBucketToBucketIndex(int bucket) {
         // Normalize AppStandby constants to indices into our bookkeeping
-        if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
-        else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
-        else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
-        else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
-        else return ACTIVE_INDEX;
+        if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) {
+            return NEVER_INDEX;
+        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) {
+            return RESTRICTED_INDEX;
+        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) {
+            return RARE_INDEX;
+        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
+            return FREQUENT_INDEX;
+        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
+            return WORKING_INDEX;
+        } else {
+            return ACTIVE_INDEX;
+        }
     }
 
     // Static to support external callers
diff --git a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
index 87bfc27..cb3c437 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
@@ -16,8 +16,12 @@
 
 package com.android.server.job;
 
+import android.annotation.NonNull;
+
 import com.android.server.job.controllers.JobStatus;
 
+import java.util.List;
+
 /**
  * Interface through which a {@link com.android.server.job.controllers.StateController} informs
  * the {@link com.android.server.job.JobSchedulerService} that there are some tasks potentially
@@ -39,4 +43,10 @@
     public void onRunJobNow(JobStatus jobStatus);
 
     public void onDeviceIdleStateChanged(boolean deviceIdle);
+
+    /**
+     * Called when these jobs are added or removed from the
+     * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+     */
+    void onRestrictedBucketChanged(@NonNull List<JobStatus> jobs);
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 46658ad..461ef21 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -43,7 +43,7 @@
  * be charging when it's been plugged in for more than two minutes, and the system has broadcast
  * ACTION_BATTERY_OK.
  */
-public final class BatteryController extends StateController {
+public final class BatteryController extends RestrictingController {
     private static final String TAG = "JobScheduler.Battery";
     private static final boolean DEBUG = JobSchedulerService.DEBUG
             || Log.isLoggable(TAG, Log.DEBUG);
@@ -73,12 +73,24 @@
     }
 
     @Override
+    public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
+        maybeStartTrackingJobLocked(jobStatus, null);
+    }
+
+    @Override
     public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
         if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
             mTrackedTasks.remove(taskStatus);
         }
     }
 
+    @Override
+    public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
+        if (!jobStatus.hasPowerConstraint()) {
+            maybeStopTrackingJobLocked(jobStatus, null, false);
+        }
+    }
+
     private void maybeReportNewChargingStateLocked() {
         final boolean stablePower = mChargeTracker.isOnStablePower();
         final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
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 8eeea1b..a0e83da 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
@@ -20,6 +20,8 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+
 import android.app.job.JobInfo;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
@@ -63,7 +65,7 @@
  *
  * Test: atest com.android.server.job.controllers.ConnectivityControllerTest
  */
-public final class ConnectivityController extends StateController implements
+public final class ConnectivityController extends RestrictingController implements
         ConnectivityManager.OnNetworkActiveListener {
     private static final String TAG = "JobScheduler.Connectivity";
     private static final boolean DEBUG = JobSchedulerService.DEBUG
@@ -138,8 +140,22 @@
         }
     }
 
+    @Override
+    public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
+        // Don't need to start tracking the job. If the job needed network, it would already be
+        // tracked.
+        updateConstraintsSatisfied(jobStatus);
+    }
+
+    @Override
+    public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
+        // Shouldn't stop tracking the job here. If the job was tracked, it still needs network,
+        // even after being unrestricted.
+        updateConstraintsSatisfied(jobStatus);
+    }
+
     /**
-     * Returns true if the job's requested network is available. This DOES NOT necesarilly mean
+     * Returns true if the job's requested network is available. This DOES NOT necessarily mean
      * that the UID has been granted access to the network.
      */
     public boolean isNetworkAvailable(JobStatus job) {
@@ -353,14 +369,24 @@
 
     private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
-        return jobStatus.getJob().getRequiredNetwork().networkCapabilities
-                .satisfiedByNetworkCapabilities(capabilities);
+        final NetworkCapabilities required;
+        // A restricted job that's out of quota MUST use an unmetered network.
+        if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+                && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
+            required = new NetworkCapabilities(
+                    jobStatus.getJob().getRequiredNetwork().networkCapabilities)
+                    .addCapability(NET_CAPABILITY_NOT_METERED);
+        } else {
+            required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+        }
+
+        return required.satisfiedByNetworkCapabilities(capabilities);
     }
 
     private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
-        // Only consider doing this for prefetching jobs
-        if (!jobStatus.getJob().isPrefetch()) {
+        // Only consider doing this for unrestricted prefetching jobs
+        if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) {
             return false;
         }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index d355715..c0b3204 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -32,7 +32,15 @@
 
 import java.util.function.Predicate;
 
-public final class IdleController extends StateController implements IdlenessListener {
+/**
+ * Simple controller that tracks whether the device is idle or not. Idleness depends on the device
+ * type and is not related to device-idle (Doze mode) despite the similar naming.
+ *
+ * @see CarIdlenessTracker
+ * @see DeviceIdlenessTracker
+ * @see IdlenessTracker
+ */
+public final class IdleController extends RestrictingController implements IdlenessListener {
     private static final String TAG = "JobScheduler.IdleController";
     // Policy: we decide that we're "idle" if the device has been unused /
     // screen off or dreaming or wireless charging dock idle for at least this long
@@ -57,6 +65,11 @@
     }
 
     @Override
+    public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
+        maybeStartTrackingJobLocked(jobStatus, null);
+    }
+
+    @Override
     public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
             boolean forUpdate) {
         if (taskStatus.clearTrackingController(JobStatus.TRACKING_IDLE)) {
@@ -64,6 +77,13 @@
         }
     }
 
+    @Override
+    public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
+        if (!jobStatus.hasIdleConstraint()) {
+            maybeStopTrackingJobLocked(jobStatus, null, false);
+        }
+    }
+
     /**
      * State-change notifications from the idleness tracker
      */
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 a8d8bd9..dbdce70 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
@@ -69,13 +69,14 @@
     public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
     public static final long NO_EARLIEST_RUNTIME = 0L;
 
-    static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
-    static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;  // 1 << 2
-    static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
+    public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
+    public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;  // 1 << 2
+    public static final int CONSTRAINT_BATTERY_NOT_LOW =
+            JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
     static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
     static final int CONSTRAINT_TIMING_DELAY = 1<<31;
     static final int CONSTRAINT_DEADLINE = 1<<30;
-    static final int CONSTRAINT_CONNECTIVITY = 1<<28;
+    public static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
     static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
@@ -117,7 +118,7 @@
     /** The minimum possible update delay is 1/2 second. */
     public static final long MIN_TRIGGER_UPDATE_DELAY = 500;
 
-    /** If not specified, trigger maxumum delay is 2 minutes. */
+    /** If not specified, trigger maximum delay is 2 minutes. */
     public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000;
 
     /** The minimum possible update delay is 1 second. */
@@ -188,6 +189,11 @@
     private final int mRequiredConstraintsOfInterest;
     int satisfiedConstraints = 0;
     private int mSatisfiedConstraintsOfInterest = 0;
+    /**
+     * Set of constraints that must be satisfied for the job if/because it's in the RESTRICTED
+     * bucket.
+     */
+    private int mDynamicConstraints = 0;
 
     // Set to true if doze constraint was satisfied due to app being whitelisted.
     public boolean dozeWhitelisted;
@@ -328,6 +334,9 @@
     /** The job is within its quota based on its standby bucket. */
     private boolean mReadyWithinQuota;
 
+    /** The job's dynamic requirements have been satisfied. */
+    private boolean mReadyDynamicSatisfied;
+
     /** Provide a handle to the service that this job will be run on. */
     public int getServiceToken() {
         return callingUid;
@@ -410,6 +419,7 @@
         this.requiredConstraints = requiredConstraints;
         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
         mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+        mReadyDynamicSatisfied = true;
 
         mLastSuccessfulRunTime = lastSuccessfulRunTime;
         mLastFailedRunTime = lastFailedRunTime;
@@ -830,41 +840,54 @@
 
     /** Does this job have any sort of networking constraint? */
     public boolean hasConnectivityConstraint() {
+        // No need to check mDynamicConstraints since connectivity will only be in that list if
+        // it's already in the requiredConstraints list.
         return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
     }
 
     public boolean hasChargingConstraint() {
-        return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
+        return hasConstraint(CONSTRAINT_CHARGING);
     }
 
     public boolean hasBatteryNotLowConstraint() {
-        return (requiredConstraints&CONSTRAINT_BATTERY_NOT_LOW) != 0;
+        return hasConstraint(CONSTRAINT_BATTERY_NOT_LOW);
     }
 
-    public boolean hasPowerConstraint() {
-        return (requiredConstraints&(CONSTRAINT_CHARGING|CONSTRAINT_BATTERY_NOT_LOW)) != 0;
+    /** Returns true if the job requires charging OR battery not low. */
+    boolean hasPowerConstraint() {
+        return hasConstraint(CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW);
     }
 
     public boolean hasStorageNotLowConstraint() {
-        return (requiredConstraints&CONSTRAINT_STORAGE_NOT_LOW) != 0;
+        return hasConstraint(CONSTRAINT_STORAGE_NOT_LOW);
     }
 
     public boolean hasTimingDelayConstraint() {
-        return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0;
+        return hasConstraint(CONSTRAINT_TIMING_DELAY);
     }
 
     public boolean hasDeadlineConstraint() {
-        return (requiredConstraints&CONSTRAINT_DEADLINE) != 0;
+        return hasConstraint(CONSTRAINT_DEADLINE);
     }
 
     public boolean hasIdleConstraint() {
-        return (requiredConstraints&CONSTRAINT_IDLE) != 0;
+        return hasConstraint(CONSTRAINT_IDLE);
     }
 
     public boolean hasContentTriggerConstraint() {
+        // No need to check mDynamicConstraints since content trigger will only be in that list if
+        // it's already in the requiredConstraints list.
         return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
     }
 
+    /**
+     * Checks both {@link #requiredConstraints} and {@link #mDynamicConstraints} to see if this job
+     * requires the specified constraint.
+     */
+    private boolean hasConstraint(int constraint) {
+        return (requiredConstraints & constraint) != 0 || (mDynamicConstraints & constraint) != 0;
+    }
+
     public long getTriggerContentUpdateDelay() {
         long time = job.getTriggerContentUpdateDelay();
         if (time < 0) {
@@ -1033,6 +1056,8 @@
         }
         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
         mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+        mReadyDynamicSatisfied =
+                mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
         if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) {
             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED,
                     sourceUid, null, getBatteryName(), getProtoConstraint(constraint),
@@ -1058,6 +1083,43 @@
         trackingControllers |= which;
     }
 
+    /**
+     * Indicates that this job cannot run without the specified constraint. This is evaluated
+     * separately from the job's explicitly requested constraints and MUST be satisfied before
+     * the job can run if the app doesn't have quota.
+     *
+     */
+    public void addDynamicConstraint(int constraint) {
+        if (constraint == CONSTRAINT_WITHIN_QUOTA) {
+            Slog.wtf(TAG, "Tried to set quota as a dynamic constraint");
+            return;
+        }
+
+        // Connectivity and content trigger are special since they're only valid to add if the
+        // job has requested network or specific content URIs. Adding these constraints to jobs
+        // that don't need them doesn't make sense.
+        if ((constraint == CONSTRAINT_CONNECTIVITY && !hasConnectivityConstraint())
+                || (constraint == CONSTRAINT_CONTENT_TRIGGER && !hasContentTriggerConstraint())) {
+            return;
+        }
+
+        mDynamicConstraints |= constraint;
+        mReadyDynamicSatisfied =
+                mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
+    }
+
+    /**
+     * Removes a dynamic constraint from a job, meaning that the requirement is not required for
+     * the job to run (if the job itself hasn't requested the constraint. This is separate from
+     * the job's explicitly requested constraints and does not remove those requested constraints.
+     *
+     */
+    public void removeDynamicConstraint(int constraint) {
+        mDynamicConstraints &= ~constraint;
+        mReadyDynamicSatisfied =
+                mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
+    }
+
     public long getLastSuccessfulRunTime() {
         return mLastSuccessfulRunTime;
     }
@@ -1099,6 +1161,8 @@
                 break;
             default:
                 satisfied |= constraint;
+                mReadyDynamicSatisfied =
+                        mDynamicConstraints == (satisfied & mDynamicConstraints);
                 break;
         }
 
@@ -1117,24 +1181,29 @@
             case CONSTRAINT_WITHIN_QUOTA:
                 mReadyWithinQuota = oldValue;
                 break;
+            default:
+                mReadyDynamicSatisfied =
+                        mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
+                break;
         }
         return toReturn;
     }
 
     private boolean isReady(int satisfiedConstraints) {
-        // Quota constraints trumps all other constraints.
-        if (!mReadyWithinQuota) {
+        // Quota and dynamic constraints trump all other constraints.
+        if (!mReadyWithinQuota && !mReadyDynamicSatisfied) {
             return false;
         }
-        // Deadline constraint trumps other constraints besides quota (except for periodic jobs
-        // where deadline is an implementation detail. A periodic job should only run if its
-        // constraints are satisfied).
+        // Deadline constraint trumps other constraints besides quota and dynamic (except for
+        // periodic jobs where deadline is an implementation detail. A periodic job should only
+        // run if its constraints are satisfied).
         // DeviceNotDozing implicit constraint must be satisfied
         // NotRestrictedInBackground implicit constraint must be satisfied
         return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
                 || isConstraintsSatisfied(satisfiedConstraints));
     }
 
+    /** All constraints besides implicit and deadline. */
     static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
             | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY
             | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
@@ -1441,6 +1510,8 @@
             case 2: return "FREQUENT";
             case 3: return "RARE";
             case 4: return "NEVER";
+            case 5:
+                return "RESTRICTED";
             default:
                 return "Unknown: " + standbyBucket;
         }
@@ -1560,6 +1631,10 @@
         pw.print(prefix); pw.print("Required constraints:");
         dumpConstraints(pw, requiredConstraints);
         pw.println();
+        pw.print(prefix);
+        pw.print("Dynamic constraints:");
+        dumpConstraints(pw, mDynamicConstraints);
+        pw.println();
         if (full) {
             pw.print(prefix); pw.print("Satisfied constraints:");
             dumpConstraints(pw, satisfiedConstraints);
@@ -1599,6 +1674,9 @@
             pw.print(prefix); pw.print("  readyDeadlineSatisfied: ");
             pw.println(mReadyDeadlineSatisfied);
         }
+        pw.print(prefix);
+        pw.print("  readyDynamicSatisfied: ");
+        pw.println(mReadyDynamicSatisfied);
 
         if (changedAuthorities != null) {
             pw.print(prefix); pw.println("Changed authorities:");
@@ -1760,6 +1838,7 @@
         }
 
         dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints);
+        dumpConstraints(proto, JobStatusDumpProto.DYNAMIC_CONSTRAINTS, mDynamicConstraints);
         if (full) {
             dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
             dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
@@ -1807,6 +1886,8 @@
                 mReadyNotRestrictedInBg);
         // mReadyDeadlineSatisfied isn't an implicit constraint...and can be determined from other
         // field values.
+        proto.write(JobStatusDumpProto.ImplicitConstraints.IS_DYNAMIC_SATISFIED,
+                mReadyDynamicSatisfied);
         proto.end(icToken);
 
         if (changedAuthorities != null) {
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 2e735a4..8eefac8 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
@@ -24,6 +24,7 @@
 import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
 import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
@@ -424,7 +425,9 @@
             QcConstants.DEFAULT_WINDOW_SIZE_ACTIVE_MS,
             QcConstants.DEFAULT_WINDOW_SIZE_WORKING_MS,
             QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS,
-            QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS
+            QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS,
+            0, // NEVER
+            QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS
     };
 
     /** The maximum period any bucket can have. */
@@ -441,7 +444,9 @@
             QcConstants.DEFAULT_MAX_JOB_COUNT_ACTIVE,
             QcConstants.DEFAULT_MAX_JOB_COUNT_WORKING,
             QcConstants.DEFAULT_MAX_JOB_COUNT_FREQUENT,
-            QcConstants.DEFAULT_MAX_JOB_COUNT_RARE
+            QcConstants.DEFAULT_MAX_JOB_COUNT_RARE,
+            0, // NEVER
+            QcConstants.DEFAULT_MAX_JOB_COUNT_RESTRICTED
     };
 
     /**
@@ -455,7 +460,9 @@
             QcConstants.DEFAULT_MAX_SESSION_COUNT_ACTIVE,
             QcConstants.DEFAULT_MAX_SESSION_COUNT_WORKING,
             QcConstants.DEFAULT_MAX_SESSION_COUNT_FREQUENT,
-            QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE
+            QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE,
+            0, // NEVER
+            QcConstants.DEFAULT_MAX_SESSION_COUNT_RESTRICTED,
     };
 
     /**
@@ -648,7 +655,11 @@
 
         // Quota constraint is not enforced while charging.
         if (mChargeTracker.isCharging()) {
-            return true;
+            // Restricted jobs require additional constraints when charging, so don't immediately
+            // mark quota as free when charging.
+            if (standbyBucket != RESTRICTED_INDEX) {
+                return true;
+            }
         }
 
         ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
@@ -1105,14 +1116,37 @@
         }
     }
 
+    private class TimerChargingUpdateFunctor implements Consumer<Timer> {
+        private long mNowElapsed;
+        private boolean mIsCharging;
+
+        private void setStatus(long nowElapsed, boolean isCharging) {
+            mNowElapsed = nowElapsed;
+            mIsCharging = isCharging;
+        }
+
+        @Override
+        public void accept(Timer timer) {
+            if (JobSchedulerService.standbyBucketForPackage(timer.mPkg.packageName,
+                    timer.mPkg.userId, mNowElapsed) != RESTRICTED_INDEX) {
+                // Restricted jobs need additional constraints even when charging, so don't
+                // immediately say that quota is free.
+                timer.onStateChangedLocked(mNowElapsed, mIsCharging);
+            }
+        }
+    }
+
+    private final TimerChargingUpdateFunctor
+            mTimerChargingUpdateFunctor = new TimerChargingUpdateFunctor();
+
     private void handleNewChargingStateLocked() {
-        final long nowElapsed = sElapsedRealtimeClock.millis();
-        final boolean isCharging = mChargeTracker.isCharging();
+        mTimerChargingUpdateFunctor.setStatus(sElapsedRealtimeClock.millis(),
+                mChargeTracker.isCharging());
         if (DEBUG) {
-            Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging);
+            Slog.d(TAG, "handleNewChargingStateLocked: " + mChargeTracker.isCharging());
         }
         // Deal with Timers first.
-        mPkgTimers.forEach((t) -> t.onStateChangedLocked(nowElapsed, isCharging));
+        mPkgTimers.forEach(mTimerChargingUpdateFunctor);
         // Now update jobs.
         maybeUpdateAllConstraintsLocked();
     }
@@ -1555,7 +1589,10 @@
         }
 
         private boolean shouldTrackLocked() {
-            return !mChargeTracker.isCharging() && !mForegroundUids.get(mUid);
+            final int standbyBucket = JobSchedulerService.standbyBucketForPackage(mPkg.packageName,
+                    mPkg.userId, sElapsedRealtimeClock.millis());
+            return (standbyBucket == RESTRICTED_INDEX || !mChargeTracker.isCharging())
+                    && !mForegroundUids.get(mUid);
         }
 
         void onStateChangedLocked(long nowElapsed, boolean isQuotaFree) {
@@ -1670,6 +1707,7 @@
                     Slog.i(TAG, "Moving pkg " + string(userId, packageName) + " to bucketIndex "
                             + bucketIndex);
                 }
+                List<JobStatus> restrictedChanges = new ArrayList<>();
                 synchronized (mLock) {
                     ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
                     if (jobs == null || jobs.size() == 0) {
@@ -1677,6 +1715,13 @@
                     }
                     for (int i = jobs.size() - 1; i >= 0; i--) {
                         JobStatus js = jobs.valueAt(i);
+                        // Effective standby bucket can change after this in some situations so
+                        // use the real bucket so that the job is tracked by the controllers.
+                        if ((bucketIndex == RESTRICTED_INDEX
+                                || js.getStandbyBucket() == RESTRICTED_INDEX)
+                                && bucketIndex != js.getStandbyBucket()) {
+                            restrictedChanges.add(js);
+                        }
                         js.setStandbyBucket(bucketIndex);
                     }
                     Timer timer = mPkgTimers.get(userId, packageName);
@@ -1687,6 +1732,9 @@
                         mStateChangedListener.onControllerStateChanged();
                     }
                 }
+                if (restrictedChanges.size() > 0) {
+                    mStateChangedListener.onRestrictedBucketChanged(restrictedChanges);
+                }
             });
         }
     }
@@ -1863,11 +1911,13 @@
         private static final String KEY_WINDOW_SIZE_WORKING_MS = "window_size_working_ms";
         private static final String KEY_WINDOW_SIZE_FREQUENT_MS = "window_size_frequent_ms";
         private static final String KEY_WINDOW_SIZE_RARE_MS = "window_size_rare_ms";
+        private static final String KEY_WINDOW_SIZE_RESTRICTED_MS = "window_size_restricted_ms";
         private static final String KEY_MAX_EXECUTION_TIME_MS = "max_execution_time_ms";
         private static final String KEY_MAX_JOB_COUNT_ACTIVE = "max_job_count_active";
         private static final String KEY_MAX_JOB_COUNT_WORKING = "max_job_count_working";
         private static final String KEY_MAX_JOB_COUNT_FREQUENT = "max_job_count_frequent";
         private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
+        private static final String KEY_MAX_JOB_COUNT_RESTRICTED = "max_job_count_restricted";
         private static final String KEY_RATE_LIMITING_WINDOW_MS = "rate_limiting_window_ms";
         private static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
                 "max_job_count_per_rate_limiting_window";
@@ -1875,6 +1925,8 @@
         private static final String KEY_MAX_SESSION_COUNT_WORKING = "max_session_count_working";
         private static final String KEY_MAX_SESSION_COUNT_FREQUENT = "max_session_count_frequent";
         private static final String KEY_MAX_SESSION_COUNT_RARE = "max_session_count_rare";
+        private static final String KEY_MAX_SESSION_COUNT_RESTRICTED =
+                "max_session_count_restricted";
         private static final String KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
                 "max_session_count_per_rate_limiting_window";
         private static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS =
@@ -1892,6 +1944,8 @@
                 8 * 60 * 60 * 1000L; // 8 hours
         private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
                 24 * 60 * 60 * 1000L; // 24 hours
+        private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
+                24 * 60 * 60 * 1000L; // 24 hours
         private static final long DEFAULT_MAX_EXECUTION_TIME_MS =
                 4 * HOUR_IN_MILLIS;
         private static final long DEFAULT_RATE_LIMITING_WINDOW_MS =
@@ -1905,6 +1959,7 @@
                 (int) (25.0 * DEFAULT_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS);
         private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
                 (int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
+        private static final int DEFAULT_MAX_JOB_COUNT_RESTRICTED = 10;
         private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
                 75; // 450/hr
         private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
@@ -1913,6 +1968,7 @@
                 8; // 1/hr
         private static final int DEFAULT_MAX_SESSION_COUNT_RARE =
                 3; // .125/hr
+        private static final int DEFAULT_MAX_SESSION_COUNT_RESTRICTED = 1; // 1/day
         private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
         private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
 
@@ -1954,6 +2010,13 @@
         public long WINDOW_SIZE_RARE_MS = DEFAULT_WINDOW_SIZE_RARE_MS;
 
         /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long WINDOW_SIZE_RESTRICTED_MS = DEFAULT_WINDOW_SIZE_RESTRICTED_MS;
+
+        /**
          * The maximum amount of time an app can have its jobs running within a 24 hour window.
          */
         public long MAX_EXECUTION_TIME_MS = DEFAULT_MAX_EXECUTION_TIME_MS;
@@ -1982,6 +2045,12 @@
          */
         public int MAX_JOB_COUNT_RARE = DEFAULT_MAX_JOB_COUNT_RARE;
 
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int MAX_JOB_COUNT_RESTRICTED = DEFAULT_MAX_JOB_COUNT_RESTRICTED;
+
         /** The period of time used to rate limit recently run jobs. */
         public long RATE_LIMITING_WINDOW_MS = DEFAULT_RATE_LIMITING_WINDOW_MS;
 
@@ -2016,6 +2085,12 @@
         public int MAX_SESSION_COUNT_RARE = DEFAULT_MAX_SESSION_COUNT_RARE;
 
         /**
+         * The maximum number of {@link TimingSession}s an app can run within this particular
+         * standby bucket's window size.
+         */
+        public int MAX_SESSION_COUNT_RESTRICTED = DEFAULT_MAX_SESSION_COUNT_RESTRICTED;
+
+        /**
          * The maximum number of {@link TimingSession}s that can run within the past
          * {@link #ALLOWED_TIME_PER_PERIOD_MS}.
          */
@@ -2087,6 +2162,8 @@
                     KEY_WINDOW_SIZE_FREQUENT_MS, DEFAULT_WINDOW_SIZE_FREQUENT_MS);
             WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
                     KEY_WINDOW_SIZE_RARE_MS, DEFAULT_WINDOW_SIZE_RARE_MS);
+            WINDOW_SIZE_RESTRICTED_MS = mParser.getDurationMillis(
+                    KEY_WINDOW_SIZE_RESTRICTED_MS, DEFAULT_WINDOW_SIZE_RESTRICTED_MS);
             MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
                     KEY_MAX_EXECUTION_TIME_MS, DEFAULT_MAX_EXECUTION_TIME_MS);
             MAX_JOB_COUNT_ACTIVE = mParser.getInt(
@@ -2097,6 +2174,8 @@
                     KEY_MAX_JOB_COUNT_FREQUENT, DEFAULT_MAX_JOB_COUNT_FREQUENT);
             MAX_JOB_COUNT_RARE = mParser.getInt(
                     KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
+            MAX_JOB_COUNT_RESTRICTED = mParser.getInt(
+                    KEY_MAX_JOB_COUNT_RESTRICTED, DEFAULT_MAX_JOB_COUNT_RESTRICTED);
             RATE_LIMITING_WINDOW_MS = mParser.getLong(
                     KEY_RATE_LIMITING_WINDOW_MS, DEFAULT_RATE_LIMITING_WINDOW_MS);
             MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
@@ -2110,6 +2189,8 @@
                     KEY_MAX_SESSION_COUNT_FREQUENT, DEFAULT_MAX_SESSION_COUNT_FREQUENT);
             MAX_SESSION_COUNT_RARE = mParser.getInt(
                     KEY_MAX_SESSION_COUNT_RARE, DEFAULT_MAX_SESSION_COUNT_RARE);
+            MAX_SESSION_COUNT_RESTRICTED = mParser.getInt(
+                    KEY_MAX_SESSION_COUNT_RESTRICTED, DEFAULT_MAX_SESSION_COUNT_RESTRICTED);
             MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
                     KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
                     DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
@@ -2173,6 +2254,13 @@
                     mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
                     changed = true;
                 }
+                // Fit in the range [allowed time (10 mins), 1 week].
+                long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                        Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS));
+                if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) {
+                    mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
+                    changed = true;
+                }
                 long newRateLimitingWindowMs = Math.min(MAX_PERIOD_MS,
                         Math.max(MIN_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS));
                 if (mRateLimitingWindowMs != newRateLimitingWindowMs) {
@@ -2206,6 +2294,12 @@
                     mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
                     changed = true;
                 }
+                int newRestrictedMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT,
+                        MAX_JOB_COUNT_RESTRICTED);
+                if (mMaxBucketJobCounts[RESTRICTED_INDEX] != newRestrictedMaxJobCount) {
+                    mMaxBucketJobCounts[RESTRICTED_INDEX] = newRestrictedMaxJobCount;
+                    changed = true;
+                }
                 int newMaxSessionCountPerRateLimitPeriod = Math.max(
                         MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
                         MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
@@ -2237,6 +2331,11 @@
                     mMaxBucketSessionCounts[RARE_INDEX] = newRareMaxSessionCount;
                     changed = true;
                 }
+                int newRestrictedMaxSessionCount = Math.max(0, MAX_SESSION_COUNT_RESTRICTED);
+                if (mMaxBucketSessionCounts[RESTRICTED_INDEX] != newRestrictedMaxSessionCount) {
+                    mMaxBucketSessionCounts[RESTRICTED_INDEX] = newRestrictedMaxSessionCount;
+                    changed = true;
+                }
                 long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS,
                         Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS));
                 if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) {
@@ -2266,11 +2365,13 @@
             pw.printPair(KEY_WINDOW_SIZE_WORKING_MS, WINDOW_SIZE_WORKING_MS).println();
             pw.printPair(KEY_WINDOW_SIZE_FREQUENT_MS, WINDOW_SIZE_FREQUENT_MS).println();
             pw.printPair(KEY_WINDOW_SIZE_RARE_MS, WINDOW_SIZE_RARE_MS).println();
+            pw.printPair(KEY_WINDOW_SIZE_RESTRICTED_MS, WINDOW_SIZE_RESTRICTED_MS).println();
             pw.printPair(KEY_MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS).println();
             pw.printPair(KEY_MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE).println();
             pw.printPair(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println();
             pw.printPair(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println();
             pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).println();
+            pw.printPair(KEY_MAX_JOB_COUNT_RESTRICTED, MAX_JOB_COUNT_RESTRICTED).println();
             pw.printPair(KEY_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS).println();
             pw.printPair(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
                     MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW).println();
@@ -2278,6 +2379,7 @@
             pw.printPair(KEY_MAX_SESSION_COUNT_WORKING, MAX_SESSION_COUNT_WORKING).println();
             pw.printPair(KEY_MAX_SESSION_COUNT_FREQUENT, MAX_SESSION_COUNT_FREQUENT).println();
             pw.printPair(KEY_MAX_SESSION_COUNT_RARE, MAX_SESSION_COUNT_RARE).println();
+            pw.printPair(KEY_MAX_SESSION_COUNT_RESTRICTED, MAX_SESSION_COUNT_RESTRICTED).println();
             pw.printPair(KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
                     MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW).println();
             pw.printPair(KEY_TIMING_SESSION_COALESCING_DURATION_MS,
@@ -2297,6 +2399,8 @@
             proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
                     WINDOW_SIZE_FREQUENT_MS);
             proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS, WINDOW_SIZE_RARE_MS);
+            proto.write(ConstantsProto.QuotaController.RESTRICTED_WINDOW_SIZE_MS,
+                    WINDOW_SIZE_RESTRICTED_MS);
             proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
                     MAX_EXECUTION_TIME_MS);
             proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE);
@@ -2305,6 +2409,8 @@
             proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
                     MAX_JOB_COUNT_FREQUENT);
             proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RESTRICTED,
+                    MAX_JOB_COUNT_RESTRICTED);
             proto.write(ConstantsProto.QuotaController.RATE_LIMITING_WINDOW_MS,
                     RATE_LIMITING_WINDOW_MS);
             proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
@@ -2317,6 +2423,8 @@
                     MAX_SESSION_COUNT_FREQUENT);
             proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RARE,
                     MAX_SESSION_COUNT_RARE);
+            proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RESTRICTED,
+                    MAX_SESSION_COUNT_RESTRICTED);
             proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
                     MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
             proto.write(ConstantsProto.QuotaController.TIMING_SESSION_COALESCING_DURATION_MS,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java
new file mode 100644
index 0000000..5c637bb
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package com.android.server.job.controllers;
+
+import com.android.server.job.JobSchedulerService;
+
+/**
+ * Controller that can also handle jobs in the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+ */
+public abstract class RestrictingController extends StateController {
+    RestrictingController(JobSchedulerService service) {
+        super(service);
+    }
+
+    /**
+     * Start tracking a job that has been added to the
+     * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+     */
+    public abstract void startTrackingRestrictedJobLocked(JobStatus jobStatus);
+
+    /**
+     * Stop tracking a job that has been removed from the
+     * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+     */
+    public abstract void stopTrackingRestrictedJobLocked(JobStatus jobStatus);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index b9df30a..9d6e012 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -25,8 +25,11 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
+import static com.android.server.usage.AppStandbyController.isUserUsage;
+
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.UsageStatsManager;
 import android.os.SystemClock;
@@ -81,6 +84,8 @@
     private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
     // Elapsed timebase time when app was last used
     private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
+    // Elapsed timebase time when app was last used by the user
+    private static final String ATTR_LAST_USED_BY_USER_ELAPSED = "lastUsedByUserElapsedTime";
     // Elapsed timebase time when the app bucket was last predicted externally
     private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime";
     // The standby bucket for the app
@@ -93,6 +98,12 @@
     private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime";
     // The time when the forced working_set state can be overridden.
     private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime";
+    // Elapsed timebase time when the app was last marked for restriction.
+    private static final String ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED =
+            "lastRestrictionAttemptElapsedTime";
+    // Reason why the app was last marked for restriction.
+    private static final String ATTR_LAST_RESTRICTION_ATTEMPT_REASON =
+            "lastRestrictionAttemptReason";
 
     // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
     private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
@@ -107,8 +118,10 @@
     private boolean mScreenOn;
 
     static class AppUsageHistory {
-        // Last used time using elapsed timebase
+        // Last used time (including system usage), using elapsed timebase
         long lastUsedElapsedTime;
+        // Last time the user used the app, using elapsed timebase
+        long lastUsedByUserElapsedTime;
         // Last used time using screen_on timebase
         long lastUsedScreenTime;
         // Last predicted time using elapsed timebase
@@ -136,6 +149,10 @@
         // under any active state timeout, so that it becomes applicable after the active state
         // timeout expires.
         long bucketWorkingSetTimeoutTime;
+        // The last time an agent attempted to put the app into the RESTRICTED bucket.
+        long lastRestrictAttemptElapsedTime;
+        // The last reason the app was marked to be put into the RESTRICTED bucket.
+        int lastRestrictReason;
     }
 
     AppIdleHistory(File storageDir, long elapsedRealtime) {
@@ -229,25 +246,37 @@
      */
     public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
             int newBucket, int usageReason, long elapsedRealtime, long timeout) {
-        // Set the timeout if applicable
-        if (timeout > elapsedRealtime) {
-            // Convert to elapsed timebase
-            final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
-            if (newBucket == STANDBY_BUCKET_ACTIVE) {
-                appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
-                        appUsageHistory.bucketActiveTimeoutTime);
-            } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
-                appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
-                        appUsageHistory.bucketWorkingSetTimeoutTime);
-            } else {
-                throw new IllegalArgumentException("Cannot set a timeout on bucket=" +
-                        newBucket);
+        int bucketingReason = REASON_MAIN_USAGE | usageReason;
+        final boolean isUserUsage = isUserUsage(bucketingReason);
+
+        if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) {
+            // Only user usage should bring an app out of the RESTRICTED bucket.
+            newBucket = STANDBY_BUCKET_RESTRICTED;
+            bucketingReason = appUsageHistory.bucketingReason;
+        } else {
+            // Set the timeout if applicable
+            if (timeout > elapsedRealtime) {
+                // Convert to elapsed timebase
+                final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
+                if (newBucket == STANDBY_BUCKET_ACTIVE) {
+                    appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
+                            appUsageHistory.bucketActiveTimeoutTime);
+                } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
+                    appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
+                            appUsageHistory.bucketWorkingSetTimeoutTime);
+                } else {
+                    throw new IllegalArgumentException("Cannot set a timeout on bucket="
+                            + newBucket);
+                }
             }
         }
 
         if (elapsedRealtime != 0) {
             appUsageHistory.lastUsedElapsedTime = mElapsedDuration
                     + (elapsedRealtime - mElapsedSnapshot);
+            if (isUserUsage) {
+                appUsageHistory.lastUsedByUserElapsedTime = appUsageHistory.lastUsedElapsedTime;
+            }
             appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
         }
 
@@ -259,7 +288,7 @@
                         + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
             }
         }
-        appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
+        appUsageHistory.bucketingReason = bucketingReason;
 
         return appUsageHistory;
     }
@@ -386,6 +415,24 @@
     }
 
     /**
+     * Notes an attempt to put the app in the {@link UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
+     * bucket.
+     *
+     * @param packageName     The package name of the app that is being restricted
+     * @param userId          The ID of the user in which the app is being restricted
+     * @param elapsedRealtime The time the attempt was made, in the (unadjusted) elapsed realtime
+     *                        timebase
+     * @param reason          The reason for the restriction attempt
+     */
+    void noteRestrictionAttempt(String packageName, int userId, long elapsedRealtime, int reason) {
+        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+        AppUsageHistory appUsageHistory =
+                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+        appUsageHistory.lastRestrictAttemptElapsedTime = getElapsedTime(elapsedRealtime);
+        appUsageHistory.lastRestrictReason = reason;
+    }
+
+    /**
      * Returns the time since the last job was run for this app. This can be larger than the
      * current elapsedRealtime, in case it happened before boot or a really large value if no jobs
      * were ever run.
@@ -547,6 +594,9 @@
                         AppUsageHistory appUsageHistory = new AppUsageHistory();
                         appUsageHistory.lastUsedElapsedTime =
                                 Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
+                        appUsageHistory.lastUsedByUserElapsedTime = getLongValue(parser,
+                                ATTR_LAST_USED_BY_USER_ELAPSED,
+                                appUsageHistory.lastUsedElapsedTime);
                         appUsageHistory.lastUsedScreenTime =
                                 Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
                         appUsageHistory.lastPredictedTime = getLongValue(parser,
@@ -570,6 +620,19 @@
                                 appUsageHistory.bucketingReason =
                                         Integer.parseInt(bucketingReason, 16);
                             } catch (NumberFormatException nfe) {
+                                Slog.wtf(TAG, "Unable to read bucketing reason", nfe);
+                            }
+                        }
+                        appUsageHistory.lastRestrictAttemptElapsedTime =
+                                getLongValue(parser, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED, 0);
+                        String lastRestrictReason = parser.getAttributeValue(
+                                null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON);
+                        if (lastRestrictReason != null) {
+                            try {
+                                appUsageHistory.lastRestrictReason =
+                                        Integer.parseInt(lastRestrictReason, 16);
+                            } catch (NumberFormatException nfe) {
+                                Slog.wtf(TAG, "Unable to read last restrict reason", nfe);
                             }
                         }
                         appUsageHistory.lastInformedBucket = -1;
@@ -618,6 +681,8 @@
                 xml.attribute(null, ATTR_NAME, packageName);
                 xml.attribute(null, ATTR_ELAPSED_IDLE,
                         Long.toString(history.lastUsedElapsedTime));
+                xml.attribute(null, ATTR_LAST_USED_BY_USER_ELAPSED,
+                        Long.toString(history.lastUsedByUserElapsedTime));
                 xml.attribute(null, ATTR_SCREEN_IDLE,
                         Long.toString(history.lastUsedScreenTime));
                 xml.attribute(null, ATTR_LAST_PREDICTED_TIME,
@@ -638,6 +703,12 @@
                     xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
                             .lastJobRunTime));
                 }
+                if (history.lastRestrictAttemptElapsedTime > 0) {
+                    xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED,
+                            Long.toString(history.lastRestrictAttemptElapsedTime));
+                }
+                xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON,
+                        Integer.toHexString(history.lastRestrictReason));
                 xml.endTag(null, TAG_PACKAGE);
             }
 
@@ -672,6 +743,9 @@
                     + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
             idpw.print(" used=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
+            idpw.print(" usedByUser=");
+            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedByUserElapsedTime,
+                    idpw);
             idpw.print(" usedScr=");
             TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
             idpw.print(" lastPred=");
@@ -684,6 +758,13 @@
                     idpw);
             idpw.print(" lastJob=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
+            if (appUsageHistory.lastRestrictAttemptElapsedTime > 0) {
+                idpw.print(" lastRestrictAttempt=");
+                TimeUtils.formatDuration(
+                        totalElapsedTime - appUsageHistory.lastRestrictAttemptElapsedTime, idpw);
+                idpw.print(" lastRestrictReason="
+                        + UsageStatsManager.reasonToString(appUsageHistory.lastRestrictReason));
+            }
             idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
             idpw.println();
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index eb0b54b..b1b8fba 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -23,6 +23,7 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
 import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
@@ -44,6 +45,7 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
@@ -73,6 +75,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkScoreManager;
 import android.os.BatteryStats;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IDeviceIdleController;
@@ -93,7 +96,9 @@
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.view.Display;
+import android.widget.Toast;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
@@ -124,7 +129,7 @@
 public class AppStandbyController implements AppStandbyInternal {
 
     private static final String TAG = "AppStandbyController";
-    static final boolean DEBUG = false;
+    static final boolean DEBUG = true;
 
     static final boolean COMPRESS_TIME = false;
     private static final long ONE_MINUTE = 60 * 1000;
@@ -615,6 +620,16 @@
                         Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
                     }
                 }
+
+                if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
+                        && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
+                        >= mInjector.getRestrictedBucketDelayMs()) {
+                    newBucket = STANDBY_BUCKET_RESTRICTED;
+                    reason = app.lastRestrictReason;
+                    if (DEBUG) {
+                        Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
+                    }
+                }
                 if (DEBUG) {
                     Slog.d(TAG, "     Old bucket=" + oldBucket
                             + ", newBucket=" + newBucket);
@@ -733,15 +748,16 @@
                             elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
                     nextCheckTime = mStrongUsageTimeoutMillis;
                 }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage
-                        (MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
-                        nextCheckTime);
-                final boolean userStartedInteracting =
-                        appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
-                        prevBucket != appHistory.currentBucket &&
-                        (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
-                maybeInformListeners(pkg, userId, elapsedRealtime,
-                        appHistory.currentBucket, reason, userStartedInteracting);
+                if (appHistory.currentBucket != prevBucket) {
+                    mHandler.sendMessageDelayed(
+                            mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
+                            nextCheckTime);
+                    final boolean userStartedInteracting =
+                            appHistory.currentBucket == STANDBY_BUCKET_ACTIVE
+                                    && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
+                    maybeInformListeners(pkg, userId, elapsedRealtime,
+                            appHistory.currentBucket, reason, userStartedInteracting);
+                }
 
                 if (previouslyIdle) {
                     notifyBatteryStats(pkg, userId, false);
@@ -923,6 +939,15 @@
         }
     }
 
+    static boolean isUserUsage(int reason) {
+        if ((reason & REASON_MAIN_MASK) == REASON_MAIN_USAGE) {
+            final int subReason = reason & REASON_SUB_MASK;
+            return subReason == REASON_SUB_USAGE_USER_INTERACTION
+                    || subReason == REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+        }
+        return false;
+    }
+
     @Override
     public int[] getIdleUidsForUser(int userId) {
         if (!mAppIdleEnabled) {
@@ -1017,6 +1042,20 @@
     }
 
     @Override
+    public void restrictApp(@NonNull String packageName, int userId, int restrictReason) {
+        // If the package is not installed, don't allow the bucket to be set.
+        if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
+            Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
+            return;
+        }
+
+        final int reason = REASON_MAIN_FORCED_BY_SYSTEM | (REASON_SUB_MASK & restrictReason);
+        final long nowElapsed = mInjector.elapsedRealtime();
+        setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RESTRICTED, reason,
+                nowElapsed, false);
+    }
+
+    @Override
     public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
             int callingUid, int callingPid) {
         setAppStandbyBuckets(
@@ -1080,6 +1119,7 @@
         synchronized (mAppIdleLock) {
             // If the package is not installed, don't allow the bucket to be set.
             if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
+                Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
                 return;
             }
             AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
@@ -1089,8 +1129,9 @@
             // Don't allow changing bucket if higher than ACTIVE
             if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
 
-            // Don't allow prediction to change from/to NEVER
+            // Don't allow prediction to change from/to NEVER or from RESTRICTED.
             if ((app.currentBucket == STANDBY_BUCKET_NEVER
+                    || app.currentBucket == STANDBY_BUCKET_RESTRICTED
                     || newBucket == STANDBY_BUCKET_NEVER)
                     && predicted) {
                 return;
@@ -1103,6 +1144,50 @@
                 return;
             }
 
+            final boolean isForcedByUser =
+                    (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
+
+            // If the current bucket is RESTRICTED, only user force or usage should bring it out.
+            if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason)
+                    && !isForcedByUser) {
+                return;
+            }
+
+            if (newBucket == STANDBY_BUCKET_RESTRICTED) {
+                mAppIdleHistory
+                        .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
+
+                if (isForcedByUser) {
+                    // Only user force can bypass the delay restriction. If the user forced the
+                    // app into the RESTRICTED bucket, then a toast confirming the action
+                    // shouldn't be surprising.
+                    if (Build.IS_DEBUGGABLE) {
+                        Toast.makeText(mContext,
+                                // Since AppStandbyController sits low in the lock hierarchy,
+                                // make sure not to call out with the lock held.
+                                mHandler.getLooper(),
+                                mContext.getResources().getString(
+                                        R.string.as_app_forced_to_restricted_bucket, packageName),
+                                Toast.LENGTH_SHORT)
+                                .show();
+                    } else {
+                        Slog.i(TAG, packageName + " restricted by user");
+                    }
+                } else {
+                    final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime
+                            + mInjector.getRestrictedBucketDelayMs() - elapsedRealtime;
+                    if (timeUntilRestrictPossibleMs > 0) {
+                        Slog.w(TAG, "Tried to restrict recently used app: " + packageName
+                                + " due to " + reason);
+                        mHandler.sendMessageDelayed(
+                                mHandler.obtainMessage(
+                                        MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
+                                timeUntilRestrictPossibleMs);
+                        return;
+                    }
+                }
+            }
+
             // If the bucket is required to stay in a higher state for a specified duration, don't
             // override unless the duration has passed
             if (predicted) {
@@ -1435,6 +1520,12 @@
         private DisplayManager mDisplayManager;
         private PowerManager mPowerManager;
         int mBootPhase;
+        /**
+         * The minimum amount of time required since the last user interaction before an app can be
+         * placed in the RESTRICTED bucket.
+         */
+        // TODO: make configurable via DeviceConfig
+        private long mRestrictedBucketDelayMs = ONE_DAY;
 
         Injector(Context context, Looper looper) {
             mContext = context;
@@ -1459,6 +1550,12 @@
                 mDisplayManager = (DisplayManager) mContext.getSystemService(
                         Context.DISPLAY_SERVICE);
                 mPowerManager = mContext.getSystemService(PowerManager.class);
+
+                final ActivityManager activityManager =
+                        (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+                if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
+                    mRestrictedBucketDelayMs = 12 * ONE_HOUR;
+                }
             }
             mBootPhase = phase;
         }
@@ -1498,6 +1595,10 @@
             return Environment.getDataSystemDirectory();
         }
 
+        long getRestrictedBucketDelayMs() {
+            return mRestrictedBucketDelayMs;
+        }
+
         void noteEvent(int event, String packageName, int uid) throws RemoteException {
             mBatteryStats.noteEvent(event, packageName, uid);
         }
diff --git a/api/current.txt b/api/current.txt
index 3bd6d69..eb8ef8f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8030,6 +8030,7 @@
     field public static final int STANDBY_BUCKET_ACTIVE = 10; // 0xa
     field public static final int STANDBY_BUCKET_FREQUENT = 30; // 0x1e
     field public static final int STANDBY_BUCKET_RARE = 40; // 0x28
+    field public static final int STANDBY_BUCKET_RESTRICTED = 45; // 0x2d
     field public static final int STANDBY_BUCKET_WORKING_SET = 20; // 0x14
   }
 
@@ -23487,6 +23488,8 @@
     field public static final int STATUS_PARITY_PASSED = 1; // 0x1
     field public static final int STATUS_PARITY_REBUILT = 2; // 0x2
     field public static final int STATUS_UNKNOWN = 0; // 0x0
+    field public static final int TYPE_BDS_CNAV1 = 1283; // 0x503
+    field public static final int TYPE_BDS_CNAV2 = 1284; // 0x504
     field public static final int TYPE_BDS_D1 = 1281; // 0x501
     field public static final int TYPE_BDS_D2 = 1282; // 0x502
     field public static final int TYPE_GAL_F = 1538; // 0x602
@@ -23496,6 +23499,9 @@
     field public static final int TYPE_GPS_L1CA = 257; // 0x101
     field public static final int TYPE_GPS_L2CNAV = 258; // 0x102
     field public static final int TYPE_GPS_L5CNAV = 259; // 0x103
+    field public static final int TYPE_IRN_L5CA = 1793; // 0x701
+    field public static final int TYPE_QZS_L1CA = 1025; // 0x401
+    field public static final int TYPE_SBS = 513; // 0x201
     field public static final int TYPE_UNKNOWN = 0; // 0x0
   }
 
@@ -24369,12 +24375,14 @@
     method protected void finalize();
     method public void flush();
     method @NonNull public android.media.AudioAttributes getAudioAttributes();
+    method public float getAudioDescriptionMixLeveldB();
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method @IntRange(from=0) public int getBufferCapacityInFrames();
     method @IntRange(from=0) public int getBufferSizeInFrames();
     method public int getChannelConfiguration();
     method public int getChannelCount();
+    method public int getDualMonoMode();
     method @NonNull public android.media.AudioFormat getFormat();
     method public static float getMaxVolume();
     method public android.os.PersistableBundle getMetrics();
@@ -24408,8 +24416,10 @@
     method public void removeOnCodecFormatChangedListener(@NonNull android.media.AudioTrack.OnCodecFormatChangedListener);
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public boolean setAudioDescriptionMixLeveldB(@FloatRange(to=48.0f, toInclusive=true) float);
     method public int setAuxEffectSendLevel(@FloatRange(from=0.0) float);
     method public int setBufferSizeInFrames(@IntRange(from=0) int);
+    method public boolean setDualMonoMode(int);
     method public int setLoopPoints(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
     method public int setNotificationMarkerPosition(int);
     method public void setOffloadDelayPadding(@IntRange(from=0) int, @IntRange(from=0) int);
@@ -24434,6 +24444,10 @@
     method public int write(@NonNull float[], int, int, int);
     method public int write(@NonNull java.nio.ByteBuffer, int, int);
     method public int write(@NonNull java.nio.ByteBuffer, int, int, long);
+    field public static final int DUAL_MONO_MODE_LL = 2; // 0x2
+    field public static final int DUAL_MONO_MODE_LR = 1; // 0x1
+    field public static final int DUAL_MONO_MODE_OFF = 0; // 0x0
+    field public static final int DUAL_MONO_MODE_RR = 3; // 0x3
     field public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; // 0x1
     field public static final int ENCAPSULATION_MODE_HANDLE = 2; // 0x2
     field public static final int ENCAPSULATION_MODE_NONE = 0; // 0x0
@@ -24983,7 +24997,9 @@
     method @Deprecated @NonNull public java.nio.ByteBuffer[] getOutputBuffers();
     method @NonNull public android.media.MediaFormat getOutputFormat();
     method @NonNull public android.media.MediaFormat getOutputFormat(int);
+    method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int);
     method @Nullable public android.media.Image getOutputImage(int);
+    method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int);
     method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
     method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
     method public void release();
@@ -25007,6 +25023,7 @@
     field public static final int BUFFER_FLAG_PARTIAL_FRAME = 8; // 0x8
     field @Deprecated public static final int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
     field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
+    field public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; // 0x2
     field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
     field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1
     field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0
@@ -25083,6 +25100,24 @@
     method public void set(int, int);
   }
 
+  public static final class MediaCodec.GraphicBlock {
+    method protected void finalize();
+    method public static boolean isCodecCopyFreeCompatible(@NonNull String[]);
+    method public boolean isMappable();
+    method @NonNull public android.media.Image map();
+    method @NonNull public static android.media.MediaCodec.GraphicBlock obtain(int, int, int, long, @NonNull String[]);
+    method public void recycle();
+  }
+
+  public static final class MediaCodec.LinearBlock {
+    method protected void finalize();
+    method public static boolean isCodecCopyFreeCompatible(@NonNull String[]);
+    method public boolean isMappable();
+    method @NonNull public java.nio.ByteBuffer map();
+    method @Nullable public static android.media.MediaCodec.LinearBlock obtain(int, @NonNull String[]);
+    method public void recycle();
+  }
+
   public static final class MediaCodec.MetricsConstants {
     field public static final String CODEC = "android.media.mediacodec.codec";
     field public static final String ENCODER = "android.media.mediacodec.encoder";
@@ -25100,6 +25135,27 @@
     method public void onFrameRendered(@NonNull android.media.MediaCodec, long, long);
   }
 
+  public static final class MediaCodec.OutputFrame {
+    method public void getChangedKeys(@NonNull java.util.Set<java.lang.String>);
+    method public int getFlags();
+    method @NonNull public android.media.MediaFormat getFormat();
+    method @Nullable public android.media.MediaCodec.GraphicBlock getGraphicBlock();
+    method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
+    method public long getPresentationTimeUs();
+  }
+
+  public final class MediaCodec.QueueRequest {
+    method public void queue();
+    method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
+    method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int);
+    method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float);
+    method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock, long, int);
+    method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
+    method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, long, int);
+    method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
+    method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String);
+  }
+
   public final class MediaCodecInfo {
     method @NonNull public String getCanonicalName();
     method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(String);
@@ -25893,6 +25949,7 @@
     field public static final String KEY_GRID_COLUMNS = "grid-cols";
     field public static final String KEY_GRID_ROWS = "grid-rows";
     field public static final String KEY_HAPTIC_CHANNEL_COUNT = "haptic-channel-count";
+    field public static final String KEY_HARDWARE_AV_SYNC_ID = "hw-av-sync-id";
     field public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
     field public static final String KEY_HDR_STATIC_INFO = "hdr-static-info";
     field public static final String KEY_HEIGHT = "height";
@@ -43686,6 +43743,7 @@
     method public void onSurfaceRedrawNeeded(android.view.SurfaceHolder);
     method public void onTouchEvent(android.view.MotionEvent);
     method public void onVisibilityChanged(boolean);
+    method public void onZoomChanged(@FloatRange(from=0.0f, to=1.0f) float);
     method public void setOffsetNotificationsEnabled(boolean);
     method public void setTouchEventsEnabled(boolean);
   }
@@ -44829,6 +44887,7 @@
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
+    field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
     field public static final int PROPERTY_RTT = 1024; // 0x400
@@ -44906,6 +44965,7 @@
   public abstract class Conference extends android.telecom.Conferenceable {
     ctor public Conference(android.telecom.PhoneAccountHandle);
     method public final boolean addConnection(android.telecom.Connection);
+    method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle);
     method public final void destroy();
     method public final android.telecom.CallAudioState getCallAudioState();
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
@@ -44920,6 +44980,8 @@
     method public final android.telecom.StatusHints getStatusHints();
     method public android.telecom.Connection.VideoProvider getVideoProvider();
     method public int getVideoState();
+    method public final boolean isRingbackRequested();
+    method public void onAnswer(int);
     method public void onCallAudioStateChanged(android.telecom.CallAudioState);
     method public void onConnectionAdded(android.telecom.Connection);
     method public void onDisconnect();
@@ -44928,6 +44990,7 @@
     method public void onMerge(android.telecom.Connection);
     method public void onMerge();
     method public void onPlayDtmfTone(char);
+    method public void onReject();
     method public void onSeparate(android.telecom.Connection);
     method public void onStopDtmfTone();
     method public void onSwap();
@@ -44947,6 +45010,8 @@
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setExtras(@Nullable android.os.Bundle);
     method public final void setOnHold();
+    method public final void setRingbackRequested(boolean);
+    method public final void setRinging();
     method public final void setStatusHints(android.telecom.StatusHints);
     method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
     method public final void setVideoState(android.telecom.Connection, int);
@@ -45104,6 +45169,7 @@
     field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
+    field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int PROPERTY_IS_RTT = 256; // 0x100
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
@@ -45176,8 +45242,10 @@
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public android.net.Uri getAddress();
     method public android.os.Bundle getExtras();
+    method @Nullable public java.util.List<android.net.Uri> getParticipants();
     method public android.telecom.Connection.RttTextStream getRttTextStream();
     method public int getVideoState();
+    method public boolean isAdhocConferenceCall();
     method public boolean isRequestingRtt();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
@@ -45197,9 +45265,13 @@
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public void onConnectionServiceFocusGained();
     method public void onConnectionServiceFocusLost();
+    method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -45314,6 +45386,7 @@
     method public boolean supportsUriScheme(String);
     method public android.telecom.PhoneAccount.Builder toBuilder();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 16384; // 0x4000
     field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
     field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
@@ -45509,6 +45582,7 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
+    method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification();
     method public android.content.Intent createManageBlockedNumbersIntent();
     method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
@@ -45536,6 +45610,7 @@
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
+    method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle);
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
     field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
@@ -45856,6 +45931,7 @@
     field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array";
     field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array";
     field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array";
+    field public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long";
     field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -45939,6 +46015,7 @@
     field public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool";
     field public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
     field public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL = "data_rapid_notification_bool";
+    field public static final String KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG = "data_switch_validation_timeout_long";
     field public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL = "data_warning_notification_bool";
     field public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
     field public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
@@ -46021,6 +46098,8 @@
     field public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
     field public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
     field public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
+    field public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG = "opportunistic_network_backoff_time_long";
+    field public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_exit_hysteresis_time_long";
     field public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_hysteresis_time_long";
     field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG = "opportunistic_network_entry_or_exit_hysteresis_time_long";
     field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT = "opportunistic_network_entry_threshold_bandwidth_int";
@@ -46028,6 +46107,9 @@
     field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int";
     field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int";
     field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
+    field public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long";
+    field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long";
+    field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool";
     field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
     field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool";
     field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
@@ -46054,6 +46136,7 @@
     field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
+    field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
     field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
     field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
@@ -46063,6 +46146,7 @@
     field public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
     field public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
     field public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY = "support_tdscdma_roaming_networks_string_array";
+    field public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL = "switch_data_to_primary_if_primary_is_oos_bool";
     field public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
     field public static final String KEY_TTY_SUPPORTED_BOOL = "tty_supported_bool";
     field public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY = "unloggable_numbers_string_array";
@@ -46204,6 +46288,7 @@
 
   public final class CellIdentityNr extends android.telephony.CellIdentity {
     method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method public int getBand();
     method @Nullable public String getMccString();
     method @Nullable public String getMncString();
     method public long getNci();
diff --git a/api/system-current.txt b/api/system-current.txt
index 5b65eaa..f032a71 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1606,12 +1606,13 @@
     field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
   }
 
-  public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
-    method protected void finalize();
+  public final class BluetoothPan implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public void close();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) protected void finalize();
     method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
-    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
-    method public boolean isTetheringOn();
-    method public void setBluetoothTethering(boolean);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isTetheringOn();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void setBluetoothTethering(boolean);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
     field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
@@ -3686,6 +3687,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public long getCurrentFunctions();
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USB) public java.util.List<android.hardware.usb.UsbPort> getPorts();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
     field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
     field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
@@ -4718,6 +4720,7 @@
     method public long getSectionFilterLength();
     method public int getTsFilterCount();
     method public int getVideoFilterCount();
+    method public boolean isTimeFilterSupported();
   }
 
   public class Descrambler implements java.lang.AutoCloseable {
@@ -4763,7 +4766,7 @@
   public class Tuner implements java.lang.AutoCloseable {
     ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @NonNull String, int, @Nullable android.media.tv.tuner.Tuner.OnResourceLostListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
-    method public void close();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int connectCiCam(int);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int disconnectCiCam();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
@@ -4781,10 +4784,11 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLna(boolean);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
-    method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopScan();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopTune();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
   }
 
   public static interface Tuner.OnResourceLostListener {
@@ -5011,6 +5015,7 @@
     method public long getAvDataId();
     method public long getDataLength();
     method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData();
+    method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
     method public int getMpuSequenceNumber();
     method public long getOffset();
     method public long getPts();
@@ -5103,7 +5108,16 @@
     method public int getVersion();
   }
 
-  public class SectionSettings extends android.media.tv.tuner.filter.Settings {
+  public abstract class SectionSettings extends android.media.tv.tuner.filter.Settings {
+    method public boolean isCrcEnabled();
+    method public boolean isRaw();
+    method public boolean isRepeat();
+  }
+
+  public abstract static class SectionSettings.Builder<T extends android.media.tv.tuner.filter.SectionSettings.Builder<T>> extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettings.Builder<T>> {
+    method @NonNull public T setCrcEnabled(boolean);
+    method @NonNull public T setRaw(boolean);
+    method @NonNull public T setRepeat(boolean);
   }
 
   public class SectionSettingsWithSectionBits extends android.media.tv.tuner.filter.SectionSettings {
@@ -5113,7 +5127,7 @@
     method @NonNull public byte[] getMode();
   }
 
-  public static class SectionSettingsWithSectionBits.Builder extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder> {
+  public static class SectionSettingsWithSectionBits.Builder extends android.media.tv.tuner.filter.SectionSettings.Builder<android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder> {
     method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits build();
     method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder setFilter(@NonNull byte[]);
     method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder setMask(@NonNull byte[]);
@@ -5126,7 +5140,7 @@
     method public int getVersion();
   }
 
-  public static class SectionSettingsWithTableInfo.Builder extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder> {
+  public static class SectionSettingsWithTableInfo.Builder extends android.media.tv.tuner.filter.SectionSettings.Builder<android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder> {
     method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo build();
     method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder setTableId(int);
     method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder setVersion(int);
@@ -5164,7 +5178,7 @@
 
   public static class TlvFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TlvFilterConfiguration.Builder> {
     method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration build();
-    method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setIsCompressedIpPacket(boolean);
+    method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setCompressedIpPacket(boolean);
     method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPacketType(int);
     method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPassthrough(boolean);
   }
@@ -7404,6 +7418,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
+    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
     method public int getVerboseLoggingLevel();
@@ -8628,7 +8643,6 @@
     method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRcsMessageServiceRegisterer();
     method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRegistryServiceRegisterer();
     method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyServiceRegisterer();
-    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getWindowServiceRegisterer();
   }
 
   public static class TelephonyServiceManager.ServiceNotFoundException extends java.lang.Exception {
@@ -10130,6 +10144,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
     field public static final String KEY_IMPORTANCE = "key_importance";
+    field public static final String KEY_NOT_CONVERSATION = "key_not_conversation";
     field public static final String KEY_PEOPLE = "key_people";
     field public static final String KEY_RANKING_SCORE = "key_ranking_score";
     field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
@@ -10736,6 +10751,13 @@
     field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff
   }
 
+  public static final class AccessNetworkConstants.NgranBands {
+    method public static int getFrequencyRangeGroup(int);
+    field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1
+    field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2
+    field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
+  }
+
   public final class BarringInfo implements android.os.Parcelable {
     ctor public BarringInfo();
     method @NonNull public android.telephony.BarringInfo createLocationInfoSanitizedCopy();
@@ -11957,6 +11979,7 @@
     method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyUserActivity();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
@@ -11980,6 +12003,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -12014,6 +12038,7 @@
     field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
     field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
     field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
+    field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
     field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
     field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
     field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
@@ -12036,6 +12061,7 @@
     field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
     field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
     field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
+    field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
     field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
     field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
     field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4
@@ -12048,17 +12074,22 @@
     field public static final String EXTRA_PCO_VALUE = "pcoValue";
     field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
     field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
+    field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
     field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
+    field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
+    field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
     field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES";
     field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
     field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1
     field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0
     field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
+    field public static final String EXTRA_SPN = "android.telephony.extra.SPN";
     field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int KEY_TYPE_EPDG = 1; // 0x1
     field public static final int KEY_TYPE_WLAN = 2; // 0x2
+    field public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
     field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
     field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
     field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
@@ -12103,10 +12134,32 @@
   public class TelephonyRegistryManager {
     method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
     method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+    method public void notifyActiveDataSubIdChanged(int);
     method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo);
+    method public void notifyCallForwardingChanged(int, boolean);
+    method public void notifyCallQualityChanged(int, int, @NonNull android.telephony.CallQuality, int);
+    method public void notifyCallStateChanged(int, int, int, @Nullable String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
     method public void notifyCarrierNetworkChange(boolean);
+    method public void notifyCellInfoChanged(int, @NonNull java.util.List<android.telephony.CellInfo>);
+    method public void notifyCellLocation(int, @NonNull android.telephony.CellIdentity);
+    method public void notifyDataActivationStateChanged(int, int, int);
+    method public void notifyDataActivityChanged(int, int);
+    method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState);
+    method public void notifyDisconnectCause(int, int, int, int);
+    method public void notifyEmergencyNumberList(int, int);
+    method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
+    method public void notifyMessageWaitingChanged(int, int, boolean);
+    method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+    method public void notifyPreciseCallState(int, int, int, int, int);
+    method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int);
+    method public void notifyRadioPowerStateChanged(int, int, int);
     method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+    method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState);
+    method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength);
+    method public void notifySrvccStateChanged(int, int);
+    method public void notifyUserMobileDataStateChanged(int, int, boolean);
+    method public void notifyVoiceActivationStateChanged(int, int, int);
     method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
   }
@@ -12686,6 +12739,9 @@
     method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
     method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
     field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+    field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+    field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+    field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
   }
 
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index 75297e5..dc6b515 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -742,6 +742,10 @@
     field @Nullable public final android.util.ArraySet<android.content.ComponentName> whitelistedComponents;
   }
 
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+    method @NonNull public static android.os.UserHandle getUserHandleFromUri(@NonNull android.net.Uri);
+  }
+
   public class ContentProviderClient implements java.lang.AutoCloseable {
     method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long);
   }
@@ -3152,6 +3156,8 @@
     method @NonNull public android.telecom.ConnectionRequest.Builder setAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
     method @NonNull public android.telecom.ConnectionRequest.Builder setAddress(@NonNull android.net.Uri);
     method @NonNull public android.telecom.ConnectionRequest.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setIsAdhocConferenceCall(boolean);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setParticipants(@Nullable java.util.List<android.net.Uri>);
     method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeFromInCall(@NonNull android.os.ParcelFileDescriptor);
     method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeToInCall(@NonNull android.os.ParcelFileDescriptor);
     method @NonNull public android.telecom.ConnectionRequest.Builder setShouldShowIncomingCallUi(boolean);
@@ -3191,6 +3197,13 @@
     field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff
   }
 
+  public static final class AccessNetworkConstants.NgranBands {
+    method public static int getFrequencyRangeGroup(int);
+    field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1
+    field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2
+    field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
+  }
+
   public final class BarringInfo implements android.os.Parcelable {
     ctor public BarringInfo();
     ctor public BarringInfo(@Nullable android.telephony.CellIdentity, @NonNull android.util.SparseArray<android.telephony.BarringInfo.BarringServiceInfo>);
@@ -3367,6 +3380,39 @@
     field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
   }
 
+  public class TelephonyRegistryManager {
+    method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+    method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+    method public void notifyActiveDataSubIdChanged(int);
+    method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo);
+    method public void notifyCallForwardingChanged(int, boolean);
+    method public void notifyCallQualityChanged(int, int, @NonNull android.telephony.CallQuality, int);
+    method public void notifyCallStateChanged(int, int, int, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
+    method public void notifyCarrierNetworkChange(boolean);
+    method public void notifyCellInfoChanged(int, @NonNull java.util.List<android.telephony.CellInfo>);
+    method public void notifyCellLocation(int, @NonNull android.telephony.CellIdentity);
+    method public void notifyDataActivationStateChanged(int, int, int);
+    method public void notifyDataActivityChanged(int, int);
+    method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState);
+    method public void notifyDisconnectCause(int, int, int, int);
+    method public void notifyEmergencyNumberList(int, int);
+    method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
+    method public void notifyMessageWaitingChanged(int, int, boolean);
+    method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+    method public void notifyPreciseCallState(int, int, int, int, int);
+    method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int);
+    method public void notifyRadioPowerStateChanged(int, int, int);
+    method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+    method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState);
+    method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength);
+    method public void notifySrvccStateChanged(int, int);
+    method public void notifyUserMobileDataStateChanged(int, int, boolean);
+    method public void notifyVoiceActivationStateChanged(int, int, int);
+    method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
+    method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+  }
+
 }
 
 package android.telephony.emergency {
@@ -3588,6 +3634,9 @@
     method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
     method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
     field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+    field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+    field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+    field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
   }
 
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 192f3f0..d05ac18 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -810,6 +810,7 @@
         FREQUENT = 2;
         RARE = 3;
         NEVER = 4;
+        RESTRICTED = 5;
     }
     optional Bucket standby_bucket = 5 [default = UNKNOWN];
 
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index 3893be4..cd751f4 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -42,7 +42,9 @@
                 + "         Sets the functions which, if the device was charging, become current on"
                     + "screen unlock. If function is blank, turn off this feature.\n"
                 + "       svc usb getFunctions\n"
-                + "          Gets the list of currently enabled functions\n\n"
+                + "          Gets the list of currently enabled functions\n"
+                + "       svc usb resetUsbGadget\n"
+                + "          Reset usb gadget\n\n"
                 + "possible values of [function] are any of 'mtp', 'ptp', 'rndis', 'midi'\n";
     }
 
@@ -75,6 +77,13 @@
                     System.err.println("Error communicating with UsbManager: " + e);
                 }
                 return;
+            } else if ("resetUsbGadget".equals(args[1])) {
+                try {
+                    usbMgr.resetUsbGadget();
+                } catch (RemoteException e) {
+                    System.err.println("Error communicating with UsbManager: " + e);
+                }
+                return;
             }
         }
         System.err.println(longHelp());
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 4cb8d93..34684c4 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -175,4 +175,11 @@
      * Called from SystemUI when it shows the AoD UI.
      */
     oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
+
+    /**
+     * Called when the wallpaper needs to zoom out.
+     * The zoom value goes from 0 to 1 (inclusive) where 1 means fully zoomed out,
+     * 0 means fully zoomed in
+     */
+    oneway void setWallpaperZoomOut(float zoom, String callingPackage, int displayId);
 }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0c5e67c..f0d0e98 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1180,13 +1180,26 @@
         }
 
         try {
-            java.lang.ClassLoader cl = getClassLoader();
+            final java.lang.ClassLoader cl = getClassLoader();
             if (!mPackageName.equals("android")) {
                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                         "initializeJavaContextClassLoader");
                 initializeJavaContextClassLoader();
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             }
+
+            // Rewrite the R 'constants' for all library apks.
+            SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
+                    false, false);
+            for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
+                final int id = packageIdentifiers.keyAt(i);
+                if (id == 0x01 || id == 0x7f) {
+                    continue;
+                }
+
+                rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
+            }
+
             ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
             app = mActivityThread.mInstrumentation.newApplication(
                     cl, appClass, appContext);
@@ -1215,19 +1228,6 @@
             }
         }
 
-        // Rewrite the R 'constants' for all library apks.
-        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
-                false, false);
-        final int N = packageIdentifiers.size();
-        for (int i = 0; i < N; i++) {
-            final int id = packageIdentifiers.keyAt(i);
-            if (id == 0x01 || id == 0x7f) {
-                continue;
-            }
-
-            rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
-        }
-
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
         return app;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3c4e861..1af275f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1004,6 +1004,31 @@
      */
     public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
 
+
+    /**
+     * {@link #extras} key: this is a remote input history which can include media messages
+     * in addition to text, as supplied to
+     * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} or
+     * {@link Builder#setRemoteInputHistory(CharSequence[])}.
+     *
+     * SystemUI can populate this through
+     * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} with the most recent inputs
+     * that have been sent through a {@link RemoteInput} of this Notification. These items can
+     * represent either media content (specified by a URI and a MIME type) or a text message
+     * (described by a CharSequence).
+     *
+     * To maintain compatibility, this can also be set by apps with
+     * {@link Builder#setRemoteInputHistory(CharSequence[])}, which will create a
+     * {@link RemoteInputHistoryItem} for each of the provided text-only messages.
+     *
+     * The extra with this key is of type {@link RemoteInputHistoryItem[]} and contains the most
+     * recent entry at the 0 index, the second most recent at the 1 index, etc.
+     *
+     * @see Builder#setRemoteInputHistory(RemoteInputHistoryItem[])
+     * @hide
+     */
+    public static final String EXTRA_REMOTE_INPUT_HISTORY_ITEMS = "android.remoteInputHistoryItems";
+
     /**
      * {@link #extras} key: boolean as supplied to
      * {@link Builder#setShowRemoteInputSpinner(boolean)}.
@@ -3833,12 +3858,37 @@
             if (text == null) {
                 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
             } else {
-                final int N = Math.min(MAX_REPLY_HISTORY, text.length);
-                CharSequence[] safe = new CharSequence[N];
-                for (int i = 0; i < N; i++) {
+                final int itemCount = Math.min(MAX_REPLY_HISTORY, text.length);
+                CharSequence[] safe = new CharSequence[itemCount];
+                RemoteInputHistoryItem[] items = new RemoteInputHistoryItem[itemCount];
+                for (int i = 0; i < itemCount; i++) {
                     safe[i] = safeCharSequence(text[i]);
+                    items[i] = new RemoteInputHistoryItem(text[i]);
                 }
                 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
+
+                // Also add these messages as structured history items.
+                mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, items);
+            }
+            return this;
+        }
+
+        /**
+         * Set the remote input history, with support for embedding URIs and mime types for
+         * images and other media.
+         * @hide
+         */
+        @NonNull
+        public Builder setRemoteInputHistory(RemoteInputHistoryItem[] items) {
+            if (items == null) {
+                mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, null);
+            } else {
+                final int itemCount = Math.min(MAX_REPLY_HISTORY, items.length);
+                RemoteInputHistoryItem[] history = new RemoteInputHistoryItem[itemCount];
+                for (int i = 0; i < itemCount; i++) {
+                    history[i] = items[i];
+                }
+                mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, history);
             }
             return this;
         }
@@ -5246,16 +5296,17 @@
                 big.setViewVisibility(R.id.actions_container, View.GONE);
             }
 
-            CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
-            if (validRemoteInput && replyText != null
-                    && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])
+            RemoteInputHistoryItem[] replyText = (RemoteInputHistoryItem[])
+                    mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+            if (validRemoteInput && replyText != null && replyText.length > 0
+                    && !TextUtils.isEmpty(replyText[0].getText())
                     && p.maxRemoteInputHistory > 0) {
                 boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER);
                 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
                 big.setViewVisibility(R.id.notification_material_reply_text_1_container,
                         View.VISIBLE);
                 big.setTextViewText(R.id.notification_material_reply_text_1,
-                        processTextSpans(replyText[0]));
+                        processTextSpans(replyText[0].getText()));
                 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
                 big.setViewVisibility(R.id.notification_material_reply_progress,
                         showSpinner ? View.VISIBLE : View.GONE);
@@ -5264,19 +5315,19 @@
                         ColorStateList.valueOf(
                                 isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p)));
 
-                if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])
+                if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1].getText())
                         && p.maxRemoteInputHistory > 1) {
                     big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
                     big.setTextViewText(R.id.notification_material_reply_text_2,
-                            processTextSpans(replyText[1]));
+                            processTextSpans(replyText[1].getText()));
                     setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
 
-                    if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])
+                    if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText())
                             && p.maxRemoteInputHistory > 2) {
                         big.setViewVisibility(
                                 R.id.notification_material_reply_text_3, View.VISIBLE);
                         big.setTextViewText(R.id.notification_material_reply_text_3,
-                                processTextSpans(replyText[2]));
+                                processTextSpans(replyText[2].getText()));
                         setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
                     }
                 }
@@ -7517,7 +7568,7 @@
             @Nullable
             private final Person mSender;
             /** True if this message was generated from the extra
-             *  {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}
+             *  {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}
              */
             private final boolean mRemoteInputHistory;
 
@@ -7569,7 +7620,7 @@
              * Should be <code>null</code> for messages by the current user, in which case
              * the platform will insert the user set in {@code MessagingStyle(Person)}.
              * @param remoteInputHistory True if the messages was generated from the extra
-             * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}.
+             * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}.
              * <p>
              * The person provided should contain an Icon, set with
              * {@link Person.Builder#setIcon(Icon)} and also have a name provided
@@ -7676,7 +7727,7 @@
 
             /**
              * @return True if the message was generated from
-             * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}.
+             * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}.
              * @hide
              */
             public boolean isRemoteInputHistory() {
@@ -7906,8 +7957,8 @@
             if (mBuilder.mActions.size() > 0) {
                 maxRows--;
             }
-            CharSequence[] remoteInputHistory = mBuilder.mN.extras.getCharSequenceArray(
-                    EXTRA_REMOTE_INPUT_HISTORY);
+            RemoteInputHistoryItem[] remoteInputHistory = (RemoteInputHistoryItem[])
+                    mBuilder.mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
             if (remoteInputHistory != null
                     && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) {
                 // Let's remove some messages to make room for the remote input history.
diff --git a/core/java/android/app/RemoteInputHistoryItem.java b/core/java/android/app/RemoteInputHistoryItem.java
new file mode 100644
index 0000000..091db3f
--- /dev/null
+++ b/core/java/android/app/RemoteInputHistoryItem.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package android.app;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Stores historical input from a RemoteInput attached to a Notification.
+ *
+ * History items represent either a text message (specified by providing a CharSequence,
+ * or a media message (specified by providing a URI and a MIME type). Media messages must also
+ * include text to insert when the image cannot be loaded, ex. when URI read permission has not been
+ * granted correctly.
+ *
+ * @hide
+ */
+public class RemoteInputHistoryItem implements Parcelable {
+    private CharSequence mText;
+    private String mMimeType;
+    private Uri mUri;
+
+    public RemoteInputHistoryItem(String mimeType, Uri uri, CharSequence backupText) {
+        this.mMimeType = mimeType;
+        this.mUri = uri;
+        this.mText = Notification.safeCharSequence(backupText);
+    }
+
+    public RemoteInputHistoryItem(CharSequence text) {
+        this.mText = Notification.safeCharSequence(text);
+    }
+
+    protected RemoteInputHistoryItem(Parcel in) {
+        mText = in.readCharSequence();
+        mMimeType = in.readStringNoHelper();
+        mUri = in.readParcelable(Uri.class.getClassLoader());
+    }
+
+    public static final Creator<RemoteInputHistoryItem> CREATOR =
+            new Creator<RemoteInputHistoryItem>() {
+                @Override
+                public RemoteInputHistoryItem createFromParcel(Parcel in) {
+                    return new RemoteInputHistoryItem(in);
+                }
+
+                @Override
+                public RemoteInputHistoryItem[] newArray(int size) {
+                    return new RemoteInputHistoryItem[size];
+                }
+            };
+
+    public CharSequence getText() {
+        return mText;
+    }
+
+    public String getMimeType() {
+        return mMimeType;
+    }
+
+    public Uri getUri() {
+        return mUri;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeCharSequence(mText);
+        dest.writeStringNoHelper(mMimeType);
+        dest.writeParcelable(mUri, flags);
+    }
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index a885009..6f1effd 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1848,6 +1848,24 @@
     }
 
     /**
+     * Set the current zoom out level of the wallpaper
+     * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in
+     *
+     * @hide
+     */
+    public void setWallpaperZoomOut(float zoom) {
+        if (zoom < 0 || zoom > 1f) {
+            throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom);
+        }
+        try {
+            sGlobals.mService.setWallpaperZoomOut(zoom, mContext.getOpPackageName(),
+                    mContext.getDisplayId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns whether wallpapers are supported for the calling user. If this function returns
      * {@code false}, any attempts to changing the wallpaper will have no effect,
      * and any attempt to obtain of the wallpaper will return {@code null}.
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index a60e591..5668944 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -142,7 +142,7 @@
 
     /**
      * The app has not be used for several days and/or is unlikely to be used for several days.
-     * Apps in this bucket will have the most restrictions, including network restrictions, except
+     * Apps in this bucket will have more restrictions, including network restrictions, except
      * during certain short periods (at a minimum, once a day) when they are allowed to execute
      * jobs, access the network, etc.
      * @see #getAppStandbyBucket()
@@ -150,6 +150,15 @@
     public static final int STANDBY_BUCKET_RARE = 40;
 
     /**
+     * The app has not be used for several days, is unlikely to be used for several days, and has
+     * been misbehaving in some manner.
+     * Apps in this bucket will have the most restrictions, including network restrictions and
+     * additional restrictions on jobs.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_RESTRICTED = 45;
+
+    /**
      * The app has never been used.
      * {@hide}
      */
@@ -278,6 +287,26 @@
      * @hide
      */
     public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
+    /**
+     * The reason for restricting the app is unknown or undefined.
+     * @hide
+     */
+    public static final int REASON_SUB_RESTRICT_UNDEFINED = 0x0000;
+    /**
+     * The app was unnecessarily using system resources (battery, memory, etc) in the background.
+     * @hide
+     */
+    public static final int REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE = 0x0001;
+    /**
+     * The app was deemed to be intentionally abusive.
+     * @hide
+     */
+    public static final int REASON_SUB_RESTRICT_ABUSE = 0x0002;
+    /**
+     * The app was displaying buggy behavior.
+     * @hide
+     */
+    public static final int REASON_SUB_RESTRICT_BUGGY = 0x0003;
 
 
     /** @hide */
@@ -287,6 +316,7 @@
             STANDBY_BUCKET_WORKING_SET,
             STANDBY_BUCKET_FREQUENT,
             STANDBY_BUCKET_RARE,
+            STANDBY_BUCKET_RESTRICTED,
             STANDBY_BUCKET_NEVER,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -598,7 +628,7 @@
      * state of the app based on app usage patterns. Standby buckets determine how much an app will
      * be restricted from running background tasks such as jobs and alarms.
      * <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
-     * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
+     * {@link #STANDBY_BUCKET_RESTRICTED}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
      * restrictive. The battery level of the device might also affect the restrictions.
      * <p>Apps in buckets &le; {@link #STANDBY_BUCKET_ACTIVE} have no standby restrictions imposed.
      * Apps in buckets &gt; {@link #STANDBY_BUCKET_FREQUENT} may have network access restricted when
@@ -642,7 +672,8 @@
     /**
      * {@hide}
      * Changes an app's standby bucket to the provided value. The caller can only set the standby
-     * bucket for a different app than itself.
+     * bucket for a different app than itself. The caller will not be able to change an app's
+     * standby bucket if that app is in the {@link #STANDBY_BUCKET_RESTRICTED} bucket.
      * @param packageName the package name of the app to set the bucket for. A SecurityException
      *                    will be thrown if the package name is that of the caller.
      * @param bucket the standby bucket to set it to, which should be one of STANDBY_BUCKET_*.
@@ -688,7 +719,8 @@
     /**
      * {@hide}
      * Changes the app standby bucket for multiple apps at once. The Map is keyed by the package
-     * name and the value is one of STANDBY_BUCKET_*.
+     * name and the value is one of STANDBY_BUCKET_*. The caller will not be able to change an
+     * app's standby bucket if that app is in the {@link #STANDBY_BUCKET_RESTRICTED} bucket.
      * @param appBuckets a map of package name to bucket value.
      */
     @SystemApi
@@ -1027,6 +1059,20 @@
                 break;
             case REASON_MAIN_FORCED_BY_SYSTEM:
                 sb.append("s");
+                switch (standbyReason & REASON_SUB_MASK) {
+                    case REASON_SUB_RESTRICT_ABUSE:
+                        sb.append("-ra");
+                        break;
+                    case REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE:
+                        sb.append("-rbru");
+                        break;
+                    case REASON_SUB_RESTRICT_BUGGY:
+                        sb.append("-rb");
+                        break;
+                    case REASON_SUB_RESTRICT_UNDEFINED:
+                        sb.append("-r");
+                        break;
+                }
                 break;
             case REASON_MAIN_FORCED_BY_USER:
                 sb.append("f");
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index cb1f055..e751354 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1222,6 +1222,7 @@
             if (mService != null) {
                 return mService.factoryReset();
             }
+            Log.e(TAG, "factoryReset(): IBluetooth Service is null");
             SystemProperties.set("persist.bluetooth.factoryreset", "true");
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
@@ -1239,7 +1240,7 @@
      */
     @UnsupportedAppUsage
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public @NonNull ParcelUuid[] getUuids() {
+    public @Nullable ParcelUuid[] getUuids() {
         if (getState() != STATE_ON) {
             return null;
         }
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 024bb06..ec63fd0 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -30,6 +30,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.CloseGuard;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -50,10 +51,11 @@
  * @hide
  */
 @SystemApi
-public final class BluetoothPan implements BluetoothProfile {
+public final class BluetoothPan implements BluetoothProfile, AutoCloseable {
     private static final String TAG = "BluetoothPan";
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
+    private CloseGuard mCloseGuard;
 
     /**
      * Intent used to broadcast the change in connection state of the Pan
@@ -166,10 +168,15 @@
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mContext = context;
         mProfileConnector.connect(context, listener);
+        mCloseGuard = new CloseGuard();
+        mCloseGuard.open("close");
     }
 
-    @UnsupportedAppUsage
-    /*package*/ void close() {
+    /**
+     * Closes the connection to the service and unregisters callbacks
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public void close() {
         if (VDBG) log("close()");
         mProfileConnector.disconnect();
     }
@@ -178,8 +185,11 @@
         return mProfileConnector.getService();
     }
 
-
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     protected void finalize() {
+        if (mCloseGuard != null) {
+            mCloseGuard.warnIfOpen();
+        }
         close();
     }
 
@@ -316,6 +326,7 @@
      * @hide
      */
     @Override
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
         final IBluetoothPan service = getService();
@@ -335,6 +346,7 @@
      * {@inheritDoc}
      */
     @Override
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getConnectionState(@Nullable BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
         final IBluetoothPan service = getService();
@@ -355,6 +367,7 @@
      *
      * @param value is whether to enable or disable bluetooth tethering
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public void setBluetoothTethering(boolean value) {
         String pkgName = mContext.getOpPackageName();
         if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
@@ -373,6 +386,7 @@
      *
      * @return true if tethering is on, false if not or some error occurred
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean isTetheringOn() {
         if (VDBG) log("isTetheringOn()");
         final IBluetoothPan service = getService();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 85826fd..c271e3c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -28,6 +28,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.AppOpsManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.PackageManager;
@@ -2526,6 +2527,16 @@
     }
 
     /**
+     * Returns the user associated with the given URI.
+     *
+     * @hide
+     */
+    @TestApi
+    public @NonNull static UserHandle getUserHandleFromUri(@NonNull Uri uri) {
+        return UserHandle.of(getUserIdFromUri(uri, Process.myUserHandle().getIdentifier()));
+    }
+
+    /**
      * Removes userId part from authority string. Expects format:
      * userId@some.authority
      * If there is no userId in the authority, it symply returns the argument
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index c8dbd16..e32865e 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -126,6 +126,9 @@
     /* Gets the current screen unlocked functions. */
     long getScreenUnlockedFunctions();
 
+    /* Resets the USB gadget. */
+    void resetUsbGadget();
+
     /* Get the functionfs control handle for the given function. Usb
      * descriptors will already be written, and the handle will be
      * ready to use.
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 67fdda3..827353b 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -794,6 +794,25 @@
     }
 
     /**
+     * Resets the USB Gadget.
+     * <p>
+     * Performs USB data stack reset through USB Gadget HAL.
+     * It will force USB data connection reset. The connection will disconnect and reconnect.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public void resetUsbGadget() {
+        try {
+            mService.resetUsbGadget();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns a list of physical USB ports on the device.
      * <p>
      * This list is guaranteed to contain all dual-role USB Type C ports but it might
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index bf13c35..6a80788 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -247,6 +247,19 @@
      */
     public static final int BRIGHTNESS_DEFAULT = -1;
 
+    /**
+     * Brightness value for fully off in float.
+     * TODO: rename this to BRIGHTNES_OFF and remove the integer-based constant.
+     * @hide
+     */
+    public static final float BRIGHTNESS_OFF_FLOAT = -1.0f;
+
+    /**
+     * Invalid brightness value.
+     * @hide
+     */
+    public static final float BRIGHTNESS_INVALID_FLOAT = Float.NaN;
+
     // Note: Be sure to update android.os.BatteryStats and PowerManager.h
     // if adding or modifying user activity event constants.
 
diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java
index 064cf7d..4f5f3d6 100644
--- a/core/java/android/os/TelephonyServiceManager.java
+++ b/core/java/android/os/TelephonyServiceManager.java
@@ -226,12 +226,4 @@
     public ServiceRegisterer getIccPhoneBookServiceRegisterer() {
         return new ServiceRegisterer("simphonebook");
     }
-
-    /**
-     * Returns {@link ServiceRegisterer} for the window service.
-     */
-    @NonNull
-    public ServiceRegisterer getWindowServiceRegisterer() {
-        return new ServiceRegisterer(Context.WINDOW_SERVICE);
-    }
 }
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index c84fbc7..8464c6d 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -131,6 +131,13 @@
     public static final String KEY_RANKING_SCORE = "key_ranking_score";
 
     /**
+     * Data type: boolean, when true it suggests this is NOT a conversation notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_NOT_CONVERSATION = "key_not_conversation";
+
+    /**
      * Create a notification adjustment.
      *
      * @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index fd04f49..e053ed5 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1539,6 +1539,7 @@
         private ArrayList<CharSequence> mSmartReplies;
         private boolean mCanBubble;
         private boolean mVisuallyInterruptive;
+        private boolean mIsConversation;
 
         private static final int PARCEL_VERSION = 2;
 
@@ -1571,6 +1572,7 @@
             out.writeCharSequenceList(mSmartReplies);
             out.writeBoolean(mCanBubble);
             out.writeBoolean(mVisuallyInterruptive);
+            out.writeBoolean(mIsConversation);
         }
 
         /** @hide */
@@ -1604,6 +1606,7 @@
             mSmartReplies = in.readCharSequenceList();
             mCanBubble = in.readBoolean();
             mVisuallyInterruptive = in.readBoolean();
+            mIsConversation = in.readBoolean();
         }
 
 
@@ -1801,6 +1804,14 @@
         }
 
         /**
+         * Returns whether this notification is a conversation notification.
+         * @hide
+         */
+        public boolean isConversation() {
+            return mIsConversation;
+        }
+
+        /**
          * @hide
          */
         @VisibleForTesting
@@ -1812,7 +1823,7 @@
                 int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
                 boolean noisy, ArrayList<Notification.Action> smartActions,
                 ArrayList<CharSequence> smartReplies, boolean canBubble,
-                boolean visuallyInterruptive) {
+                boolean visuallyInterruptive, boolean isConversation) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1834,6 +1845,7 @@
             mSmartReplies = smartReplies;
             mCanBubble = canBubble;
             mVisuallyInterruptive = visuallyInterruptive;
+            mIsConversation = isConversation;
         }
 
         /**
@@ -1859,7 +1871,8 @@
                     other.mSmartActions,
                     other.mSmartReplies,
                     other.mCanBubble,
-                    other.mVisuallyInterruptive);
+                    other.mVisuallyInterruptive,
+                    other.mIsConversation);
         }
 
         /**
@@ -1912,7 +1925,8 @@
                         == (other.mSmartActions == null ? 0 : other.mSmartActions.size()))
                     && Objects.equals(mSmartReplies, other.mSmartReplies)
                     && Objects.equals(mCanBubble, other.mCanBubble)
-                    && Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive);
+                    && Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive)
+                    && Objects.equals(mIsConversation, other.mIsConversation);
         }
     }
 
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index 00e0b7c..84b6869 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -37,4 +37,5 @@
     void requestWallpaperColors();
     @UnsupportedAppUsage
     void destroy();
+    void setZoomOut(float scale);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index e3fd8d2..9169143 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -16,6 +16,7 @@
 
 package android.service.wallpaper;
 
+import android.annotation.FloatRange;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -122,6 +123,9 @@
     private static final int MSG_WINDOW_MOVED = 10035;
     private static final int MSG_TOUCH_EVENT = 10040;
     private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050;
+    private static final int MSG_SCALE = 10100;
+
+    private static final float MAX_SCALE = 1.15f;
 
     private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
 
@@ -170,6 +174,7 @@
         int mType;
         int mCurWidth;
         int mCurHeight;
+        float mZoom = 0f;
         int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
         int mWindowPrivateFlags =
                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
@@ -496,6 +501,15 @@
         }
 
         /**
+         * Returns the current scale of the surface
+         * @hide
+         */
+        @VisibleForTesting
+        public float getZoom() {
+            return mZoom;
+        }
+
+        /**
          * Called once to initialize the engine.  After returning, the
          * engine's surface will be created by the framework.
          */
@@ -623,6 +637,16 @@
         }
 
         /**
+         * Called when the zoom level of the wallpaper changed.
+         * This method will be called with the initial zoom level when the surface is created.
+         *
+         * @param zoom the zoom level, between 0 indicating fully zoomed in and 1 indicating fully
+         *             zoomed out.
+         */
+        public void onZoomChanged(@FloatRange(from = 0f, to = 1f) float zoom) {
+        }
+
+        /**
          * Notifies the engine that wallpaper colors changed significantly.
          * This will trigger a {@link #onComputeColors()} call.
          */
@@ -706,6 +730,7 @@
             out.print(prefix); out.print("mConfiguration=");
                     out.println(mMergedConfiguration.getMergedConfiguration());
             out.print(prefix); out.print("mLayout="); out.println(mLayout);
+            out.print(prefix); out.print("mZoom="); out.println(mZoom);
             synchronized (mLock) {
                 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
                         out.print(" mPendingXOffset="); out.println(mPendingXOffset);
@@ -721,6 +746,37 @@
             }
         }
 
+        /**
+         * Set the wallpaper zoom to the given value. This value will be ignored when in ambient
+         * mode (and zoom will be reset to 0).
+         * @hide
+         * @param zoom between 0 and 1 (inclusive) indicating fully zoomed in to fully zoomed out
+         *              respectively.
+         */
+        @VisibleForTesting
+        public void setZoom(float zoom) {
+            if (DEBUG) {
+                Log.v(TAG, "set zoom received: " + zoom);
+            }
+            boolean updated = false;
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Log.v(TAG, "mZoom: " + mZoom + " updated: " + zoom);
+                }
+                if (mIsInAmbientMode) {
+                    mZoom = 0;
+                }
+                if (Float.compare(zoom, mZoom) != 0) {
+                    mZoom = zoom;
+                    updated = true;
+                }
+            }
+            if (DEBUG) Log.v(TAG, "setZoom updated? " + updated);
+            if (updated && !mDestroyed) {
+                onZoomChanged(mZoom);
+            }
+        }
+
         private void dispatchPointer(MotionEvent event) {
             if (event.isTouchEvent()) {
                 synchronized (mLock) {
@@ -920,6 +976,7 @@
                                     c.surfaceCreated(mSurfaceHolder);
                                 }
                             }
+                            onZoomChanged(0f);
                         }
 
                         redrawNeeded |= creating || (relayoutResult
@@ -1080,6 +1137,7 @@
                 mIsInAmbientMode = inAmbientMode;
                 if (mCreated) {
                     onAmbientModeChanged(inAmbientMode, animationDuration);
+                    setZoom(0);
                 }
             }
         }
@@ -1354,6 +1412,11 @@
             }
         }
 
+        public void setZoomOut(float scale) {
+            Message msg = mCaller.obtainMessageI(MSG_SCALE, Float.floatToIntBits(scale));
+            mCaller.sendMessage(msg);
+        }
+
         public void reportShown() {
             if (!mShownReported) {
                 mShownReported = true;
@@ -1426,6 +1489,9 @@
                 case MSG_UPDATE_SURFACE:
                     mEngine.updateSurface(true, false, false);
                     break;
+                case MSG_SCALE:
+                    mEngine.setZoom(Float.intBitsToFloat(message.arg1));
+                    break;
                 case MSG_VISIBILITY_CHANGED:
                     if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
                             + ": " + message.arg1);
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index e25826c..5737591 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -28,8 +28,10 @@
 import android.telephony.Annotation.CallState;
 import android.telephony.Annotation.DataActivityType;
 import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.DisconnectCauses;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.PreciseCallStates;
+import android.telephony.Annotation.PreciseDisconnectCauses;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
 import android.telephony.Annotation.SrvccState;
@@ -56,6 +58,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public class TelephonyRegistryManager {
 
     private static final String TAG = "TelephonyRegistryManager";
@@ -225,11 +228,9 @@
      * invalid.
      * @param state latest call state. e.g, offhook, ringing
      * @param incomingNumber incoming phone number.
-     *
-     * @hide
      */
     public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state,
-            String incomingNumber) {
+            @Nullable String incomingNumber) {
         try {
             sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
         } catch (RemoteException ex) {
@@ -263,10 +264,8 @@
      * @param slotIndex for which the service state changed. Can be derived from subId except
      * subId is invalid.
      * @param state service state e.g, in service, out of service or roaming status.
-     *
-     * @hide
      */
-    public void notifyServiceStateChanged(int subId, int slotIndex, ServiceState state) {
+    public void notifyServiceStateChanged(int subId, int slotIndex, @NonNull ServiceState state) {
         try {
             sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
         } catch (RemoteException ex) {
@@ -281,11 +280,9 @@
      * @param slotIndex for which the signalstrength changed. Can be derived from subId except when
      * subId is invalid.
      * @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()}
-     *
-     * @hide
      */
     public void notifySignalStrengthChanged(int subId, int slotIndex,
-        SignalStrength signalStrength) {
+            @NonNull SignalStrength signalStrength) {
         try {
             sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
         } catch (RemoteException ex) {
@@ -302,8 +299,6 @@
      * except when subId is invalid.
      * @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false}
      * otherwise.
-     *
-     * @hide
      */
     public void notifyMessageWaitingChanged(int subId, int slotIndex, boolean msgWaitingInd) {
         try {
@@ -319,8 +314,6 @@
      * @param subId for which call forwarding status changed.
      * @param callForwardInd {@code true} indicates there is call forwarding, {@code false}
      * otherwise.
-     *
-     * @hide
      */
     public void notifyCallForwardingChanged(int subId, boolean callForwardInd) {
         try {
@@ -336,8 +329,6 @@
      * @param subId for which data activity state changed.
      * @param dataActivityType indicates the latest data activity type e.g, {@link
      * TelephonyManager#DATA_ACTIVITY_IN}
-     *
-     * @hide
      */
     public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) {
         try {
@@ -358,10 +349,9 @@
      *
      * @see android.telephony.PreciseDataConnection
      * @see TelephonyManager#DATA_DISCONNECTED
-     * @hide
      */
     public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
-            @ApnType int apnType, PreciseDataConnectionState preciseState) {
+            @ApnType int apnType, @Nullable PreciseDataConnectionState preciseState) {
         try {
             sRegistry.notifyDataConnectionForSubscriber(
                     slotIndex, subId, apnType, preciseState);
@@ -378,10 +368,8 @@
      * subId is invalid.
      * @param callQuality Information about call quality e.g, call quality level
      * @param networkType associated with this data connection. e.g, LTE
-     *
-     * @hide
      */
-    public void notifyCallQualityChanged(int subId, int slotIndex, CallQuality callQuality,
+    public void notifyCallQualityChanged(int subId, int slotIndex, @NonNull CallQuality callQuality,
         @NetworkType int networkType) {
         try {
             sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
@@ -396,8 +384,6 @@
      * @param subId for which emergency number list changed.
      * @param slotIndex for which emergency number list changed. Can be derived from subId except
      * when subId is invalid.
-     *
-     * @hide
      */
     public void notifyEmergencyNumberList(int subId, int slotIndex) {
         try {
@@ -414,8 +400,6 @@
      * @param slotIndex for which radio power state changed. Can be derived from subId except when
      * subId is invalid.
      * @param radioPowerState the current modem radio state.
-     *
-     * @hide
      */
     public void notifyRadioPowerStateChanged(int subId, int slotIndex,
         @RadioPowerState int radioPowerState) {
@@ -430,10 +414,8 @@
      * Notify {@link PhoneCapability} changed.
      *
      * @param phoneCapability the capability of the modem group.
-     *
-     * @hide
      */
-    public void notifyPhoneCapabilityChanged(PhoneCapability phoneCapability) {
+    public void notifyPhoneCapabilityChanged(@NonNull PhoneCapability phoneCapability) {
         try {
             sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
         } catch (RemoteException ex) {
@@ -462,8 +444,6 @@
      * @param slotIndex for which data activation state changed. Can be derived from subId except
      * when subId is invalid.
      * @param activationState sim activation state e.g, activated.
-     *
-     * @hide
      */
     public void notifyDataActivationStateChanged(int subId, int slotIndex,
         @SimActivationState int activationState) {
@@ -483,8 +463,6 @@
      * @param slotIndex for which voice activation state changed. Can be derived from subId except
      * subId is invalid.
      * @param activationState sim activation state e.g, activated.
-     *
-     * @hide
      */
     public void notifyVoiceActivationStateChanged(int subId, int slotIndex,
         @SimActivationState int activationState) {
@@ -504,8 +482,6 @@
      * @param slotIndex for which mobile data state has changed. Can be derived from subId except
      * when subId is invalid.
      * @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise.
-     *
-     * @hide
      */
     public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) {
         try {
@@ -516,31 +492,12 @@
     }
 
     /**
-     * TODO: this is marked as deprecated, can we move this one safely?
-     *
-     * @param subId
-     * @param slotIndex
-     * @param rawData
-     *
-     * @hide
-     */
-    public void notifyOemHookRawEventForSubscriber(int subId, int slotIndex, byte[] rawData) {
-        try {
-            sRegistry.notifyOemHookRawEventForSubscriber(slotIndex, subId, rawData);
-        } catch (RemoteException ex) {
-            // system process is dead
-        }
-    }
-
-    /**
      * Notify IMS call disconnect causes which contains {@link android.telephony.ims.ImsReasonInfo}.
      *
      * @param subId for which ims call disconnect.
      * @param imsReasonInfo the reason for ims call disconnect.
-     *
-     * @hide
      */
-    public void notifyImsDisconnectCause(int subId, ImsReasonInfo imsReasonInfo) {
+    public void notifyImsDisconnectCause(int subId, @NonNull ImsReasonInfo imsReasonInfo) {
         try {
             sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
         } catch (RemoteException ex) {
@@ -557,11 +514,9 @@
      * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
      * @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
      * @param failCause data fail cause.
-     *
-     * @hide
      */
     public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, @ApnType int apnType,
-        String apn, @DataFailureCause int failCause) {
+            @Nullable String apn, @DataFailureCause int failCause) {
         try {
             sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause);
         } catch (RemoteException ex) {
@@ -575,8 +530,6 @@
      *
      * @param subId for which srvcc state changed.
      * @param state srvcc state
-     *
-     * @hide
      */
     public void notifySrvccStateChanged(int subId, @SrvccState int state) {
         try {
@@ -596,8 +549,6 @@
      * @param ringCallPreciseState ringCall state.
      * @param foregroundCallPreciseState foreground call state.
      * @param backgroundCallPreciseState background call state.
-     *
-     * @hide
      */
     public void notifyPreciseCallState(int subId, int slotIndex,
             @PreciseCallStates int ringCallPreciseState,
@@ -621,10 +572,9 @@
      * @param cause {@link DisconnectCause} for the disconnected call.
      * @param preciseCause {@link android.telephony.PreciseDisconnectCause} for the disconnected
      * call.
-     *
-     * @hide
      */
-    public void notifyDisconnectCause(int slotIndex, int subId, int cause, int preciseCause) {
+    public void notifyDisconnectCause(int slotIndex, int subId, @DisconnectCauses int cause,
+            @PreciseDisconnectCauses int preciseCause) {
         try {
             sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
         } catch (RemoteException ex) {
@@ -637,10 +587,8 @@
      *
      * <p>To be compatible with {@link TelephonyRegistry}, use {@link CellIdentity} which is
      * parcelable, and convert to CellLocation in client code.
-     *
-     * @hide
      */
-    public void notifyCellLocation(int subId, CellIdentity cellLocation) {
+    public void notifyCellLocation(int subId, @NonNull CellIdentity cellLocation) {
         try {
             sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
         } catch (RemoteException ex) {
@@ -654,10 +602,8 @@
      *
      * @param subId for which cellinfo changed.
      * @param cellInfo A list of cellInfo associated with the given subscription.
-     *
-     * @hide
      */
-    public void notifyCellInfoChanged(int subId, List<CellInfo> cellInfo) {
+    public void notifyCellInfoChanged(int subId, @NonNull List<CellInfo> cellInfo) {
         try {
             sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
         } catch (RemoteException ex) {
@@ -666,8 +612,8 @@
     }
 
     /**
-     * @param activeDataSubId
-     * @hide
+     * Notify that the active data subscription ID has changed.
+     * @param activeDataSubId The new subscription ID for active data
      */
     public void notifyActiveDataSubIdChanged(int activeDataSubId) {
         try {
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 07d0d7d..f608958 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -22,6 +22,7 @@
 import android.annotation.StyleRes;
 import android.app.Notification;
 import android.app.Person;
+import android.app.RemoteInputHistoryItem;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -161,8 +162,9 @@
         if (headerText != null) {
             mConversationTitle = headerText.getText();
         }
-        addRemoteInputHistoryToMessages(newMessages,
-                extras.getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY));
+        RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[])
+                extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+        addRemoteInputHistoryToMessages(newMessages, history);
         boolean showSpinner =
                 extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
         bind(newMessages, newHistoricMessages, showSpinner);
@@ -175,14 +177,18 @@
 
     private void addRemoteInputHistoryToMessages(
             List<Notification.MessagingStyle.Message> newMessages,
-            CharSequence[] remoteInputHistory) {
+            RemoteInputHistoryItem[] remoteInputHistory) {
         if (remoteInputHistory == null || remoteInputHistory.length == 0) {
             return;
         }
         for (int i = remoteInputHistory.length - 1; i >= 0; i--) {
-            CharSequence message = remoteInputHistory[i];
-            newMessages.add(new Notification.MessagingStyle.Message(
-                    message, 0, (Person) null, true /* remoteHistory */));
+            RemoteInputHistoryItem historyMessage = remoteInputHistory[i];
+            Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message(
+                    historyMessage.getText(), 0, (Person) null, true /* remoteHistory */);
+            if (historyMessage.getUri() != null) {
+                message.setData(historyMessage.getMimeType(), historyMessage.getUri());
+            }
+            newMessages.add(message);
         }
     }
 
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 5937283..32a7cf3 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -243,7 +243,8 @@
     return localBitmap.valid() ? &localBitmap->bitmap() : nullptr;
 }
 
-SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes) {
+SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes,
+                                       bool* isHardware) {
     SkASSERT(env);
     SkASSERT(bitmap);
     SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
@@ -252,6 +253,9 @@
     if (outRowBytes) {
         *outRowBytes = localBitmap->rowBytes();
     }
+    if (isHardware) {
+        *isHardware = localBitmap->isHardware();
+    }
     return localBitmap->info();
 }
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 99034ed..1e49765 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -62,7 +62,8 @@
 
     static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
     static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
-    static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes);
+    static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes,
+                                     bool* isHardware);
     static SkRegion* getNativeRegion(JNIEnv*, jobject region);
 
     /*
diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp
index 3c7691b..6628529 100644
--- a/core/jni/android/graphics/apex/android_bitmap.cpp
+++ b/core/jni/android/graphics/apex/android_bitmap.cpp
@@ -78,7 +78,7 @@
     }
 }
 
-static uint32_t getInfoFlags(const SkImageInfo& info) {
+static uint32_t getAlphaFlags(const SkImageInfo& info) {
     switch (info.alphaType()) {
         case kUnknown_SkAlphaType:
             LOG_ALWAYS_FATAL("Bitmap has no alpha type");
@@ -92,6 +92,14 @@
     }
 }
 
+static uint32_t getInfoFlags(const SkImageInfo& info, bool isHardware) {
+    uint32_t flags = getAlphaFlags(info);
+    if (isHardware) {
+        flags |= ANDROID_BITMAP_FLAGS_IS_HARDWARE;
+    }
+    return flags;
+}
+
 ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) {
     SkColorType dstColorType = getColorType(dstFormat);
     if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) {
@@ -108,19 +116,19 @@
     return nullptr;
 }
 
-static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes) {
+static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes, bool isHardware) {
     AndroidBitmapInfo info;
     info.width = imageInfo.width();
     info.height = imageInfo.height();
     info.stride = rowBytes;
     info.format = getFormat(imageInfo);
-    info.flags = getInfoFlags(imageInfo);
+    info.flags = getInfoFlags(imageInfo, isHardware);
     return info;
 }
 
 AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) {
     Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
-    return getInfo(bitmap->info(), bitmap->rowBytes());
+    return getInfo(bitmap->info(), bitmap->rowBytes(), bitmap->isHardware());
 }
 
 ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) {
@@ -131,8 +139,9 @@
 
 AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) {
     uint32_t rowBytes = 0;
-    SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes);
-    return getInfo(imageInfo, rowBytes);
+    bool isHardware = false;
+    SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes, &isHardware);
+    return getInfo(imageInfo, rowBytes, isHardware);
 }
 
 void* ABitmap_getPixels(ABitmap* bitmapHandle) {
@@ -175,7 +184,7 @@
 
 class CompressWriter : public SkWStream {
 public:
-    CompressWriter(void* userContext, AndroidBitmap_compress_write_fn fn)
+    CompressWriter(void* userContext, AndroidBitmap_CompressWriteFunc fn)
           : mUserContext(userContext), mFn(fn), mBytesWritten(0) {}
 
     bool write(const void* buffer, size_t size) override {
@@ -190,7 +199,7 @@
 
 private:
     void* mUserContext;
-    AndroidBitmap_compress_write_fn mFn;
+    AndroidBitmap_CompressWriteFunc mFn;
     size_t mBytesWritten;
 };
 
@@ -198,7 +207,7 @@
 
 int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels,
                      AndroidBitmapCompressFormat inFormat, int32_t quality, void* userContext,
-                     AndroidBitmap_compress_write_fn fn) {
+                     AndroidBitmap_CompressWriteFunc fn) {
     Bitmap::JavaCompressFormat format;
     switch (inFormat) {
         case ANDROID_BITMAP_COMPRESS_FORMAT_JPEG:
@@ -290,3 +299,12 @@
             return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
     }
 }
+
+AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+    AHardwareBuffer* buffer = bitmap->hardwareBuffer();
+    if (buffer) {
+        AHardwareBuffer_acquire(buffer);
+    }
+    return buffer;
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
index 683851d..45fec2ab 100644
--- a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
+++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
@@ -21,6 +21,8 @@
 #include <jni.h>
 #include <sys/cdefs.h>
 
+struct AHardwareBuffer;
+
 __BEGIN_DECLS
 
 /**
@@ -61,7 +63,19 @@
 // NDK access
 int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels,
                      AndroidBitmapCompressFormat format, int32_t quality, void* userContext,
-                     AndroidBitmap_compress_write_fn);
+                     AndroidBitmap_CompressWriteFunc);
+/**
+ *  Retrieve the native object associated with a HARDWARE Bitmap.
+ *
+ *  Client must not modify it while a Bitmap is wrapping it.
+ *
+ *  @param bitmap Handle to an android.graphics.Bitmap.
+ *  @return on success, a pointer to the
+ *         AHardwareBuffer associated with bitmap. This acquires
+ *         a reference on the buffer, and the client must call
+ *         AHardwareBuffer_release when finished with it.
+ */
+AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap);
 
 __END_DECLS
 
@@ -116,6 +130,7 @@
         ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); }
         void* getPixels() const { return ABitmap_getPixels(mBitmap); }
         void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); }
+        AHardwareBuffer* getHardwareBuffer() const { return ABitmap_getHardwareBuffer(mBitmap); }
 
     private:
         // takes ownership of the provided ABitmap
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 041019e..a888b43 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1355,6 +1355,71 @@
     lpTrack->setParameters(param.toString());
 }
 
+static jint android_media_AudioTrack_setAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz,
+                                                                   jfloat level) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == nullptr) {
+        jniThrowException(env, "java/lang/IllegalStateException", "AudioTrack not initialized");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
+    return (jint)AUDIO_JAVA_ERROR;
+}
+
+static jint android_media_AudioTrack_getAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz,
+                                                                   jfloatArray level) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == nullptr) {
+        ALOGE("%s: AudioTrack not initialized", __func__);
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    jfloat *nativeLevel = (jfloat *)env->GetPrimitiveArrayCritical(level, NULL);
+    if (nativeLevel == nullptr) {
+        ALOGE("%s: Cannot retrieve level pointer", __func__);
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
+    // By contract we can return -infinity if unsupported.
+    *nativeLevel = -std::numeric_limits<float>::infinity();
+    env->ReleasePrimitiveArrayCritical(level, nativeLevel, 0 /* mode */);
+    nativeLevel = nullptr;
+    return (jint)AUDIO_JAVA_SUCCESS;
+}
+
+static jint android_media_AudioTrack_setDualMonoMode(JNIEnv *env, jobject thiz, jint dualMonoMode) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == nullptr) {
+        jniThrowException(env, "java/lang/IllegalStateException", "AudioTrack not initialized");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
+    return (jint)AUDIO_JAVA_ERROR;
+}
+
+static jint android_media_AudioTrack_getDualMonoMode(JNIEnv *env, jobject thiz,
+                                                     jintArray dualMonoMode) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == nullptr) {
+        ALOGE("%s: AudioTrack not initialized", __func__);
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    jfloat *nativeDualMonoMode = (jfloat *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL);
+    if (nativeDualMonoMode == nullptr) {
+        ALOGE("%s: Cannot retrieve dualMonoMode pointer", __func__);
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    // TODO: replace in r-dev or r-tv-dev with code if HW is able to select dual mono mode.
+    // By contract we can return DUAL_MONO_MODE_OFF if unsupported.
+    *nativeDualMonoMode = 0; // DUAL_MONO_MODE_OFF for now.
+    env->ReleasePrimitiveArrayCritical(dualMonoMode, nativeDualMonoMode, 0 /* mode */);
+    nativeDualMonoMode = nullptr;
+    return (jint)AUDIO_JAVA_SUCCESS;
+}
+
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
@@ -1425,6 +1490,12 @@
         {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
         {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
         {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
+        {"native_set_audio_description_mix_level_db", "(F)I",
+         (void *)android_media_AudioTrack_setAudioDescriptionMixLeveldB},
+        {"native_get_audio_description_mix_level_db", "([F)I",
+         (void *)android_media_AudioTrack_getAudioDescriptionMixLeveldB},
+        {"native_set_dual_mono_mode", "(I)I", (void *)android_media_AudioTrack_setDualMonoMode},
+        {"native_get_dual_mono_mode", "([I)I", (void *)android_media_AudioTrack_getDualMonoMode},
 };
 
 // field names found in android/media/AudioTrack.java
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index f702b3e..d2bf205 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -34,4 +34,5 @@
     STOP_REASON_TIMEOUT = 3;
     STOP_REASON_DEVICE_IDLE = 4;
     STOP_REASON_DEVICE_THERMAL = 5;
+    STOP_REASON_RESTRAINED = 6;
 }
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index b71e539..303d62d 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -282,6 +282,10 @@
         // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
         // WINDOW_SIZE_MS.
         optional int64 rare_window_size_ms = 6;
+        // The quota window size of the particular standby bucket. Apps in this standby bucket are
+        // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+        // WINDOW_SIZE_MS.
+        optional int64 restricted_window_size_ms = 20;
         // The maximum amount of time an app can have its jobs running within a 24 hour window.
         optional int64 max_execution_time_ms = 7;
         // The maximum number of jobs an app can run within this particular standby bucket's
@@ -296,6 +300,9 @@
         // The maximum number of jobs an app can run within this particular standby bucket's
         // window size.
         optional int32 max_job_count_rare = 11;
+        // The maximum number of jobs an app can run within this particular standby bucket's
+        // window size.
+        optional int32 max_job_count_restricted = 21;
         // The period of time used to rate limit recently run jobs.
         optional int32 rate_limiting_window_ms = 19;
         // The maximum number of jobs that should be allowed to run in the past
@@ -313,12 +320,17 @@
         // The maximum number of timing sessions an app can run within this particular standby
         // bucket's window size.
         optional int32 max_session_count_rare = 16;
+        // The maximum number of timing sessions an app can run within this particular standby
+        // bucket's window size.
+        optional int32 max_session_count_restricted = 22;
         // The maximum number of timing sessions that should be allowed to run in the past
         // rate_limiting_window_ms.
         optional int32 max_session_count_per_rate_limiting_window = 17;
         // Treat two distinct {@link TimingSession}s as the same if they start and end within this
         // amount of time of each other.
         optional int64 timing_session_coalescing_duration_ms = 18;
+
+        // Next tag: 23
     }
     optional QuotaController quota_controller = 24;
 
@@ -943,6 +955,9 @@
     optional JobInfo job_info = 6;
 
     repeated ConstraintEnum required_constraints = 7;
+    // Dynamic constraints are additional constraints imposed by the system that MUST be met before
+    // the app can run if the app does not have quota.
+    repeated ConstraintEnum dynamic_constraints = 31;
     repeated ConstraintEnum satisfied_constraints = 8;
     repeated ConstraintEnum unsatisfied_constraints = 9;
     optional bool is_doze_whitelisted = 10;
@@ -956,6 +971,8 @@
         // Battery Saver). This implicit constraint must be satisfied for the
         // job to run.
         optional bool is_not_restricted_in_bg = 2;
+        // True if dynamic constraints have been satisfied.
+        optional bool is_dynamic_satisfied = 3;
     }
     optional ImplicitConstraints implicit_constraints = 25;
 
@@ -998,6 +1015,7 @@
         FREQUENT = 2;
         RARE = 3;
         NEVER = 4;
+        RESTRICTED = 5;
     }
     optional Bucket standby_bucket = 17;
     optional bool is_exempted_from_app_standby = 27;
@@ -1028,7 +1046,7 @@
     // was no attempt.
     optional int64 time_since_first_force_batch_attempt_ms = 29;
 
-    // Next tag: 31
+    // Next tag: 32
 }
 
 // Dump from com.android.server.job.JobConcurrencyManager.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6a00ecb..bed418d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5315,4 +5315,8 @@
 
     <!-- Accessibility description of caption view -->
     <string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
+
+    <!-- Text to tell the user that a package has been forced by themselves in the RESTRICTED bucket. [CHAR LIMIT=NONE] -->
+    <string name="as_app_forced_to_restricted_bucket">
+        <xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e6eb5d..357b1f0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3810,6 +3810,9 @@
   <java-symbol type="string" name="config_rawContactsLocalAccountName" />
   <java-symbol type="string" name="config_rawContactsLocalAccountType" />
 
+  <!-- For App Standby -->
+  <java-symbol type="string" name="as_app_forced_to_restricted_bucket" />
+
   <!-- Assistant handles -->
   <java-symbol type="dimen" name="assist_handle_shadow_radius" />
 
diff --git a/packages/WindowManager/OWNERS b/libs/WindowManager/OWNERS
similarity index 100%
rename from packages/WindowManager/OWNERS
rename to libs/WindowManager/OWNERS
diff --git a/packages/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
similarity index 100%
rename from packages/WindowManager/Shell/Android.bp
rename to libs/WindowManager/Shell/Android.bp
diff --git a/packages/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
similarity index 100%
rename from packages/WindowManager/Shell/AndroidManifest.xml
rename to libs/WindowManager/Shell/AndroidManifest.xml
diff --git a/packages/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
similarity index 100%
rename from packages/WindowManager/Shell/OWNERS
rename to libs/WindowManager/Shell/OWNERS
diff --git a/packages/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
similarity index 100%
rename from packages/WindowManager/Shell/res/values/config.xml
rename to libs/WindowManager/Shell/res/values/config.xml
diff --git a/packages/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
similarity index 100%
rename from packages/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
diff --git a/packages/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp
similarity index 100%
rename from packages/WindowManager/Shell/tests/Android.bp
rename to libs/WindowManager/Shell/tests/Android.bp
diff --git a/packages/WindowManager/Shell/tests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/AndroidManifest.xml
similarity index 100%
rename from packages/WindowManager/Shell/tests/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/AndroidManifest.xml
diff --git a/packages/WindowManager/Shell/tests/AndroidTest.xml b/libs/WindowManager/Shell/tests/AndroidTest.xml
similarity index 100%
rename from packages/WindowManager/Shell/tests/AndroidTest.xml
rename to libs/WindowManager/Shell/tests/AndroidTest.xml
diff --git a/packages/WindowManager/Shell/tests/res/values/config.xml b/libs/WindowManager/Shell/tests/res/values/config.xml
similarity index 100%
rename from packages/WindowManager/Shell/tests/res/values/config.xml
rename to libs/WindowManager/Shell/tests/res/values/config.xml
diff --git a/packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
similarity index 100%
rename from packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
rename to libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index a6c4e9d..4b2857f 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -31,7 +31,7 @@
     , mDecodeSize(mTargetSize)
     , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
     , mUnpremultipliedRequired(false)
-    , mOutColorSpace(mCodec->getInfo().refColorSpace())
+    , mOutColorSpace(mCodec->computeOutputColorSpace(mOutColorType, nullptr))
     , mSampleSize(1)
 {
 }
@@ -111,7 +111,6 @@
             if (!gray()) {
                 return false;
             }
-            mOutColorSpace = nullptr;
             break;
         case kN32_SkColorType:
             break;
@@ -137,9 +136,15 @@
     mOutColorSpace = std::move(colorSpace);
 }
 
+sk_sp<SkColorSpace> ImageDecoder::getOutputColorSpace() const {
+    // kGray_8 is used for ALPHA_8, which ignores the color space.
+    return mOutColorType == kGray_8_SkColorType ? nullptr : mOutColorSpace;
+}
+
+
 SkImageInfo ImageDecoder::getOutputInfo() const {
     SkISize size = mCropRect ? mCropRect->size() : mTargetSize;
-    return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), mOutColorSpace);
+    return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace());
 }
 
 bool ImageDecoder::opaque() const {
@@ -154,7 +159,7 @@
     void* decodePixels = pixels;
     size_t decodeRowBytes = rowBytes;
     auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(),
-                                        mOutColorSpace);
+                                        getOutputColorSpace());
     // Used if we need a temporary before scaling or subsetting.
     // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
     SkBitmap tmp;
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index 96f97e5..0c99f84 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -66,6 +66,7 @@
     ImageDecoder& operator=(const ImageDecoder&) = delete;
 
     SkAlphaType getOutAlphaType() const;
+    sk_sp<SkColorSpace> getOutputColorSpace() const;
 };
 
 } // namespace android
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index a83db3f..ca0bfb1 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -16,9 +16,9 @@
 
 package android.location;
 
-import android.annotation.TestApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -39,7 +39,8 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({TYPE_UNKNOWN, TYPE_GPS_L1CA, TYPE_GPS_L2CNAV, TYPE_GPS_L5CNAV, TYPE_GPS_CNAV2,
-        TYPE_GLO_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_GAL_I, TYPE_GAL_F})
+            TYPE_SBS, TYPE_GLO_L1CA, TYPE_QZS_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_BDS_CNAV1,
+            TYPE_BDS_CNAV2, TYPE_GAL_I, TYPE_GAL_F, TYPE_IRN_L5CA})
     public @interface GnssNavigationMessageType {}
 
     // The following enumerations must be in sync with the values declared in gps.h
@@ -54,16 +55,26 @@
     public static final int TYPE_GPS_L5CNAV = 0x0103;
     /** GPS CNAV-2 message contained in the structure. */
     public static final int TYPE_GPS_CNAV2 = 0x0104;
+    /** SBAS message contained in the structure. */
+    public static final int TYPE_SBS = 0x0201;
     /** Glonass L1 CA message contained in the structure. */
     public static final int TYPE_GLO_L1CA = 0x0301;
+    /** QZSS L1 C/A message contained in the structure. */
+    public static final int TYPE_QZS_L1CA = 0x0401;
     /** Beidou D1 message contained in the structure. */
     public static final int TYPE_BDS_D1 = 0x0501;
     /** Beidou D2 message contained in the structure. */
     public static final int TYPE_BDS_D2 = 0x0502;
+    /** Beidou CNAV1 message contained in the structure. */
+    public static final int TYPE_BDS_CNAV1 = 0x0503;
+    /** Beidou CNAV2 message contained in the structure. */
+    public static final int TYPE_BDS_CNAV2 = 0x0504;
     /** Galileo I/NAV message contained in the structure. */
     public static final int TYPE_GAL_I = 0x0601;
     /** Galileo F/NAV message contained in the structure. */
     public static final int TYPE_GAL_F = 0x0602;
+    /** IRNSS L5 C/A message contained in the structure. */
+    public static final int TYPE_IRN_L5CA = 0x0701;
 
     /**
      * The Navigation Message Status is 'unknown'.
@@ -199,16 +210,26 @@
                 return "GPS L5-CNAV";
             case TYPE_GPS_CNAV2:
                 return "GPS CNAV2";
+            case TYPE_SBS:
+                return "SBS";
             case TYPE_GLO_L1CA:
                 return "Glonass L1 C/A";
+            case TYPE_QZS_L1CA:
+                return "QZSS L1 C/A";
             case TYPE_BDS_D1:
                 return "Beidou D1";
             case TYPE_BDS_D2:
                 return "Beidou D2";
+            case TYPE_BDS_CNAV1:
+                return "Beidou CNAV1";
+            case TYPE_BDS_CNAV2:
+                return "Beidou CNAV2";
             case TYPE_GAL_I:
                 return "Galileo I";
             case TYPE_GAL_F:
                 return "Galileo F";
+            case TYPE_IRN_L5CA:
+                return "IRNSS L5 C/A";
             default:
                 return "<Invalid:" + mType + ">";
         }
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index f566f64..1f61591 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -256,6 +256,71 @@
      */
     public static final int ENCAPSULATION_MODE_HANDLE = 2;
 
+    /* Dual Mono handling is used when a stereo audio stream
+     * contains separate audio content on the left and right channels.
+     * Such information about the content of the stream may be found, for example, in
+     * ITU T-REC-J.94-201610 A.6.2.3 Component descriptor.
+     */
+    /** @hide */
+    @IntDef({
+        DUAL_MONO_MODE_OFF,
+        DUAL_MONO_MODE_LR,
+        DUAL_MONO_MODE_LL,
+        DUAL_MONO_MODE_RR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DualMonoMode {}
+    // Important: The DUAL_MONO_MODE values must be kept in sync with native header files.
+    /**
+     * This mode disables any Dual Mono presentation effect.
+     *
+     */
+    public static final int DUAL_MONO_MODE_OFF = 0;
+
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the left and right audio channels blended together
+     * and delivered to both channels.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that the left-right stereo symmetric
+     * channels are pairwise blended;
+     * the other channels such as center are left alone.
+     *
+     * The Dual Mono effect occurs before volume scaling.
+     */
+    public static final int DUAL_MONO_MODE_LR = 1;
+
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the left audio channel replicated into the right audio channel.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that all channels with left-right
+     * stereo symmetry will have the left channel position replicated
+     * into the right channel position.
+     * The center channels (with no left/right symmetry) or unbalanced
+     * channels are left alone.
+     *
+     * The Dual Mono effect occurs before volume scaling.
+     */
+    public static final int DUAL_MONO_MODE_LL = 2;
+
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the right audio channel replicated into the left audio channel.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that all channels with left-right
+     * stereo symmetry will have the right channel position replicated
+     * into the left channel position.
+     * The center channels (with no left/right symmetry) or unbalanced
+     * channels are left alone.
+     *
+     * The Dual Mono effect occurs before volume scaling.
+     */
+    public static final int DUAL_MONO_MODE_RR = 3;
+
     /** @hide */
     @IntDef({
         WRITE_BLOCKING,
@@ -1355,6 +1420,140 @@
                 attributes.getContentType(), attributes.getUsage(), attributes.getFlags());
     }
 
+    /*
+     * The MAX_LEVEL should be exactly representable by an IEEE 754-2008 base32 float.
+     * This means fractions must be divisible by a power of 2. For example,
+     * 10.25f is OK as 0.25 is 1/4, but 10.1f is NOT OK as 1/10 is not expressable by
+     * a finite binary fraction.
+     *
+     * 48.f is the nominal max for API level {@link android os.Build.VERSION_CODES#R}.
+     * We use this to suggest a baseline range for implementation.
+     *
+     * The API contract specification allows increasing this value in a future
+     * API release, but not decreasing this value.
+     */
+    private static final float MAX_AUDIO_DESCRIPTION_MIX_LEVEL = 48.f;
+
+    private static boolean isValidAudioDescriptionMixLevel(float level) {
+        return !(Float.isNaN(level) || level > MAX_AUDIO_DESCRIPTION_MIX_LEVEL);
+    }
+
+    /**
+     * Sets the Audio Description mix level in dB.
+     *
+     * For AudioTracks incorporating a secondary Audio Description stream
+     * (where such contents may be sent through an Encapsulation Mode
+     * {@link #ENCAPSULATION_MODE_ELEMENTARY_STREAM} or {@link #ENCAPSULATION_MODE_HANDLE}
+     * or internally by a HW channel),
+     * the level of mixing of the Audio Description to the Main Audio stream
+     * is controlled by this method.
+     *
+     * Such mixing occurs <strong>prior</strong> to overall volume scaling.
+     *
+     * @param level a floating point value between
+     *     {@code Float.NEGATIVE_INFINITY} to {@code +48.f},
+     *     where {@code Float.NEGATIVE_INFINITY} means the Audio Description is not mixed
+     *     and a level of {@code 0.f} means the Audio Description is mixed without scaling.
+     * @return true on success, false on failure.
+     */
+    public boolean setAudioDescriptionMixLeveldB(
+            @FloatRange(to = 48.f, toInclusive = true) float level) {
+        if (!isValidAudioDescriptionMixLevel(level)) {
+            throw new IllegalArgumentException("level is out of range" + level);
+        }
+        return native_set_audio_description_mix_level_db(level) == SUCCESS;
+    }
+
+    /**
+     * Returns the Audio Description mix level in dB.
+     *
+     * If Audio Description mixing is unavailable from the hardware device,
+     * a value of {@code Float.NEGATIVE_INFINITY} is returned.
+     *
+     * @return the current Audio Description Mix Level in dB.
+     *     A value of {@code Float.NEGATIVE_INFINITY} means
+     *     that the audio description is not mixed or
+     *     the hardware is not available.
+     *     This should reflect the <strong>true</strong> internal device mix level;
+     *     hence the application might receive any floating value
+     *     except {@code Float.NaN}.
+     */
+    public float getAudioDescriptionMixLeveldB() {
+        float[] level = { Float.NEGATIVE_INFINITY };
+        try {
+            final int status = native_get_audio_description_mix_level_db(level);
+            if (status != SUCCESS || Float.isNaN(level[0])) {
+                return Float.NEGATIVE_INFINITY;
+            }
+        } catch (Exception e) {
+            return Float.NEGATIVE_INFINITY;
+        }
+        return level[0];
+    }
+
+    private static boolean isValidDualMonoMode(@DualMonoMode int dualMonoMode) {
+        switch (dualMonoMode) {
+            case DUAL_MONO_MODE_OFF:
+            case DUAL_MONO_MODE_LR:
+            case DUAL_MONO_MODE_LL:
+            case DUAL_MONO_MODE_RR:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Sets the Dual Mono mode presentation on the output device.
+     *
+     * The Dual Mono mode is generally applied to stereo audio streams
+     * where the left and right channels come from separate sources.
+     *
+     * For compressed audio, where the decoding is done in hardware,
+     * Dual Mono presentation needs to be performed
+     * by the hardware output device
+     * as the PCM audio is not available to the framework.
+     *
+     * @param dualMonoMode one of {@link #DUAL_MONO_MODE_OFF},
+     *     {@link #DUAL_MONO_MODE_LR},
+     *     {@link #DUAL_MONO_MODE_LL},
+     *     {@link #DUAL_MONO_MODE_RR}.
+     *
+     * @return true on success, false on failure if the output device
+     *     does not support Dual Mono mode.
+     */
+    public boolean setDualMonoMode(@DualMonoMode int dualMonoMode) {
+        if (!isValidDualMonoMode(dualMonoMode)) {
+            throw new IllegalArgumentException(
+                    "Invalid Dual Mono mode " + dualMonoMode);
+        }
+        return native_set_dual_mono_mode(dualMonoMode) == SUCCESS;
+    }
+
+    /**
+     * Returns the Dual Mono mode presentation setting.
+     *
+     * If no Dual Mono presentation is available for the output device,
+     * then {@link #DUAL_MONO_MODE_OFF} is returned.
+     *
+     * @return one of {@link #DUAL_MONO_MODE_OFF},
+     *     {@link #DUAL_MONO_MODE_LR},
+     *     {@link #DUAL_MONO_MODE_LL},
+     *     {@link #DUAL_MONO_MODE_RR}.
+     */
+    public @DualMonoMode int getDualMonoMode() {
+        int[] dualMonoMode = { DUAL_MONO_MODE_OFF };
+        try {
+            final int status = native_get_dual_mono_mode(dualMonoMode);
+            if (status != SUCCESS || !isValidDualMonoMode(dualMonoMode[0])) {
+                return DUAL_MONO_MODE_OFF;
+            }
+        } catch (Exception e) {
+            return DUAL_MONO_MODE_OFF;
+        }
+        return dualMonoMode[0];
+    }
+
     // mask of all the positional channels supported, however the allowed combinations
     // are further restricted by the matching left/right rule and
     // AudioSystem.OUT_CHANNEL_COUNT_MAX
@@ -3947,6 +4146,11 @@
 
     private native void native_set_delay_padding(int delayInFrames, int paddingInFrames);
 
+    private native int native_set_audio_description_mix_level_db(float level);
+    private native int native_get_audio_description_mix_level_db(float[] level);
+    private native int native_set_dual_mono_mode(int dualMonoMode);
+    private native int native_get_dual_mono_mode(int[] dualMonoMode);
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f780d40..abc7e0b 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -23,6 +23,7 @@
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
+import android.hardware.HardwareBuffer;
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.os.Build;
 import android.os.Bundle;
@@ -39,9 +40,13 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.ReadOnlyBufferException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -1736,7 +1741,25 @@
                 {
                     int index = msg.arg2;
                     synchronized(mBufferLock) {
-                        validateInputByteBuffer(mCachedInputBuffers, index);
+                        switch (mBufferMode) {
+                            case BUFFER_MODE_LEGACY:
+                                validateInputByteBuffer(mCachedInputBuffers, index);
+                                break;
+                            case BUFFER_MODE_BLOCK:
+                                while (mQueueRequests.size() <= index) {
+                                    mQueueRequests.add(null);
+                                }
+                                QueueRequest request = mQueueRequests.get(index);
+                                if (request == null) {
+                                    request = new QueueRequest(mCodec, index);
+                                    mQueueRequests.set(index, request);
+                                }
+                                request.setAccessible(true);
+                                break;
+                            default:
+                                throw new IllegalStateException(
+                                        "Unrecognized buffer mode: " + mBufferMode);
+                        }
                     }
                     mCallback.onInputBufferAvailable(mCodec, index);
                     break;
@@ -1747,7 +1770,26 @@
                     int index = msg.arg2;
                     BufferInfo info = (MediaCodec.BufferInfo) msg.obj;
                     synchronized(mBufferLock) {
-                        validateOutputByteBuffer(mCachedOutputBuffers, index, info);
+                        switch (mBufferMode) {
+                            case BUFFER_MODE_LEGACY:
+                                validateOutputByteBuffer(mCachedOutputBuffers, index, info);
+                                break;
+                            case BUFFER_MODE_BLOCK:
+                                while (mOutputFrames.size() <= index) {
+                                    mOutputFrames.add(null);
+                                }
+                                OutputFrame frame = mOutputFrames.get(index);
+                                if (frame == null) {
+                                    frame = new OutputFrame(index);
+                                    mOutputFrames.set(index, frame);
+                                }
+                                frame.setBufferInfo(info);
+                                frame.setAccessible(true);
+                                break;
+                            default:
+                                throw new IllegalStateException(
+                                        "Unrecognized buffer mode: " + mBufferMode);
+                        }
                     }
                     mCallback.onOutputBufferAvailable(
                             mCodec, index, info);
@@ -1913,8 +1955,33 @@
      */
     public static final int CONFIGURE_FLAG_ENCODE = 1;
 
+    /**
+     * If this codec is to be used with {@link LinearBlock} and/or {@link
+     * GraphicBlock}, pass this flag.
+     * <p>
+     * When this flag is set, the following APIs throw IllegalStateException.
+     * <ul>
+     * <li>{@link #getInputBuffer}
+     * <li>{@link #getInputImage}
+     * <li>{@link #getInputBuffers}
+     * <li>{@link #getOutputBuffer}
+     * <li>{@link #getOutputImage}
+     * <li>{@link #getOutputBuffers}
+     * <li>{@link #queueInputBuffer}
+     * <li>{@link #queueSecureInputBuffer}
+     * <li>{@link #dequeueInputBuffer}
+     * <li>{@link #dequeueOutputBuffer}
+     * </ul>
+     */
+    public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2;
+
     /** @hide */
-    @IntDef(flag = true, value = { CONFIGURE_FLAG_ENCODE })
+    @IntDef(
+        flag = true,
+        value = {
+            CONFIGURE_FLAG_ENCODE,
+            CONFIGURE_FLAG_USE_BLOCK_MODEL,
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ConfigureFlag {}
 
@@ -1984,6 +2051,11 @@
                 descrambler != null ? descrambler.getBinder() : null, flags);
     }
 
+    private static final int BUFFER_MODE_INVALID = -1;
+    private static final int BUFFER_MODE_LEGACY = 0;
+    private static final int BUFFER_MODE_BLOCK = 1;
+    private int mBufferMode = BUFFER_MODE_INVALID;
+
     private void configure(
             @Nullable MediaFormat format, @Nullable Surface surface,
             @Nullable MediaCrypto crypto, @Nullable IHwBinder descramblerBinder,
@@ -2022,6 +2094,13 @@
 
         mHasSurface = surface != null;
         mCrypto = crypto;
+        synchronized (mBufferLock) {
+            if ((flags & CONFIGURE_FLAG_USE_BLOCK_MODEL) != 0) {
+                mBufferMode = BUFFER_MODE_BLOCK;
+            } else {
+                mBufferMode = BUFFER_MODE_LEGACY;
+            }
+        }
 
         native_configure(keys, values, surface, crypto, descramblerBinder, flags);
     }
@@ -2446,6 +2525,9 @@
             int offset, int size, long presentationTimeUs, int flags)
         throws CryptoException {
         synchronized(mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
             invalidateByteBuffer(mCachedInputBuffers, index);
             mDequeuedInputBuffers.remove(index);
         }
@@ -2695,6 +2777,9 @@
             long presentationTimeUs,
             int flags) throws CryptoException {
         synchronized(mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
             invalidateByteBuffer(mCachedInputBuffers, index);
             mDequeuedInputBuffers.remove(index);
         }
@@ -2726,6 +2811,11 @@
      * @throws MediaCodec.CodecException upon codec error.
      */
     public final int dequeueInputBuffer(long timeoutUs) {
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
+        }
         int res = native_dequeueInputBuffer(timeoutUs);
         if (res >= 0) {
             synchronized(mBufferLock) {
@@ -2738,6 +2828,654 @@
     private native final int native_dequeueInputBuffer(long timeoutUs);
 
     /**
+     * Section of memory that represents a linear block. Applications may
+     * acquire a block via {@link LinearBlock#obtain} and queue all or part
+     * of the block as an input buffer to a codec, or get a block allocated by
+     * codec as an output buffer from {@link OutputFrame}.
+     *
+     * {@see QueueRequest#setLinearBlock}
+     * {@see QueueRequest#setEncryptedLinearBlock}
+     * {@see OutputFrame#getLinearBlock}
+     */
+    public static final class LinearBlock {
+        // No public constructors.
+        private LinearBlock() {}
+
+        /**
+         * Returns true if the buffer is mappable.
+         * @throws IllegalStateException if invalid
+         */
+        public boolean isMappable() {
+            synchronized (mLock) {
+                if (!mValid) {
+                    throw new IllegalStateException();
+                }
+                return mMappable;
+            }
+        }
+
+        /**
+         * Map the memory and return the mapped region.
+         * <p>
+         * The returned memory region becomes inaccessible after
+         * {@link #recycle}, or the buffer is queued to the codecs and not
+         * returned to the client yet.
+         *
+         * @return mapped memory region as {@link ByteBuffer} object
+         * @throws IllegalStateException if not mappable or invalid
+         */
+        public @NonNull ByteBuffer map() {
+            synchronized (mLock) {
+                if (!mValid) {
+                    throw new IllegalStateException();
+                }
+                if (!mMappable) {
+                    throw new IllegalStateException();
+                }
+                if (mMapped == null) {
+                    mMapped = native_map();
+                }
+                return mMapped;
+            }
+        }
+
+        private native ByteBuffer native_map();
+
+        /**
+         * Mark this block as ready to be recycled by the framework once it is
+         * no longer in use. All operations to this object after
+         * this call will cause exceptions, as well as attempt to access the
+         * previously mapped memory region. Caller should clear all references
+         * to this object after this call.
+         * <p>
+         * To avoid excessive memory consumption, it is recommended that callers
+         * recycle buffers as soon as they no longer need the buffers
+         *
+         * @throws IllegalStateException if invalid
+         */
+        public void recycle() {
+            synchronized (mLock) {
+                if (!mValid) {
+                    throw new IllegalStateException();
+                }
+                if (mMapped != null) {
+                    mMapped.setAccessible(false);
+                    mMapped = null;
+                }
+                native_recycle();
+                mValid = false;
+                mNativeContext = 0;
+            }
+            sPool.offer(this);
+        }
+
+        private native void native_recycle();
+
+        private native void native_obtain(int capacity, String[] codecNames);
+
+        @Override
+        protected void finalize() {
+            native_recycle();
+        }
+
+        /**
+         * Returns true if it is possible to allocate a linear block that can be
+         * passed to all listed codecs as input buffers without copying the
+         * content.
+         * <p>
+         * Note that even if this function returns true, {@link #obtain} may
+         * still throw due to invalid arguments or allocation failure.
+         *
+         * @param codecNames  list of codecs that the client wants to use a
+         *                    linear block without copying. Null entries are
+         *                    ignored.
+         */
+        public static boolean isCodecCopyFreeCompatible(@NonNull String[] codecNames) {
+            return native_checkCompatible(codecNames);
+        }
+
+        private static native boolean native_checkCompatible(@NonNull String[] codecNames);
+
+        /**
+         * Obtain a linear block object no smaller than {@code capacity}.
+         * If {@link #isCodecCopyFreeCompatible} with the same
+         * {@code codecNames} returned true, the returned
+         * {@link LinearBlock} object can be queued to the listed codecs without
+         * copying. The returned {@link LinearBlock} object is always
+         * read/write mappable.
+         *
+         * @param capacity requested capacity of the linear block in bytes
+         * @param codecNames  list of codecs that the client wants to use this
+         *                    linear block without copying. Null entries are
+         *                    ignored.
+         * @return  a linear block object.
+         * @throws IllegalArgumentException if the capacity is invalid or
+         *                                  codecNames contains invalid name
+         * @throws IOException if an error occurred while allocating a buffer
+         */
+        public static @Nullable LinearBlock obtain(
+                int capacity, @NonNull String[] codecNames) {
+            LinearBlock buffer = sPool.poll();
+            if (buffer == null) {
+                buffer = new LinearBlock();
+            }
+            synchronized (buffer.mLock) {
+                buffer.native_obtain(capacity, codecNames);
+            }
+            return buffer;
+        }
+
+        // Called from native
+        private void setInternalStateLocked(long context, boolean isMappable) {
+            mNativeContext = context;
+            mMappable = isMappable;
+            mValid = (context != 0);
+        }
+
+        private static final BlockingQueue<LinearBlock> sPool =
+                new LinkedBlockingQueue<>();
+
+        private final Object mLock = new Object();
+        private boolean mValid = false;
+        private boolean mMappable = false;
+        private ByteBuffer mMapped = null;
+        private long mNativeContext = 0;
+    }
+
+    /**
+     * Section of memory that represents a graphic block. Applications may
+     * acquire a block via {@link GraphicBlock#obtain} and queue
+     * the block as an input buffer to a codec, or get a block allocated by
+     * codec as an output buffer from {@link OutputFrame}.
+     *
+     * {@see QueueRequest#setGraphicBlock}
+     * {@see OutputFrame#getGraphicBlock}
+     */
+    public static final class GraphicBlock {
+        // No public constructors.
+        private GraphicBlock() {}
+
+        /**
+         * Returns true if the buffer is mappable.
+         * @throws IllegalStateException if invalid
+         */
+        public boolean isMappable() {
+            synchronized (mLock) {
+                if (!mValid) {
+                    throw new IllegalStateException();
+                }
+                return mMappable;
+            }
+        }
+
+        /**
+         * Map the memory and return the mapped region.
+         * <p>
+         * Calling {@link #recycle} or
+         * {@link QueueRequest#setGraphicBlock} causes the returned
+         * {@link Image} object to be closed, if not already.
+         *
+         * @return mapped memory region as {@link Image} object
+         * @throws IllegalStateException if not mappable or invalid
+         */
+        public @NonNull Image map() {
+            synchronized (mLock) {
+                if (!mValid) {
+                    throw new IllegalStateException();
+                }
+                if (!mMappable) {
+                    throw new IllegalStateException();
+                }
+                if (mMapped == null) {
+                    mMapped = native_map();
+                }
+                return mMapped;
+            }
+        }
+
+        private native Image native_map();
+
+        /**
+         * Mark this block as ready to be recycled by the framework once it is
+         * no longer in use. All operations to this object after
+         * this call will cause exceptions, as well as attempt to access the
+         * previously mapped memory region. Caller should clear all references
+         * to this object after this call.
+         * <p>
+         * To avoid excessive memory consumption, it is recommended that callers
+         * recycle buffers as soon as they no longer need the buffers.
+         *
+         * @throws IllegalStateException if invalid
+         */
+        public void recycle() {
+            synchronized (mLock) {
+                if (!mValid) {
+                    throw new IllegalStateException();
+                }
+                if (mMapped != null) {
+                    mMapped.close();
+                    mMapped = null;
+                }
+                native_recycle();
+                mValid = false;
+                mNativeContext = 0;
+            }
+            sPool.offer(this);
+        }
+
+        private native void native_recycle();
+
+        /**
+         * Returns true if it is possible to allocate a graphic block that
+         * can be passed to all listed codecs as an input buffer without
+         * copying.
+         * <p>
+         * Note that even if this function returns true, {@link #obtain}
+         * may still throw due to invalid arguments or allocation failure.
+         * In addition, choosing a format that is not natively supported by the
+         * codec may cause color conversion.
+         *
+         * @param codecNames  list of codecs that the client wants to use a
+         *                    graphic block without copying. Null entries are
+         *                    ignored.
+         */
+        public static boolean isCodecCopyFreeCompatible(@NonNull String[] codecNames) {
+            return native_checkCompatible(codecNames);
+        }
+
+        private static native boolean native_checkCompatible(@NonNull String[] codecNames);
+
+        // Called from native
+        private void setInternalStateLocked(long context, boolean isMappable) {
+            mNativeContext = context;
+            mMappable = isMappable;
+            mValid = (context != 0);
+        }
+
+        private static final BlockingQueue<GraphicBlock> sPool =
+                new LinkedBlockingQueue<>();
+
+        /**
+         * Obtain a graphic block object of dimension
+         * {@code width}x{@code height}.
+         * If {@link #isCodecCopyFreeCompatible} with the same
+         * {@code codecNames} returned true, the returned
+         * {@link GraphicBlock} object can be queued to the listed codecs
+         * without copying. The returned {@link GraphicBlock} object is always
+         * read/write mappable.
+         *
+         * @param width requested width of the graphic block
+         * @param height requested height of the graphic block
+         * @param format the format of pixels. One of the {@code COLOR_Format}
+         *               values from {@link MediaCodecInfo.CodecCapabilities}.
+         * @param usage the usage of the buffer. @HardwareBuffer.Usage
+         * @param codecNames  list of codecs that the client wants to use this
+         *                    graphic block without copying. Null entries are
+         *                    ignored.
+         * @return  a graphic block object.
+         * @throws IllegalArgumentException if the parameters are invalid or
+         *     not supported
+         * @throws IOException if an error occurred while allocating a buffer
+         */
+        public static @NonNull GraphicBlock obtain(
+                int width,
+                int height,
+                int format,
+                @HardwareBuffer.Usage long usage,
+                @NonNull String[] codecNames) {
+            GraphicBlock buffer = sPool.poll();
+            if (buffer == null) {
+                buffer = new GraphicBlock();
+            }
+            if (width < 0 || height < 0) {
+                throw new IllegalArgumentException();
+            }
+            synchronized (buffer.mLock) {
+                buffer.native_obtain(width, height, format, usage, codecNames);
+            }
+            return buffer;
+        }
+
+        private native void native_obtain(
+                int width,
+                int height,
+                int format,
+                @HardwareBuffer.Usage long usage,
+                @NonNull String[] codecNames);
+
+        @Override
+        protected void finalize() {
+            native_recycle();
+        }
+
+        private final Object mLock = new Object();
+        private boolean mValid = false;
+        private boolean mMappable = false;
+        private Image mMapped = null;
+        private long mNativeContext = 0;
+    }
+
+    /**
+     * Builder-like class for queue requests. Use this class to prepare a
+     * queue request and send it.
+     */
+    public final class QueueRequest {
+        // No public constructor
+        private QueueRequest(@NonNull MediaCodec codec, int index) {
+            mCodec = codec;
+            mIndex = index;
+        }
+
+        /**
+         * Set a linear block to this queue request. Exactly one buffer must be
+         * set for a queue request before calling {@link #queue}. It is possible
+         * to use the same {@link LinearBlock} object for multiple queue
+         * requests. The behavior is undefined if the range of the buffer
+         * overlaps for multiple requests, or the application writes into the
+         * region being processed by the codec.
+         *
+         * @param block The linear block object
+         * @param offset The byte offset into the input buffer at which the data starts.
+         * @param size The number of bytes of valid input data.
+         * @param presentationTimeUs The presentation timestamp in microseconds for this
+         *                           buffer. This is normally the media time at which this
+         *                           buffer should be presented (rendered). When using an output
+         *                           surface, this will be propagated as the {@link
+         *                           SurfaceTexture#getTimestamp timestamp} for the frame (after
+         *                           conversion to nanoseconds).
+         * @param flags A bitmask of flags
+         *              {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
+         *              While not prohibited, most codecs do not use the
+         *              {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
+         * @return this object
+         * @throws IllegalStateException if a buffer is already set
+         */
+        public @NonNull QueueRequest setLinearBlock(
+                @NonNull LinearBlock block,
+                int offset,
+                int size,
+                long presentationTimeUs,
+                @BufferFlag int flags) {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            if (mLinearBlock != null || mGraphicBlock != null) {
+                throw new IllegalStateException();
+            }
+            mLinearBlock = block;
+            mOffset = offset;
+            mSize = size;
+            mPresentationTimeUs = presentationTimeUs;
+            mFlags = flags;
+            return this;
+        }
+
+        /**
+         * Set an encrypted linear block to this queue request. Exactly one
+         * buffer must be set for a queue request before calling {@link #queue}.
+         *
+         * @param block The linear block object
+         * @param offset The byte offset into the input buffer at which the data starts.
+         * @param presentationTimeUs The presentation timestamp in microseconds for this
+         *                           buffer. This is normally the media time at which this
+         *                           buffer should be presented (rendered). When using an output
+         *                           surface, this will be propagated as the {@link
+         *                           SurfaceTexture#getTimestamp timestamp} for the frame (after
+         *                           conversion to nanoseconds).
+         * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
+         * @param flags A bitmask of flags
+         *              {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
+         *              While not prohibited, most codecs do not use the
+         *              {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
+         * @return this object
+         * @throws IllegalStateException if a buffer is already set
+         */
+        public @NonNull QueueRequest setEncryptedLinearBlock(
+                @NonNull LinearBlock block,
+                int offset,
+                @NonNull MediaCodec.CryptoInfo cryptoInfo,
+                long presentationTimeUs,
+                @BufferFlag int flags) {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            if (mLinearBlock != null || mGraphicBlock != null) {
+                throw new IllegalStateException();
+            }
+            mLinearBlock = block;
+            mOffset = offset;
+            mCryptoInfo = cryptoInfo;
+            mPresentationTimeUs = presentationTimeUs;
+            mFlags = flags;
+            return this;
+        }
+
+        /**
+         * Set a graphic block to this queue request. Exactly one buffer must
+         * be set for a queue request before calling {@link #queue}.
+         *
+         * @param block The graphic block object
+         * @param presentationTimeUs The presentation timestamp in microseconds for this
+         *                           buffer. This is normally the media time at which this
+         *                           buffer should be presented (rendered). When using an output
+         *                           surface, this will be propagated as the {@link
+         *                           SurfaceTexture#getTimestamp timestamp} for the frame (after
+         *                           conversion to nanoseconds).
+         * @param flags A bitmask of flags
+         *              {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
+         *              While not prohibited, most codecs do not use the
+         *              {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
+         * @return this object
+         * @throws IllegalStateException if a buffer is already set
+         */
+        public @NonNull QueueRequest setGraphicBlock(
+                @NonNull GraphicBlock block,
+                long presentationTimeUs,
+                @BufferFlag int flags) {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            if (mLinearBlock != null || mGraphicBlock != null) {
+                throw new IllegalStateException();
+            }
+            mGraphicBlock = block;
+            mPresentationTimeUs = presentationTimeUs;
+            mFlags = flags;
+            return this;
+        }
+
+        /**
+         * Add a integer parameter. See {@link MediaFormat} for the list of
+         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * call with the same key which is not processed by the codec yet, the
+         * value set from this method will override the unprocessed value.
+         */
+        public @NonNull QueueRequest setIntegerParameter(
+                @NonNull String key, int value) {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            mTuningKeys.add(key);
+            mTuningValues.add(Integer.valueOf(value));
+            return this;
+        }
+
+        /**
+         * Add a long parameter. See {@link MediaFormat} for the list of
+         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * call with the same key which is not processed by the codec yet, the
+         * value set from this method will override the unprocessed value.
+         */
+        public @NonNull QueueRequest setLongParameter(
+                @NonNull String key, long value) {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            mTuningKeys.add(key);
+            mTuningValues.add(Long.valueOf(value));
+            return this;
+        }
+
+        /**
+         * Add a float parameter. See {@link MediaFormat} for the list of
+         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * call with the same key which is not processed by the codec yet, the
+         * value set from this method will override the unprocessed value.
+         */
+        public @NonNull QueueRequest setFloatParameter(
+                @NonNull String key, float value) {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            mTuningKeys.add(key);
+            mTuningValues.add(Float.valueOf(value));
+            return this;
+        }
+
+        /**
+         * Add a {@link ByteBuffer} parameter. See {@link MediaFormat} for the list of
+         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * call with the same key which is not processed by the codec yet, the
+         * value set from this method will override the unprocessed value.
+         */
+        public @NonNull QueueRequest setByteBufferParameter(
+                @NonNull String key, @NonNull ByteBuffer value) {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            mTuningKeys.add(key);
+            mTuningValues.add(value);
+            return this;
+        }
+
+        /**
+         * Add a string parameter. See {@link MediaFormat} for the list of
+         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * call with the same key which is not processed by the codec yet, the
+         * value set from this method will override the unprocessed value.
+         */
+        public @NonNull QueueRequest setStringParameter(
+                @NonNull String key, @NonNull String value) {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            mTuningKeys.add(key);
+            mTuningValues.add(value);
+            return this;
+        }
+
+        /**
+         * Finish building a queue request and queue the buffers with tunings.
+         */
+        public void queue() {
+            if (!isAccessible()) {
+                throw new IllegalStateException();
+            }
+            if (mLinearBlock == null && mGraphicBlock == null) {
+                throw new IllegalStateException();
+            }
+            setAccessible(false);
+            if (mLinearBlock != null) {
+                mCodec.native_queueLinearBlock(
+                        mIndex, mLinearBlock, mOffset, mSize, mCryptoInfo,
+                        mPresentationTimeUs, mFlags,
+                        mTuningKeys, mTuningValues);
+            } else if (mGraphicBlock != null) {
+                mCodec.native_queueGraphicBlock(
+                        mIndex, mGraphicBlock, mPresentationTimeUs, mFlags,
+                        mTuningKeys, mTuningValues);
+            }
+            clear();
+        }
+
+        @NonNull QueueRequest clear() {
+            mLinearBlock = null;
+            mOffset = 0;
+            mSize = 0;
+            mCryptoInfo = null;
+            mGraphicBlock = null;
+            mPresentationTimeUs = 0;
+            mFlags = 0;
+            mTuningKeys.clear();
+            mTuningValues.clear();
+            return this;
+        }
+
+        boolean isAccessible() {
+            return mAccessible;
+        }
+
+        @NonNull QueueRequest setAccessible(boolean accessible) {
+            mAccessible = accessible;
+            return this;
+        }
+
+        private final MediaCodec mCodec;
+        private final int mIndex;
+        private LinearBlock mLinearBlock = null;
+        private int mOffset = 0;
+        private int mSize = 0;
+        private MediaCodec.CryptoInfo mCryptoInfo = null;
+        private GraphicBlock mGraphicBlock = null;
+        private long mPresentationTimeUs = 0;
+        private @BufferFlag int mFlags = 0;
+        private final ArrayList<String> mTuningKeys = new ArrayList<>();
+        private final ArrayList<Object> mTuningValues = new ArrayList<>();
+
+        private boolean mAccessible = false;
+    }
+
+    private native void native_queueLinearBlock(
+            int index,
+            @NonNull LinearBlock block,
+            int offset,
+            int size,
+            @Nullable CryptoInfo cryptoInfo,
+            long presentationTimeUs,
+            int flags,
+            @NonNull ArrayList<String> keys,
+            @NonNull ArrayList<Object> values);
+
+    private native void native_queueGraphicBlock(
+            int index,
+            @NonNull GraphicBlock block,
+            long presentationTimeUs,
+            int flags,
+            @NonNull ArrayList<String> keys,
+            @NonNull ArrayList<Object> values);
+
+    private final ArrayList<QueueRequest> mQueueRequests = new ArrayList<>();
+
+    /**
+     * Return a clear {@link QueueRequest} object for an input slot index.
+     *
+     * @param index input slot index from
+     *              {@link Callback#onInputBufferAvailable}
+     * @return queue request object
+     * @throws IllegalStateException if not using block model
+     * @throws IllegalArgumentException if the input slot is not available or
+     *                                  the index is out of range
+     */
+    public @NonNull QueueRequest getQueueRequest(int index) {
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_BLOCK) {
+                throw new IllegalStateException();
+            }
+            if (index < 0 || index >= mQueueRequests.size()) {
+                throw new IllegalArgumentException();
+            }
+            QueueRequest request = mQueueRequests.get(index);
+            if (request == null) {
+                throw new IllegalArgumentException();
+            }
+            if (!request.isAccessible()) {
+                throw new IllegalArgumentException();
+            }
+            return request.clear();
+        }
+    }
+
+    /**
      * If a non-negative timeout had been specified in the call
      * to {@link #dequeueOutputBuffer}, indicates that the call timed out.
      */
@@ -2789,8 +3527,13 @@
     @OutputBufferInfo
     public final int dequeueOutputBuffer(
             @NonNull BufferInfo info, long timeoutUs) {
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
+        }
         int res = native_dequeueOutputBuffer(info, timeoutUs);
-        synchronized(mBufferLock) {
+        synchronized (mBufferLock) {
             if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
                 cacheBuffers(false /* input */);
             } else if (res >= 0) {
@@ -2826,15 +3569,7 @@
      * @throws MediaCodec.CodecException upon codec error.
      */
     public final void releaseOutputBuffer(int index, boolean render) {
-        BufferInfo info = null;
-        synchronized(mBufferLock) {
-            invalidateByteBuffer(mCachedOutputBuffers, index);
-            mDequeuedOutputBuffers.remove(index);
-            if (mHasSurface) {
-                info = mDequeuedOutputInfos.remove(index);
-            }
-        }
-        releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */);
+        releaseOutputBufferInternal(index, render, false /* updatePTS */, 0 /* dummy */);
     }
 
     /**
@@ -2887,16 +3622,33 @@
      * @throws MediaCodec.CodecException upon codec error.
      */
     public final void releaseOutputBuffer(int index, long renderTimestampNs) {
+        releaseOutputBufferInternal(
+                index, true /* render */, true /* updatePTS */, renderTimestampNs);
+    }
+
+    private void releaseOutputBufferInternal(
+            int index, boolean render, boolean updatePts, long renderTimestampNs) {
         BufferInfo info = null;
         synchronized(mBufferLock) {
-            invalidateByteBuffer(mCachedOutputBuffers, index);
-            mDequeuedOutputBuffers.remove(index);
-            if (mHasSurface) {
-                info = mDequeuedOutputInfos.remove(index);
+            switch (mBufferMode) {
+                case BUFFER_MODE_LEGACY:
+                    invalidateByteBuffer(mCachedOutputBuffers, index);
+                    mDequeuedOutputBuffers.remove(index);
+                    if (mHasSurface) {
+                        info = mDequeuedOutputInfos.remove(index);
+                    }
+                    break;
+                case BUFFER_MODE_BLOCK:
+                    OutputFrame frame = mOutputFrames.get(index);
+                    frame.setAccessible(false);
+                    frame.clear();
+                    break;
+                default:
+                    throw new IllegalStateException();
             }
         }
         releaseOutputBuffer(
-                index, true /* render */, true /* updatePTS */, renderTimestampNs);
+                index, render, updatePts, renderTimestampNs);
     }
 
     @UnsupportedAppUsage
@@ -3116,6 +3868,8 @@
             mCachedOutputBuffers = null;
             mDequeuedInputBuffers.clear();
             mDequeuedOutputBuffers.clear();
+            mQueueRequests.clear();
+            mOutputFrames.clear();
         }
     }
 
@@ -3154,11 +3908,16 @@
      */
     @NonNull
     public ByteBuffer[] getInputBuffers() {
-        if (mCachedInputBuffers == null) {
-            throw new IllegalStateException();
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
+            if (mCachedInputBuffers == null) {
+                throw new IllegalStateException();
+            }
+            // FIXME: check codec status
+            return mCachedInputBuffers;
         }
-        // FIXME: check codec status
-        return mCachedInputBuffers;
     }
 
     /**
@@ -3185,11 +3944,16 @@
      */
     @NonNull
     public ByteBuffer[] getOutputBuffers() {
-        if (mCachedOutputBuffers == null) {
-            throw new IllegalStateException();
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
+            if (mCachedOutputBuffers == null) {
+                throw new IllegalStateException();
+            }
+            // FIXME: check codec status
+            return mCachedOutputBuffers;
         }
-        // FIXME: check codec status
-        return mCachedOutputBuffers;
     }
 
     /**
@@ -3212,8 +3976,13 @@
      */
     @Nullable
     public ByteBuffer getInputBuffer(int index) {
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
+        }
         ByteBuffer newBuffer = getBuffer(true /* input */, index);
-        synchronized(mBufferLock) {
+        synchronized (mBufferLock) {
             invalidateByteBuffer(mCachedInputBuffers, index);
             mDequeuedInputBuffers.put(index, newBuffer);
         }
@@ -3241,8 +4010,13 @@
      */
     @Nullable
     public Image getInputImage(int index) {
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
+        }
         Image newImage = getImage(true /* input */, index);
-        synchronized(mBufferLock) {
+        synchronized (mBufferLock) {
             invalidateByteBuffer(mCachedInputBuffers, index);
             mDequeuedInputBuffers.put(index, newImage);
         }
@@ -3270,8 +4044,13 @@
      */
     @Nullable
     public ByteBuffer getOutputBuffer(int index) {
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
+        }
         ByteBuffer newBuffer = getBuffer(false /* input */, index);
-        synchronized(mBufferLock) {
+        synchronized (mBufferLock) {
             invalidateByteBuffer(mCachedOutputBuffers, index);
             mDequeuedOutputBuffers.put(index, newBuffer);
         }
@@ -3298,8 +4077,13 @@
      */
     @Nullable
     public Image getOutputImage(int index) {
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_LEGACY) {
+                throw new IllegalStateException();
+            }
+        }
         Image newImage = getImage(false /* input */, index);
-        synchronized(mBufferLock) {
+        synchronized (mBufferLock) {
             invalidateByteBuffer(mCachedOutputBuffers, index);
             mDequeuedOutputBuffers.put(index, newImage);
         }
@@ -3307,6 +4091,149 @@
     }
 
     /**
+     * A single output frame and its associated metadata.
+     */
+    public static final class OutputFrame {
+        // No public constructor
+        OutputFrame(int index) {
+            mIndex = index;
+        }
+
+        /**
+         * Returns the output linear block, or null if this frame is empty.
+         *
+         * @throws IllegalStateException if this output frame is not linear.
+         */
+        public @Nullable LinearBlock getLinearBlock() {
+            if (mGraphicBlock != null) {
+                throw new IllegalStateException();
+            }
+            return mLinearBlock;
+        }
+
+        /**
+         * Returns the output graphic block, or null if this frame is empty.
+         *
+         * @throws IllegalStateException if this output frame is not graphic.
+         */
+        public @Nullable GraphicBlock getGraphicBlock() {
+            if (mLinearBlock != null) {
+                throw new IllegalStateException();
+            }
+            return mGraphicBlock;
+        }
+
+        /**
+         * Returns the presentation timestamp in microseconds.
+         */
+        public long getPresentationTimeUs() {
+            return mPresentationTimeUs;
+        }
+
+        /**
+         * Returns the buffer flags.
+         */
+        public @BufferFlag int getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * Returns a read-only {@link MediaFormat} for this frame. The returned
+         * object is valid only while the client is holding the output frame.
+         */
+        public @NonNull MediaFormat getFormat() {
+            return mFormat;
+        }
+
+        /**
+         * Populate {@code keys} with the name of entries that has changed from
+         * the previous frame. The entries may have been removed/changed/added.
+         * Client can find out what the change is by querying {@link MediaFormat}
+         * object returned from {@link #getFormat}.
+         */
+        public void getChangedKeys(@NonNull Set<String> keys) {
+            keys.clear();
+            keys.addAll(mChangedKeys);
+        }
+
+        void clear() {
+            mLinearBlock = null;
+            mGraphicBlock = null;
+            mFormat = null;
+            mChangedKeys.clear();
+            mLoaded = false;
+        }
+
+        boolean isAccessible() {
+            return mAccessible;
+        }
+
+        void setAccessible(boolean accessible) {
+            mAccessible = accessible;
+        }
+
+        void setBufferInfo(MediaCodec.BufferInfo info) {
+            mPresentationTimeUs = info.presentationTimeUs;
+            mFlags = info.flags;
+        }
+
+        boolean isLoaded() {
+            return mLoaded;
+        }
+
+        void setLoaded(boolean loaded) {
+            mLoaded = loaded;
+        }
+
+        private final int mIndex;
+        private LinearBlock mLinearBlock = null;
+        private GraphicBlock mGraphicBlock = null;
+        private long mPresentationTimeUs = 0;
+        private @BufferFlag int mFlags = 0;
+        private MediaFormat mFormat = null;
+        private final ArrayList<String> mChangedKeys = new ArrayList<>();
+        private boolean mAccessible = false;
+        private boolean mLoaded = false;
+    }
+
+    private final ArrayList<OutputFrame> mOutputFrames = new ArrayList<>();
+
+    /**
+     * Returns an {@link OutputFrame} object.
+     *
+     * @param index output buffer index from
+     *              {@link Callback#onOutputBufferAvailable}
+     * @return {@link OutputFrame} object describing the output buffer
+     * @throws IllegalStateException if not using block model
+     * @throws IllegalArgumentException if the output buffer is not available or
+     *                                  the index is out of range
+     */
+    public @NonNull OutputFrame getOutputFrame(int index) {
+        synchronized (mBufferLock) {
+            if (mBufferMode != BUFFER_MODE_BLOCK) {
+                throw new IllegalStateException();
+            }
+            if (index < 0 || index >= mOutputFrames.size()) {
+                throw new IllegalArgumentException();
+            }
+            OutputFrame frame = mOutputFrames.get(index);
+            if (frame == null) {
+                throw new IllegalArgumentException();
+            }
+            if (!frame.isAccessible()) {
+                throw new IllegalArgumentException();
+            }
+            if (!frame.isLoaded()) {
+                native_getOutputFrame(frame, index);
+                frame.setLoaded(true);
+            }
+            return frame;
+        }
+    }
+
+    private native void native_getOutputFrame(OutputFrame frame, int index);
+
+    /**
      * The content is scaled to the surface dimensions
      */
     public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT               = 1;
@@ -3889,7 +4816,9 @@
         @Override
         public void close() {
             if (mIsImageValid) {
-                java.nio.NioUtils.freeDirectBuffer(mBuffer);
+                if (mBuffer != null) {
+                    java.nio.NioUtils.freeDirectBuffer(mBuffer);
+                }
                 mIsImageValid = false;
             }
         }
@@ -3908,7 +4837,6 @@
             super.setCropRect(cropRect);
         }
 
-
         public MediaImage(
                 @NonNull ByteBuffer buffer, @NonNull ByteBuffer info, boolean readOnly,
                 long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
@@ -3963,7 +4891,6 @@
                         throw new UnsupportedOperationException("unexpected strides: "
                                 + colInc + " pixel, " + rowInc + " row on plane " + ix);
                     }
-
                     buffer.clear();
                     buffer.position(mBuffer.position() + planeOffset
                             + (xOffset / horiz) * colInc + (yOffset / vert) * rowInc);
@@ -3983,6 +4910,30 @@
             super.setCropRect(cropRect);
         }
 
+        public MediaImage(
+                @NonNull Image.Plane[] planes, int width, int height, int format, boolean readOnly,
+                long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
+            mWidth = width;
+            mHeight = height;
+            mFormat = format;
+            mTimestamp = timestamp;
+            mIsImageValid = true;
+            mIsReadOnly = readOnly;
+            mBuffer = null;
+            mInfo = null;
+            mPlanes = planes;
+
+            // save offsets and info
+            mXOffset = xOffset;
+            mYOffset = yOffset;
+
+            if (cropRect == null) {
+                cropRect = new Rect(0, 0, mWidth, mHeight);
+            }
+            cropRect.offset(-xOffset, -yOffset);
+            super.setCropRect(cropRect);
+        }
+
         private class MediaPlane extends Plane {
             public MediaPlane(@NonNull ByteBuffer buffer, int rowInc, int colInc) {
                 mData = buffer;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 1a0f139..f408ac3 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -294,6 +294,14 @@
     public static final String KEY_BIT_RATE = "bitrate";
 
     /**
+     * A key describing the hardware AV sync id.
+     * The associated value is an integer
+     *
+     * @see android.media.tv.tuner.Tuner#getAvSyncHwId
+     */
+    public static final String KEY_HARDWARE_AV_SYNC_ID = "hw-av-sync-id";
+
+    /**
      * A key describing the max bitrate in bits/sec.
      * This is usually over a one-second sliding window (e.g. over any window of one second).
      * The associated value is an integer
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
index 2c08e5b..364516c 100644
--- a/media/java/android/media/tv/tuner/DemuxCapabilities.java
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -58,11 +58,13 @@
     private final long mSectionFilterLength;
     private final int mFilterCaps;
     private final int[] mLinkCaps;
+    private final boolean mSupportTimeFilter;
 
     // Used by JNI
     private DemuxCapabilities(int demuxCount, int recordCount, int playbackCount, int tsFilterCount,
             int sectionFilterCount, int audioFilterCount, int videoFilterCount, int pesFilterCount,
-            int pcrFilterCount, long sectionFilterLength, int filterCaps, int[] linkCaps) {
+            int pcrFilterCount, long sectionFilterLength, int filterCaps, int[] linkCaps,
+            boolean timeFilter) {
         mDemuxCount = demuxCount;
         mRecordCount = recordCount;
         mPlaybackCount = playbackCount;
@@ -75,6 +77,7 @@
         mSectionFilterLength = sectionFilterLength;
         mFilterCaps = filterCaps;
         mLinkCaps = linkCaps;
+        mSupportTimeFilter = timeFilter;
     }
 
     /**
@@ -161,4 +164,10 @@
     public int[] getLinkCapabilities() {
         return mLinkCaps;
     }
+    /**
+     * Is {@link android.media.tv.tuner.filter.TimeFilter} supported.
+     */
+    public boolean isTimeFilterSupported() {
+        return mSupportTimeFilter;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index bac425b..5e01244 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -105,16 +105,33 @@
      *
      * @param tuner the Tuner instance to share frontend resource with.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     public void shareFrontendFromTuner(@NonNull Tuner tuner) {
         // TODO: implementation.
     }
 
+    /**
+     * Updates client priority with an arbitrary value along with a nice value.
+     *
+     * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+     * to reclaim insufficient resources from another client.
+     * <p>The nice value represents how much the client intends to give up the resource when an
+     * insufficient resource situation happens.
+     *
+     * @param priority the new priority.
+     * @param niceValue the nice value.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    public void updateResourcePriority(int priority, int niceValue) {
+        // TODO: implementation.
+    }
 
     private long mNativeContext; // used by native jMediaTuner
 
     /**
      * Releases the Tuner instance.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Override
     public void close() {
         // TODO: implementation.
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index eb2f4a9..97a2b22 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -19,6 +19,7 @@
 import android.annotation.BytesLong;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.media.MediaCodec.LinearBlock;
 
 /**
  * Filter event sent from {@link Filter} objects with media type.
@@ -32,7 +33,7 @@
     private final long mPts;
     private final long mDataLength;
     private final long mOffset;
-    private final Object mLinearBuffer;
+    private final LinearBlock mLinearBlock;
     private final boolean mIsSecureMemory;
     private final long mDataId;
     private final int mMpuSequenceNumber;
@@ -41,14 +42,14 @@
 
     // This constructor is used by JNI code only
     private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset,
-            Object buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
+            LinearBlock buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
             boolean isPrivateData, AudioDescriptor extraMetaData) {
         mStreamId = streamId;
         mIsPtsPresent = isPtsPresent;
         mPts = pts;
         mDataLength = dataLength;
         mOffset = offset;
-        mLinearBuffer = buffer;
+        mLinearBlock = buffer;
         mIsSecureMemory = isSecureMemory;
         mDataId = dataId;
         mMpuSequenceNumber = mpuSequenceNumber;
@@ -96,13 +97,11 @@
     }
 
     /**
-     * Gets a linear buffer associated to the memory where audio or video data stays.
-     * TODO: use LinearBuffer when it's ready.
-     *
-     * @hide
+     * Gets a linear block associated to the memory where audio or video data stays.
      */
-    public Object getLinearBuffer() {
-        return mLinearBuffer;
+    @Nullable
+    public LinearBlock getLinearBlock() {
+        return mLinearBlock;
     }
 
     /**
@@ -125,6 +124,20 @@
     }
 
     /**
+     * Reserves the ID which is used by HAL to provide additional information for AV data.
+     *
+     * <p>The corresponding data is used and released by {@link android.media.AudioTrack}.
+     * <p>The data is also released when the {@link Filter} instance is closed.
+     *
+     * @return the reserved AV data ID.
+     * @hide
+     */
+    public long reserveAvDataId() {
+        // TODO: implement
+        return mDataId;
+    }
+
+    /**
      * Gets MPU sequence number of filtered data.
      */
     public int getMpuSequenceNumber() {
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 093dc6f..466fa3e 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -18,6 +18,7 @@
 
 import android.annotation.BytesLong;
 import android.annotation.SystemApi;
+import android.media.tv.tuner.filter.RecordSettings.ScHevcIndex;
 
 /**
  * Filter event sent from {@link Filter} objects with MMTP type.
@@ -38,6 +39,7 @@
     /**
      * Gets indexes which can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2.
      */
+    @ScHevcIndex
     public int getScHevcIndexMask() {
         return mScHevcIndexMask;
     }
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 70788a7..9470138 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.media.tv.tuner.TunerUtils;
 
@@ -25,9 +26,81 @@
  * @hide
  */
 @SystemApi
-public class SectionSettings extends Settings {
+public abstract class SectionSettings extends Settings {
+    final boolean mCrcEnabled;
+    final boolean mIsRepeat;
+    final boolean mIsRaw;
 
-    SectionSettings(int mainType) {
+    SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw) {
         super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION));
+        mCrcEnabled = crcEnabled;
+        mIsRepeat = isRepeat;
+        mIsRaw = isRaw;
+    }
+
+    /**
+     * Returns whether the filter enables CRC (Cyclic redundancy check) and discards data which
+     * doesn't pass the check.
+     */
+    public boolean isCrcEnabled() {
+        return mCrcEnabled;
+    }
+    /**
+     * Returns whether the filter repeats the data with the same version.
+     */
+    public boolean isRepeat() {
+        return mIsRepeat;
+    }
+    /**
+     * Returns whether the filter sends {@link FilterCallback#onFilterStatusChanged} instead of
+     * {@link FilterCallback#onFilterEvent}.
+     */
+    public boolean isRaw() {
+        return mIsRaw;
+    }
+
+    /**
+     * Builder for {@link SectionSettings}.
+     *
+     * @param <T> The subclass to be built.
+     */
+    public abstract static class Builder<T extends Builder<T>>
+            extends Settings.Builder<Builder<T>> {
+        boolean mCrcEnabled;
+        boolean mIsRepeat;
+        boolean mIsRaw;
+
+        Builder(int mainType) {
+            super(mainType);
+        }
+
+        /**
+         * Sets whether the filter enables CRC (Cyclic redundancy check) and discards data which
+         * doesn't pass the check.
+         */
+        @NonNull
+        public T setCrcEnabled(boolean crcEnabled) {
+            mCrcEnabled = crcEnabled;
+            return self();
+        }
+        /**
+         * Sets whether the filter repeats the data with the same version.
+         */
+        @NonNull
+        public T setRepeat(boolean isRepeat) {
+            mIsRepeat = isRepeat;
+            return self();
+        }
+        /**
+         * Sets whether the filter send onFilterStatus instead of
+         * {@link FilterCallback#onFilterEvent}.
+         */
+        @NonNull
+        public T setRaw(boolean isRaw) {
+            mIsRaw = isRaw;
+            return self();
+        }
+
+        /* package */ abstract T self();
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
index eeeabdf..cb547ec 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -34,8 +34,9 @@
     private final byte[] mMode;
 
 
-    private SectionSettingsWithSectionBits(int mainType, byte[] filter, byte[] mask, byte[] mode) {
-        super(mainType);
+    private SectionSettingsWithSectionBits(int mainType, boolean isCheckCrc, boolean isRepeat,
+            boolean isRaw, byte[] filter, byte[] mask, byte[] mode) {
+        super(mainType, isCheckCrc, isRepeat, isRaw);
         mFilter = filter;
         mMask = mask;
         mMode = mode;
@@ -86,7 +87,7 @@
     /**
      * Builder for {@link SectionSettingsWithSectionBits}.
      */
-    public static class Builder extends Settings.Builder<Builder> {
+    public static class Builder extends SectionSettings.Builder<Builder> {
         private byte[] mFilter;
         private byte[] mMask;
         private byte[] mMode;
@@ -125,7 +126,8 @@
          */
         @NonNull
         public SectionSettingsWithSectionBits build() {
-            return new SectionSettingsWithSectionBits(mMainType, mFilter, mMask, mMode);
+            return new SectionSettingsWithSectionBits(
+                    mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mFilter, mMask, mMode);
         }
 
         @Override
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index c5ff45c..09d1dae9 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -32,8 +32,9 @@
     private final int mTableId;
     private final int mVersion;
 
-    private SectionSettingsWithTableInfo(int mainType, int tableId, int version) {
-        super(mainType);
+    private SectionSettingsWithTableInfo(int mainType, boolean isCheckCrc, boolean isRepeat,
+            boolean isRaw, int tableId, int version) {
+        super(mainType, isCheckCrc, isRepeat, isRaw);
         mTableId = tableId;
         mVersion = version;
     }
@@ -67,7 +68,7 @@
     /**
      * Builder for {@link SectionSettingsWithTableInfo}.
      */
-    public static class Builder extends Settings.Builder<Builder> {
+    public static class Builder extends SectionSettings.Builder<Builder> {
         private int mTableId;
         private int mVersion;
 
@@ -97,7 +98,8 @@
          */
         @NonNull
         public SectionSettingsWithTableInfo build() {
-            return new SectionSettingsWithTableInfo(mMainType, mTableId, mVersion);
+            return new SectionSettingsWithTableInfo(
+                    mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mTableId, mVersion);
         }
 
         @Override
diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
index b6878e6..3d83a74 100644
--- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
@@ -106,7 +106,7 @@
          * Sets whether the data is compressed IP packet.
          */
         @NonNull
-        public Builder setIsCompressedIpPacket(boolean isCompressedIpPacket) {
+        public Builder setCompressedIpPacket(boolean isCompressedIpPacket) {
             mIsCompressedIpPacket = isCompressedIpPacket;
             return this;
         }
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index aeacd8f..c17b1b7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -1,6 +1,8 @@
 cc_library_shared {
     name: "libmedia_jni",
 
+    defaults: ["libcodec2-internal-defaults"],
+
     srcs: [
         "android_media_ImageWriter.cpp",
         "android_media_ImageReader.cpp",
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 20980a9..ab6966d 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -18,6 +18,8 @@
 #define LOG_TAG "MediaCodec-JNI"
 #include <utils/Log.h>
 
+#include <type_traits>
+
 #include "android_media_MediaCodec.h"
 
 #include "android_media_MediaCrypto.h"
@@ -31,13 +33,20 @@
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 
+#include <C2Buffer.h>
+
 #include <android/hardware/cas/native/1.0/IDescrambler.h>
 
+#include <binder/MemoryHeapBase.h>
+
 #include <cutils/compiler.h>
 
 #include <gui/Surface.h>
 
+#include <hidlmemory/FrameworkUtils.h>
+
 #include <media/MediaCodecBuffer.h>
+#include <media/hardware/VideoAPI.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -47,7 +56,6 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/PersistentSurface.h>
 #include <mediadrm/ICrypto.h>
-#include <nativehelper/ScopedLocalRef.h>
 
 #include <system/window.h>
 
@@ -110,6 +118,39 @@
     jfieldID levelField;
 } gCodecInfo;
 
+static struct {
+    jclass clazz;
+    jobject nativeByteOrder;
+    jmethodID orderId;
+    jmethodID asReadOnlyBufferId;
+    jmethodID positionId;
+    jmethodID limitId;
+} gByteBufferInfo;
+
+static struct {
+    jmethodID sizeId;
+    jmethodID getId;
+    jmethodID addId;
+} gArrayListInfo;
+
+static struct {
+    jclass clazz;
+    jmethodID ctorId;
+    jmethodID setInternalStateId;
+    jfieldID contextId;
+    jfieldID validId;
+    jfieldID lockId;
+} gLinearBlockInfo;
+
+static struct {
+    jclass clazz;
+    jmethodID ctorId;
+    jmethodID setInternalStateId;
+    jfieldID contextId;
+    jfieldID validId;
+    jfieldID lockId;
+} gGraphicBlockInfo;
+
 struct fields_t {
     jmethodID postEventFromNativeID;
     jmethodID lockAndGetContextID;
@@ -123,11 +164,65 @@
     jfieldID cryptoInfoPatternID;
     jfieldID patternEncryptBlocksID;
     jfieldID patternSkipBlocksID;
+    jfieldID queueRequestIndexID;
+    jfieldID outputFrameLinearBlockID;
+    jfieldID outputFrameGraphicBlockID;
+    jfieldID outputFrameChangedKeysID;
+    jfieldID outputFrameFormatID;
 };
 
 static fields_t gFields;
 static const void *sRefBaseOwner;
 
+struct JMediaCodecLinearBlock {
+    std::shared_ptr<C2Buffer> mBuffer;
+    std::shared_ptr<C2ReadView> mReadonlyMapping;
+
+    std::shared_ptr<C2LinearBlock> mBlock;
+    std::shared_ptr<C2WriteView> mReadWriteMapping;
+
+    sp<IMemoryHeap> mHeap;
+    sp<hardware::HidlMemory> mMemory;
+
+    sp<MediaCodecBuffer> mLegacyBuffer;
+
+    std::once_flag mCopyWarningFlag;
+
+    std::shared_ptr<C2Buffer> toC2Buffer(size_t offset, size_t size) {
+        if (mBuffer) {
+            if (mBuffer->data().type() != C2BufferData::LINEAR) {
+                return nullptr;
+            }
+            C2ConstLinearBlock block = mBuffer->data().linearBlocks().front();
+            if (offset == 0 && size == block.capacity()) {
+                return mBuffer;
+            }
+            return C2Buffer::CreateLinearBuffer(block.subBlock(offset, size));
+        }
+        if (mBlock) {
+            return C2Buffer::CreateLinearBuffer(mBlock->share(offset, size, C2Fence{}));
+        }
+        return nullptr;
+    }
+
+    sp<hardware::HidlMemory> toHidlMemory() {
+        if (mMemory) {
+            return mMemory;
+        }
+        return nullptr;
+    }
+};
+
+struct JMediaCodecGraphicBlock {
+    std::shared_ptr<C2Buffer> mBuffer;
+    std::shared_ptr<const C2GraphicView> mReadonlyMapping;
+
+    std::shared_ptr<C2GraphicBlock> mBlock;
+    std::shared_ptr<C2GraphicView> mReadWriteMapping;
+
+    sp<MediaCodecBuffer> mLegacyBuffer;
+};
+
 ////////////////////////////////////////////////////////////////////////////////
 
 JMediaCodec::JMediaCodec(
@@ -141,8 +236,6 @@
     mClass = (jclass)env->NewGlobalRef(clazz);
     mObject = env->NewWeakGlobalRef(thiz);
 
-    cacheJavaObjects(env);
-
     mLooper = new ALooper;
     mLooper->setName("MediaCodec_looper");
 
@@ -163,45 +256,6 @@
     CHECK((mCodec != NULL) != (mInitStatus != OK));
 }
 
-void JMediaCodec::cacheJavaObjects(JNIEnv *env) {
-    jclass clazz = (jclass)env->FindClass("java/nio/ByteBuffer");
-    mByteBufferClass = (jclass)env->NewGlobalRef(clazz);
-    CHECK(mByteBufferClass != NULL);
-
-    ScopedLocalRef<jclass> byteOrderClass(
-            env, env->FindClass("java/nio/ByteOrder"));
-    CHECK(byteOrderClass.get() != NULL);
-
-    jmethodID nativeOrderID = env->GetStaticMethodID(
-            byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
-    CHECK(nativeOrderID != NULL);
-
-    jobject nativeByteOrderObj =
-        env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
-    mNativeByteOrderObj = env->NewGlobalRef(nativeByteOrderObj);
-    CHECK(mNativeByteOrderObj != NULL);
-    env->DeleteLocalRef(nativeByteOrderObj);
-    nativeByteOrderObj = NULL;
-
-    mByteBufferOrderMethodID = env->GetMethodID(
-            mByteBufferClass,
-            "order",
-            "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
-    CHECK(mByteBufferOrderMethodID != NULL);
-
-    mByteBufferAsReadOnlyBufferMethodID = env->GetMethodID(
-            mByteBufferClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
-    CHECK(mByteBufferAsReadOnlyBufferMethodID != NULL);
-
-    mByteBufferPositionMethodID = env->GetMethodID(
-            mByteBufferClass, "position", "(I)Ljava/nio/Buffer;");
-    CHECK(mByteBufferPositionMethodID != NULL);
-
-    mByteBufferLimitMethodID = env->GetMethodID(
-            mByteBufferClass, "limit", "(I)Ljava/nio/Buffer;");
-    CHECK(mByteBufferLimitMethodID != NULL);
-}
-
 status_t JMediaCodec::initCheck() const {
     return mInitStatus;
 }
@@ -247,19 +301,6 @@
     mObject = NULL;
     env->DeleteGlobalRef(mClass);
     mClass = NULL;
-    deleteJavaObjects(env);
-}
-
-void JMediaCodec::deleteJavaObjects(JNIEnv *env) {
-    env->DeleteGlobalRef(mByteBufferClass);
-    mByteBufferClass = NULL;
-    env->DeleteGlobalRef(mNativeByteOrderObj);
-    mNativeByteOrderObj = NULL;
-
-    mByteBufferOrderMethodID = NULL;
-    mByteBufferAsReadOnlyBufferMethodID = NULL;
-    mByteBufferPositionMethodID = NULL;
-    mByteBufferLimitMethodID = NULL;
 }
 
 status_t JMediaCodec::enableOnFrameRenderedListener(jboolean enable) {
@@ -300,6 +341,12 @@
         mSurfaceTextureClient.clear();
     }
 
+    constexpr int32_t CONFIGURE_FLAG_ENCODE = 1;
+    AString mime;
+    CHECK(format->findString("mime", &mime));
+    mGraphicOutput = (mime.startsWithIgnoreCase("video/") || mime.startsWithIgnoreCase("image/"))
+            && !(flags & CONFIGURE_FLAG_ENCODE);
+
     return mCodec->configure(
             format, mSurfaceTextureClient, crypto, descrambler, flags);
 }
@@ -370,6 +417,32 @@
             presentationTimeUs, flags, errorDetailMsg);
 }
 
+status_t JMediaCodec::queueBuffer(
+        size_t index, const std::shared_ptr<C2Buffer> &buffer, int64_t timeUs,
+        uint32_t flags, const sp<AMessage> &tunings, AString *errorDetailMsg) {
+    return mCodec->queueBuffer(
+            index, buffer, timeUs, flags, tunings, errorDetailMsg);
+}
+
+status_t JMediaCodec::queueEncryptedLinearBlock(
+        size_t index,
+        const sp<hardware::HidlMemory> &buffer,
+        size_t offset,
+        const CryptoPlugin::SubSample *subSamples,
+        size_t numSubSamples,
+        const uint8_t key[16],
+        const uint8_t iv[16],
+        CryptoPlugin::Mode mode,
+        const CryptoPlugin::Pattern &pattern,
+        int64_t presentationTimeUs,
+        uint32_t flags,
+        const sp<AMessage> &tunings,
+        AString *errorDetailMsg) {
+    return mCodec->queueEncryptedBuffer(
+            index, buffer, offset, subSamples, numSubSamples, key, iv, mode, pattern,
+            presentationTimeUs, flags, tunings, errorDetailMsg);
+}
+
 status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
     return mCodec->dequeueInputBuffer(index, timeoutUs);
 }
@@ -444,7 +517,7 @@
     }
 
     *bufArray = (jobjectArray)env->NewObjectArray(
-            buffers.size(), mByteBufferClass, NULL);
+            buffers.size(), gByteBufferInfo.clazz, NULL);
     if (*bufArray == NULL) {
         return NO_MEMORY;
     }
@@ -470,6 +543,39 @@
     return OK;
 }
 
+template <typename T>
+static jobject CreateByteBuffer(
+        JNIEnv *env, T *base, size_t capacity, size_t offset, size_t size,
+        bool readOnly, bool clearBuffer) {
+    jobject byteBuffer =
+        env->NewDirectByteBuffer(
+                const_cast<typename std::remove_const<T>::type *>(base),
+                capacity);
+    if (readOnly && byteBuffer != NULL) {
+        jobject readOnlyBuffer = env->CallObjectMethod(
+                byteBuffer, gByteBufferInfo.asReadOnlyBufferId);
+        env->DeleteLocalRef(byteBuffer);
+        byteBuffer = readOnlyBuffer;
+    }
+    if (byteBuffer == NULL) {
+        return nullptr;
+    }
+    jobject me = env->CallObjectMethod(
+            byteBuffer, gByteBufferInfo.orderId, gByteBufferInfo.nativeByteOrder);
+    env->DeleteLocalRef(me);
+    me = env->CallObjectMethod(
+            byteBuffer, gByteBufferInfo.limitId,
+            clearBuffer ? capacity : offset + size);
+    env->DeleteLocalRef(me);
+    me = env->CallObjectMethod(
+            byteBuffer, gByteBufferInfo.positionId,
+            clearBuffer ? 0 : offset);
+    env->DeleteLocalRef(me);
+    me = NULL;
+    return byteBuffer;
+}
+
+
 // static
 template <typename T>
 status_t JMediaCodec::createByteBufferFromABuffer(
@@ -488,29 +594,9 @@
         return OK;
     }
 
-    jobject byteBuffer =
-        env->NewDirectByteBuffer(buffer->base(), buffer->capacity());
-    if (readOnly && byteBuffer != NULL) {
-        jobject readOnlyBuffer = env->CallObjectMethod(
-                byteBuffer, mByteBufferAsReadOnlyBufferMethodID);
-        env->DeleteLocalRef(byteBuffer);
-        byteBuffer = readOnlyBuffer;
-    }
-    if (byteBuffer == NULL) {
-        return NO_MEMORY;
-    }
-    jobject me = env->CallObjectMethod(
-            byteBuffer, mByteBufferOrderMethodID, mNativeByteOrderObj);
-    env->DeleteLocalRef(me);
-    me = env->CallObjectMethod(
-            byteBuffer, mByteBufferLimitMethodID,
-            clearBuffer ? buffer->capacity() : (buffer->offset() + buffer->size()));
-    env->DeleteLocalRef(me);
-    me = env->CallObjectMethod(
-            byteBuffer, mByteBufferPositionMethodID,
-            clearBuffer ? 0 : buffer->offset());
-    env->DeleteLocalRef(me);
-    me = NULL;
+    jobject byteBuffer = CreateByteBuffer(
+            env, buffer->base(), buffer->capacity(), buffer->offset(), buffer->size(),
+            readOnly, clearBuffer);
 
     *buf = byteBuffer;
     return OK;
@@ -628,6 +714,92 @@
     return OK;
 }
 
+status_t JMediaCodec::getOutputFrame(
+        JNIEnv *env, jobject frame, size_t index) const {
+    sp<MediaCodecBuffer> buffer;
+
+    status_t err = mCodec->getOutputBuffer(index, &buffer);
+    if (err != OK) {
+        return err;
+    }
+
+    if (buffer->size() > 0) {
+        // asC2Buffer clears internal reference, so set the reference again.
+        std::shared_ptr<C2Buffer> c2Buffer = buffer->asC2Buffer();
+        buffer->copy(c2Buffer);
+        if (c2Buffer) {
+            switch (c2Buffer->data().type()) {
+                case C2BufferData::LINEAR: {
+                    std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+                    context->mBuffer = c2Buffer;
+                    ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
+                            gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
+                    env->SetLongField(
+                            linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release());
+                    env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
+                    break;
+                }
+                case C2BufferData::GRAPHIC: {
+                    std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
+                    context->mBuffer = c2Buffer;
+                    ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
+                            gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
+                    env->SetLongField(
+                            graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release());
+                    env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
+                    break;
+                }
+                case C2BufferData::LINEAR_CHUNKS:  [[fallthrough]];
+                case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]];
+                case C2BufferData::INVALID:        [[fallthrough]];
+                default:
+                    return INVALID_OPERATION;
+            }
+        } else {
+            if (!mGraphicOutput) {
+                std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+                context->mLegacyBuffer = buffer;
+                ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
+                        gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
+                env->SetLongField(
+                        linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release());
+                env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
+            } else {
+                std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
+                context->mLegacyBuffer = buffer;
+                ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
+                        gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
+                env->SetLongField(
+                        graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release());
+                env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
+            }
+        }
+    }
+
+    jobject format;
+    err = getOutputFormat(env, index, &format);
+    if (err != OK) {
+        return err;
+    }
+    env->SetObjectField(frame, gFields.outputFrameFormatID, format);
+    env->DeleteLocalRef(format);
+    format = nullptr;
+
+    sp<RefBase> obj;
+    if (buffer->meta()->findObject("changedKeys", &obj) && obj) {
+        sp<MediaCodec::WrapperObject<std::set<std::string>>> changedKeys{
+            (decltype(changedKeys.get()))obj.get()};
+        ScopedLocalRef<jobject> changedKeysObj{env, env->GetObjectField(
+                frame, gFields.outputFrameChangedKeysID)};
+        for (const std::string &key : changedKeys->value) {
+            ScopedLocalRef<jstring> keyStr{env, env->NewStringUTF(key.c_str())};
+            (void)env->CallBooleanMethod(changedKeysObj.get(), gArrayListInfo.addId, keyStr.get());
+        }
+    }
+    return OK;
+}
+
+
 status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
     AString name;
 
@@ -1428,6 +1600,139 @@
             env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
 }
 
+struct NativeCryptoInfo {
+    NativeCryptoInfo(JNIEnv *env, jobject cryptoInfoObj)
+        : mEnv{env},
+          mIvObj{env, (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID)},
+          mKeyObj{env, (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID)} {
+        mNumSubSamples = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
+
+        ScopedLocalRef<jintArray> numBytesOfClearDataObj{env, (jintArray)env->GetObjectField(
+                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID)};
+
+        ScopedLocalRef<jintArray> numBytesOfEncryptedDataObj{env, (jintArray)env->GetObjectField(
+                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID)};
+
+        jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+        if (jmode == gCryptoModes.Unencrypted) {
+            mMode = CryptoPlugin::kMode_Unencrypted;
+        } else if (jmode == gCryptoModes.AesCtr) {
+            mMode = CryptoPlugin::kMode_AES_CTR;
+        } else if (jmode == gCryptoModes.AesCbc) {
+            mMode = CryptoPlugin::kMode_AES_CBC;
+        }  else {
+            throwExceptionAsNecessary(env, INVALID_OPERATION);
+            return;
+        }
+
+        ScopedLocalRef<jobject> patternObj{
+            env, env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID)};
+
+        CryptoPlugin::Pattern pattern;
+        if (patternObj.get() == nullptr) {
+            pattern.mEncryptBlocks = 0;
+            pattern.mSkipBlocks = 0;
+        } else {
+            pattern.mEncryptBlocks = env->GetIntField(
+                    patternObj.get(), gFields.patternEncryptBlocksID);
+            pattern.mSkipBlocks = env->GetIntField(
+                    patternObj.get(), gFields.patternSkipBlocksID);
+        }
+
+        mErr = OK;
+        if (mNumSubSamples <= 0) {
+            mErr = -EINVAL;
+        } else if (numBytesOfClearDataObj == nullptr
+                && numBytesOfEncryptedDataObj == nullptr) {
+            mErr = -EINVAL;
+        } else if (numBytesOfEncryptedDataObj != nullptr
+                && env->GetArrayLength(numBytesOfEncryptedDataObj.get()) < mNumSubSamples) {
+            mErr = -ERANGE;
+        } else if (numBytesOfClearDataObj != nullptr
+                && env->GetArrayLength(numBytesOfClearDataObj.get()) < mNumSubSamples) {
+            mErr = -ERANGE;
+        // subSamples array may silently overflow if number of samples are too large.  Use
+        // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
+        } else if (CC_UNLIKELY(mNumSubSamples >= (signed)(INT32_MAX / sizeof(*mSubSamples))) ) {
+            mErr = -EINVAL;
+        } else {
+            jint *numBytesOfClearData =
+                (numBytesOfClearDataObj == nullptr)
+                    ? nullptr
+                    : env->GetIntArrayElements(numBytesOfClearDataObj.get(), nullptr);
+
+            jint *numBytesOfEncryptedData =
+                (numBytesOfEncryptedDataObj == nullptr)
+                    ? nullptr
+                    : env->GetIntArrayElements(numBytesOfEncryptedDataObj.get(), nullptr);
+
+            mSubSamples = new CryptoPlugin::SubSample[mNumSubSamples];
+
+            for (jint i = 0; i < mNumSubSamples; ++i) {
+                mSubSamples[i].mNumBytesOfClearData =
+                    (numBytesOfClearData == nullptr) ? 0 : numBytesOfClearData[i];
+
+                mSubSamples[i].mNumBytesOfEncryptedData =
+                    (numBytesOfEncryptedData == nullptr) ? 0 : numBytesOfEncryptedData[i];
+            }
+
+            if (numBytesOfEncryptedData != nullptr) {
+                env->ReleaseIntArrayElements(
+                        numBytesOfEncryptedDataObj.get(), numBytesOfEncryptedData, 0);
+                numBytesOfEncryptedData = nullptr;
+            }
+
+            if (numBytesOfClearData != nullptr) {
+                env->ReleaseIntArrayElements(
+                        numBytesOfClearDataObj.get(), numBytesOfClearData, 0);
+                numBytesOfClearData = nullptr;
+            }
+        }
+
+        if (mErr == OK && mKeyObj.get() != nullptr) {
+            if (env->GetArrayLength(mKeyObj.get()) != 16) {
+                mErr = -EINVAL;
+            } else {
+                mKey = env->GetByteArrayElements(mKeyObj.get(), nullptr);
+            }
+        }
+
+        if (mErr == OK && mIvObj.get() != nullptr) {
+            if (env->GetArrayLength(mIvObj.get()) != 16) {
+                mErr = -EINVAL;
+            } else {
+                mIv = env->GetByteArrayElements(mIvObj.get(), nullptr);
+            }
+        }
+    }
+
+    ~NativeCryptoInfo() {
+        if (mIv != nullptr) {
+            mEnv->ReleaseByteArrayElements(mIvObj.get(), mIv, 0);
+        }
+
+        if (mKey != nullptr) {
+            mEnv->ReleaseByteArrayElements(mKeyObj.get(), mKey, 0);
+        }
+
+        if (mSubSamples != nullptr) {
+            delete[] mSubSamples;
+        }
+    }
+
+    JNIEnv *mEnv{nullptr};
+    ScopedLocalRef<jbyteArray> mIvObj;
+    ScopedLocalRef<jbyteArray> mKeyObj;
+    status_t mErr{OK};
+
+    CryptoPlugin::SubSample *mSubSamples{nullptr};
+    int32_t mNumSubSamples{0};
+    jbyte *mIv{nullptr};
+    jbyte *mKey{nullptr};
+    enum CryptoPlugin::Mode mMode;
+    CryptoPlugin::Pattern mPattern;
+};
+
 static void android_media_MediaCodec_queueSecureInputBuffer(
         JNIEnv *env,
         jobject thiz,
@@ -1445,154 +1750,276 @@
         return;
     }
 
-    jint numSubSamples =
-        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
-
-    jintArray numBytesOfClearDataObj =
-        (jintArray)env->GetObjectField(
-                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
-
-    jintArray numBytesOfEncryptedDataObj =
-        (jintArray)env->GetObjectField(
-                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
-
-    jbyteArray keyObj =
-        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
-
-    jbyteArray ivObj =
-        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
-
-    jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
-    enum CryptoPlugin::Mode mode;
-    if (jmode == gCryptoModes.Unencrypted) {
-        mode = CryptoPlugin::kMode_Unencrypted;
-    } else if (jmode == gCryptoModes.AesCtr) {
-        mode = CryptoPlugin::kMode_AES_CTR;
-    } else if (jmode == gCryptoModes.AesCbc) {
-        mode = CryptoPlugin::kMode_AES_CBC;
-    }  else {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
-        return;
-    }
-
-    jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID);
-
-    CryptoPlugin::Pattern pattern;
-    if (patternObj == NULL) {
-        pattern.mEncryptBlocks = 0;
-        pattern.mSkipBlocks = 0;
-    } else {
-        pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID);
-        pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID);
-    }
-
-    status_t err = OK;
-
-    CryptoPlugin::SubSample *subSamples = NULL;
-    jbyte *key = NULL;
-    jbyte *iv = NULL;
-
-    if (numSubSamples <= 0) {
-        err = -EINVAL;
-    } else if (numBytesOfClearDataObj == NULL
-            && numBytesOfEncryptedDataObj == NULL) {
-        err = -EINVAL;
-    } else if (numBytesOfEncryptedDataObj != NULL
-            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
-        err = -ERANGE;
-    } else if (numBytesOfClearDataObj != NULL
-            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
-        err = -ERANGE;
-    // subSamples array may silently overflow if number of samples are too large.  Use
-    // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
-    } else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) {
-        err = -EINVAL;
-    } else {
-        jboolean isCopy;
-
-        jint *numBytesOfClearData =
-            (numBytesOfClearDataObj == NULL)
-                ? NULL
-                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
-
-        jint *numBytesOfEncryptedData =
-            (numBytesOfEncryptedDataObj == NULL)
-                ? NULL
-                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
-
-        subSamples = new CryptoPlugin::SubSample[numSubSamples];
-
-        for (jint i = 0; i < numSubSamples; ++i) {
-            subSamples[i].mNumBytesOfClearData =
-                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
-
-            subSamples[i].mNumBytesOfEncryptedData =
-                (numBytesOfEncryptedData == NULL)
-                    ? 0 : numBytesOfEncryptedData[i];
-        }
-
-        if (numBytesOfEncryptedData != NULL) {
-            env->ReleaseIntArrayElements(
-                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
-            numBytesOfEncryptedData = NULL;
-        }
-
-        if (numBytesOfClearData != NULL) {
-            env->ReleaseIntArrayElements(
-                    numBytesOfClearDataObj, numBytesOfClearData, 0);
-            numBytesOfClearData = NULL;
-        }
-    }
-
-    if (err == OK && keyObj != NULL) {
-        if (env->GetArrayLength(keyObj) != 16) {
-            err = -EINVAL;
-        } else {
-            jboolean isCopy;
-            key = env->GetByteArrayElements(keyObj, &isCopy);
-        }
-    }
-
-    if (err == OK && ivObj != NULL) {
-        if (env->GetArrayLength(ivObj) != 16) {
-            err = -EINVAL;
-        } else {
-            jboolean isCopy;
-            iv = env->GetByteArrayElements(ivObj, &isCopy);
-        }
-    }
-
+    NativeCryptoInfo cryptoInfo{env, cryptoInfoObj};
     AString errorDetailMsg;
 
+    status_t err = cryptoInfo.mErr;
     if (err == OK) {
         err = codec->queueSecureInputBuffer(
                 index, offset,
-                subSamples, numSubSamples,
-                (const uint8_t *)key, (const uint8_t *)iv,
-                mode,
-                pattern,
+                cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples,
+                (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv,
+                cryptoInfo.mMode,
+                cryptoInfo.mPattern,
                 timestampUs,
                 flags,
                 &errorDetailMsg);
     }
 
-    if (iv != NULL) {
-        env->ReleaseByteArrayElements(ivObj, iv, 0);
-        iv = NULL;
-    }
-
-    if (key != NULL) {
-        env->ReleaseByteArrayElements(keyObj, key, 0);
-        key = NULL;
-    }
-
-    delete[] subSamples;
-    subSamples = NULL;
-
     throwExceptionAsNecessary(
             env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
 }
 
+static status_t ConvertKeyValueListsToAMessage(
+        JNIEnv *env, jobject keys, jobject values, sp<AMessage> *msg) {
+    static struct Fields {
+        explicit Fields(JNIEnv *env) {
+            ScopedLocalRef<jclass> clazz{env, env->FindClass("java/lang/String")};
+            CHECK(clazz.get() != NULL);
+            mStringClass = (jclass)env->NewGlobalRef(clazz.get());
+
+            clazz.reset(env->FindClass("java/lang/Integer"));
+            CHECK(clazz.get() != NULL);
+            mIntegerClass = (jclass)env->NewGlobalRef(clazz.get());
+
+            mIntegerValueId = env->GetMethodID(clazz.get(), "intValue", "()I");
+            CHECK(mIntegerValueId != NULL);
+
+            clazz.reset(env->FindClass("java/lang/Long"));
+            CHECK(clazz.get() != NULL);
+            mLongClass = (jclass)env->NewGlobalRef(clazz.get());
+
+            mLongValueId = env->GetMethodID(clazz.get(), "longValue", "()J");
+            CHECK(mLongValueId != NULL);
+
+            clazz.reset(env->FindClass("java/lang/Float"));
+            CHECK(clazz.get() != NULL);
+            mFloatClass = (jclass)env->NewGlobalRef(clazz.get());
+
+            mFloatValueId = env->GetMethodID(clazz.get(), "floatValue", "()F");
+            CHECK(mFloatValueId != NULL);
+
+            clazz.reset(env->FindClass("java/util/ArrayList"));
+            CHECK(clazz.get() != NULL);
+
+            mByteBufferArrayId = env->GetMethodID(gByteBufferInfo.clazz, "array", "()[B");
+            CHECK(mByteBufferArrayId != NULL);
+        }
+
+        jclass mStringClass;
+        jclass mIntegerClass;
+        jmethodID mIntegerValueId;
+        jclass mLongClass;
+        jmethodID mLongValueId;
+        jclass mFloatClass;
+        jmethodID mFloatValueId;
+        jmethodID mByteBufferArrayId;
+    } sFields{env};
+
+    jint size = env->CallIntMethod(keys, gArrayListInfo.sizeId);
+    if (size != env->CallIntMethod(values, gArrayListInfo.sizeId)) {
+        return BAD_VALUE;
+    }
+
+    sp<AMessage> result{new AMessage};
+    for (jint i = 0; i < size; ++i) {
+        ScopedLocalRef<jstring> jkey{
+            env, (jstring)env->CallObjectMethod(keys, gArrayListInfo.getId, i)};
+        const char *tmp = env->GetStringUTFChars(jkey.get(), nullptr);
+        AString key;
+        if (tmp) {
+            key.setTo(tmp);
+        }
+        env->ReleaseStringUTFChars(jkey.get(), tmp);
+        if (key.empty()) {
+            return NO_MEMORY;
+        }
+
+        ScopedLocalRef<jobject> jvalue{
+            env, env->CallObjectMethod(values, gArrayListInfo.getId, i)};
+
+        if (env->IsInstanceOf(jvalue.get(), sFields.mStringClass)) {
+            const char *tmp = env->GetStringUTFChars((jstring)jvalue.get(), nullptr);
+            AString value;
+            if (tmp) {
+                value.setTo(tmp);
+            }
+            env->ReleaseStringUTFChars((jstring)jvalue.get(), tmp);
+            if (value.empty()) {
+                return NO_MEMORY;
+            }
+            result->setString(key.c_str(), value);
+        } else if (env->IsInstanceOf(jvalue.get(), sFields.mIntegerClass)) {
+            jint value = env->CallIntMethod(jvalue.get(), sFields.mIntegerValueId);
+            result->setInt32(key.c_str(), value);
+        } else if (env->IsInstanceOf(jvalue.get(), sFields.mLongClass)) {
+            jlong value = env->CallLongMethod(jvalue.get(), sFields.mLongValueId);
+            result->setInt64(key.c_str(), value);
+        } else if (env->IsInstanceOf(jvalue.get(), sFields.mFloatClass)) {
+            jfloat value = env->CallFloatMethod(jvalue.get(), sFields.mFloatValueId);
+            result->setFloat(key.c_str(), value);
+        } else if (env->IsInstanceOf(jvalue.get(), gByteBufferInfo.clazz)) {
+            jint position = env->CallIntMethod(jvalue.get(), gByteBufferInfo.positionId);
+            jint limit = env->CallIntMethod(jvalue.get(), gByteBufferInfo.limitId);
+            sp<ABuffer> buffer{new ABuffer(limit - position)};
+            void *data = env->GetDirectBufferAddress(jvalue.get());
+            if (data != nullptr) {
+                memcpy(buffer->data(),
+                       static_cast<const uint8_t *>(data) + position,
+                       buffer->size());
+            } else {
+                ScopedLocalRef<jbyteArray> byteArray{env, (jbyteArray)env->CallObjectMethod(
+                        jvalue.get(), sFields.mByteBufferArrayId)};
+                env->GetByteArrayRegion(byteArray.get(), position, buffer->size(),
+                                        reinterpret_cast<jbyte *>(buffer->data()));
+            }
+            result->setBuffer(key.c_str(), buffer);
+        }
+    }
+
+    *msg = result;
+    return OK;
+}
+
+static void android_media_MediaCodec_native_queueLinearBlock(
+        JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
+        jint offset, jint size, jobject cryptoInfoObj,
+        jlong presentationTimeUs, jint flags, jobject keys, jobject values) {
+    ALOGV("android_media_MediaCodec_native_queueLinearBlock");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == nullptr) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    sp<AMessage> tunings;
+    status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings);
+    if (err != OK) {
+        throwExceptionAsNecessary(env, err);
+        return;
+    }
+
+    std::shared_ptr<C2Buffer> buffer;
+    sp<hardware::HidlMemory> memory;
+    ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gLinearBlockInfo.lockId)};
+    if (env->MonitorEnter(lock.get()) == JNI_OK) {
+        if (env->GetBooleanField(bufferObj, gLinearBlockInfo.validId)) {
+            JMediaCodecLinearBlock *context =
+                (JMediaCodecLinearBlock *)env->GetLongField(bufferObj, gLinearBlockInfo.contextId);
+            if (cryptoInfoObj != nullptr) {
+                memory = context->toHidlMemory();
+            } else {
+                buffer = context->toC2Buffer(offset, size);
+            }
+        }
+        env->MonitorExit(lock.get());
+    } else {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    AString errorDetailMsg;
+    if (cryptoInfoObj != nullptr) {
+        if (!memory) {
+            throwExceptionAsNecessary(env, BAD_VALUE);
+            return;
+        }
+
+        NativeCryptoInfo cryptoInfo{env, cryptoInfoObj};
+        size_t cryptoSize = 0;
+        for (int i = 0; i < cryptoInfo.mNumSubSamples; ++i) {
+            cryptoSize += cryptoInfo.mSubSamples[i].mNumBytesOfClearData;
+            cryptoSize += cryptoInfo.mSubSamples[i].mNumBytesOfEncryptedData;
+        }
+        err = codec->queueEncryptedLinearBlock(
+                index,
+                memory,
+                offset,
+                cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples,
+                (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv,
+                cryptoInfo.mMode,
+                cryptoInfo.mPattern,
+                presentationTimeUs,
+                flags,
+                tunings,
+                &errorDetailMsg);
+    } else {
+        if (!buffer) {
+            throwExceptionAsNecessary(env, BAD_VALUE);
+            return;
+        }
+        err = codec->queueBuffer(
+                index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
+    }
+    throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
+}
+
+static void android_media_MediaCodec_native_queueGraphicBlock(
+        JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
+        jlong presentationTimeUs, jint flags, jobject keys, jobject values) {
+    ALOGV("android_media_MediaCodec_native_queueGraphicBlock");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    sp<AMessage> tunings;
+    status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings);
+    if (err != OK) {
+        throwExceptionAsNecessary(env, err);
+        return;
+    }
+
+    std::shared_ptr<C2Buffer> buffer;
+    std::shared_ptr<C2GraphicBlock> block;
+    ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gGraphicBlockInfo.lockId)};
+    if (env->MonitorEnter(lock.get()) == JNI_OK) {
+        if (env->GetBooleanField(bufferObj, gGraphicBlockInfo.validId)) {
+            JMediaCodecGraphicBlock *context = (JMediaCodecGraphicBlock *)env->GetLongField(
+                    bufferObj, gGraphicBlockInfo.contextId);
+            buffer = context->mBuffer;
+            block = context->mBlock;
+        }
+        env->MonitorExit(lock.get());
+    } else {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    if (!block && !buffer) {
+        throwExceptionAsNecessary(env, BAD_VALUE);
+        return;
+    }
+    if (!buffer) {
+        buffer = C2Buffer::CreateGraphicBuffer(block->share(block->crop(), C2Fence{}));
+    }
+    AString errorDetailMsg;
+    err = codec->queueBuffer(index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
+    throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
+}
+
+static void android_media_MediaCodec_native_getOutputFrame(
+        JNIEnv *env, jobject thiz, jobject frame, jint index) {
+    ALOGV("android_media_MediaCodec_native_getOutputFrame");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    status_t err = codec->getOutputFrame(env, frame, index);
+    if (err != OK) {
+        throwExceptionAsNecessary(env, err);
+    }
+}
+
 static jint android_media_MediaCodec_dequeueInputBuffer(
         JNIEnv *env, jobject thiz, jlong timeoutUs) {
     ALOGV("android_media_MediaCodec_dequeueInputBuffer");
@@ -1991,6 +2418,31 @@
     gFields.patternSkipBlocksID = env->GetFieldID(clazz.get(), "mSkipBlocks", "I");
     CHECK(gFields.patternSkipBlocksID != NULL);
 
+    clazz.reset(env->FindClass("android/media/MediaCodec$QueueRequest"));
+    CHECK(clazz.get() != NULL);
+
+    gFields.queueRequestIndexID = env->GetFieldID(clazz.get(), "mIndex", "I");
+    CHECK(gFields.queueRequestIndexID != NULL);
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$OutputFrame"));
+    CHECK(clazz.get() != NULL);
+
+    gFields.outputFrameLinearBlockID =
+        env->GetFieldID(clazz.get(), "mLinearBlock", "Landroid/media/MediaCodec$LinearBlock;");
+    CHECK(gFields.outputFrameLinearBlockID != NULL);
+
+    gFields.outputFrameGraphicBlockID =
+        env->GetFieldID(clazz.get(), "mGraphicBlock", "Landroid/media/MediaCodec$GraphicBlock;");
+    CHECK(gFields.outputFrameGraphicBlockID != NULL);
+
+    gFields.outputFrameChangedKeysID =
+        env->GetFieldID(clazz.get(), "mChangedKeys", "Ljava/util/ArrayList;");
+    CHECK(gFields.outputFrameChangedKeysID != NULL);
+
+    gFields.outputFrameFormatID =
+        env->GetFieldID(clazz.get(), "mFormat", "Landroid/media/MediaFormat;");
+    CHECK(gFields.outputFrameFormatID != NULL);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
     CHECK(clazz.get() != NULL);
 
@@ -2105,6 +2557,96 @@
     field = env->GetFieldID(clazz.get(), "level", "I");
     CHECK(field != NULL);
     gCodecInfo.levelField = field;
+
+    clazz.reset(env->FindClass("java/nio/ByteBuffer"));
+    CHECK(clazz.get() != NULL);
+    gByteBufferInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    ScopedLocalRef<jclass> byteOrderClass(
+            env, env->FindClass("java/nio/ByteOrder"));
+    CHECK(byteOrderClass.get() != NULL);
+
+    jmethodID nativeOrderID = env->GetStaticMethodID(
+            byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
+    CHECK(nativeOrderID != NULL);
+
+    ScopedLocalRef<jobject> nativeByteOrderObj{
+        env, env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID)};
+    gByteBufferInfo.nativeByteOrder = env->NewGlobalRef(nativeByteOrderObj.get());
+    CHECK(gByteBufferInfo.nativeByteOrder != NULL);
+    nativeByteOrderObj.reset();
+
+    gByteBufferInfo.orderId = env->GetMethodID(
+            clazz.get(),
+            "order",
+            "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
+    CHECK(gByteBufferInfo.orderId != NULL);
+
+    gByteBufferInfo.asReadOnlyBufferId = env->GetMethodID(
+            clazz.get(), "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
+    CHECK(gByteBufferInfo.asReadOnlyBufferId != NULL);
+
+    gByteBufferInfo.positionId = env->GetMethodID(
+            clazz.get(), "position", "(I)Ljava/nio/Buffer;");
+    CHECK(gByteBufferInfo.positionId != NULL);
+
+    gByteBufferInfo.limitId = env->GetMethodID(
+            clazz.get(), "limit", "(I)Ljava/nio/Buffer;");
+    CHECK(gByteBufferInfo.limitId != NULL);
+
+    clazz.reset(env->FindClass("java/util/ArrayList"));
+    CHECK(clazz.get() != NULL);
+
+    gArrayListInfo.sizeId = env->GetMethodID(clazz.get(), "size", "()I");
+    CHECK(gArrayListInfo.sizeId != NULL);
+
+    gArrayListInfo.getId = env->GetMethodID(clazz.get(), "get", "(I)Ljava/lang/Object;");
+    CHECK(gArrayListInfo.getId != NULL);
+
+    gArrayListInfo.addId = env->GetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z");
+    CHECK(gArrayListInfo.addId != NULL);
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$LinearBlock"));
+    CHECK(clazz.get() != NULL);
+
+    gLinearBlockInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    gLinearBlockInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+    CHECK(gLinearBlockInfo.ctorId != NULL);
+
+    gLinearBlockInfo.setInternalStateId = env->GetMethodID(
+            clazz.get(), "setInternalStateLocked", "(JZ)V");
+    CHECK(gLinearBlockInfo.setInternalStateId != NULL);
+
+    gLinearBlockInfo.contextId = env->GetFieldID(clazz.get(), "mNativeContext", "J");
+    CHECK(gLinearBlockInfo.contextId != NULL);
+
+    gLinearBlockInfo.validId = env->GetFieldID(clazz.get(), "mValid", "Z");
+    CHECK(gLinearBlockInfo.validId != NULL);
+
+    gLinearBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
+    CHECK(gLinearBlockInfo.lockId != NULL);
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$GraphicBlock"));
+    CHECK(clazz.get() != NULL);
+
+    gGraphicBlockInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    gGraphicBlockInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+    CHECK(gGraphicBlockInfo.ctorId != NULL);
+
+    gGraphicBlockInfo.setInternalStateId = env->GetMethodID(
+            clazz.get(), "setInternalStateLocked", "(JZ)V");
+    CHECK(gGraphicBlockInfo.setInternalStateId != NULL);
+
+    gGraphicBlockInfo.contextId = env->GetFieldID(clazz.get(), "mNativeContext", "J");
+    CHECK(gGraphicBlockInfo.contextId != NULL);
+
+    gGraphicBlockInfo.validId = env->GetFieldID(clazz.get(), "mValid", "Z");
+    CHECK(gGraphicBlockInfo.validId != NULL);
+
+    gGraphicBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
+    CHECK(gGraphicBlockInfo.lockId != NULL);
 }
 
 static void android_media_MediaCodec_native_setup(
@@ -2155,6 +2697,345 @@
     android_media_MediaCodec_release(env, thiz);
 }
 
+// MediaCodec.LinearBlock
+
+static jobject android_media_MediaCodec_LinearBlock_native_map(
+        JNIEnv *env, jobject thiz) {
+    JMediaCodecLinearBlock *context =
+        (JMediaCodecLinearBlock *)env->GetLongField(thiz, gLinearBlockInfo.contextId);
+    if (context->mBuffer) {
+        std::shared_ptr<C2Buffer> buffer = context->mBuffer;
+        if (!context->mReadonlyMapping) {
+            const C2BufferData data = buffer->data();
+            if (data.type() != C2BufferData::LINEAR) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            if (data.linearBlocks().size() != 1u) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            C2ConstLinearBlock block = data.linearBlocks().front();
+            context->mReadonlyMapping =
+                std::make_shared<C2ReadView>(block.map().get());
+        }
+        return CreateByteBuffer(
+                env,
+                context->mReadonlyMapping->data(),  // base
+                context->mReadonlyMapping->capacity(),  // capacity
+                0u,  // offset
+                context->mReadonlyMapping->capacity(),  // size
+                true,  // readOnly
+                true /* clearBuffer */);
+    } else if (context->mBlock) {
+        std::shared_ptr<C2LinearBlock> block = context->mBlock;
+        if (!context->mReadWriteMapping) {
+            context->mReadWriteMapping =
+                std::make_shared<C2WriteView>(block->map().get());
+        }
+        return CreateByteBuffer(
+                env,
+                context->mReadWriteMapping->base(),
+                context->mReadWriteMapping->capacity(),
+                context->mReadWriteMapping->offset(),
+                context->mReadWriteMapping->size(),
+                false,  // readOnly
+                true /* clearBuffer */);
+    } else if (context->mLegacyBuffer) {
+        return CreateByteBuffer(
+                env,
+                context->mLegacyBuffer->base(),
+                context->mLegacyBuffer->capacity(),
+                context->mLegacyBuffer->offset(),
+                context->mLegacyBuffer->size(),
+                true,  // readOnly
+                true /* clearBuffer */);
+    } else if (context->mHeap) {
+        return CreateByteBuffer(
+                env,
+                static_cast<uint8_t *>(context->mHeap->getBase()) + context->mHeap->getOffset(),
+                context->mHeap->getSize(),
+                0,
+                context->mHeap->getSize(),
+                false,  // readOnly
+                true /* clearBuffer */);
+    }
+    throwExceptionAsNecessary(env, INVALID_OPERATION);
+    return nullptr;
+}
+
+static void android_media_MediaCodec_LinearBlock_native_recycle(
+        JNIEnv *env, jobject thiz) {
+    JMediaCodecLinearBlock *context =
+        (JMediaCodecLinearBlock *)env->GetLongField(thiz, gLinearBlockInfo.contextId);
+    env->CallVoidMethod(thiz, gLinearBlockInfo.setInternalStateId, 0, false);
+    delete context;
+}
+
+static void PopulateNamesVector(
+        JNIEnv *env, jobjectArray codecNames, std::vector<std::string> *names) {
+    jsize length = env->GetArrayLength(codecNames);
+    for (jsize i = 0; i < length; ++i) {
+        jstring jstr = static_cast<jstring>(env->GetObjectArrayElement(codecNames, i));
+        if (jstr == nullptr) {
+            // null entries are ignored
+            continue;
+        }
+        const char *cstr = env->GetStringUTFChars(jstr, nullptr);
+        if (cstr == nullptr) {
+            throwExceptionAsNecessary(env, BAD_VALUE);
+            return;
+        }
+        names->emplace_back(cstr);
+        env->ReleaseStringUTFChars(jstr, cstr);
+    }
+}
+
+static void android_media_MediaCodec_LinearBlock_native_obtain(
+        JNIEnv *env, jobject thiz, jint capacity, jobjectArray codecNames) {
+    std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+    std::vector<std::string> names;
+    PopulateNamesVector(env, codecNames, &names);
+    bool hasSecure = false;
+    bool hasNonSecure = false;
+    for (const std::string &name : names) {
+        if (name.length() >= 7 && name.substr(name.length() - 7) == ".secure") {
+            hasSecure = true;
+        } else {
+            hasNonSecure = true;
+        }
+    }
+    if (hasSecure && !hasNonSecure) {
+        context->mHeap = new MemoryHeapBase(capacity);
+        context->mMemory = hardware::fromHeap(context->mHeap);
+    } else {
+        context->mBlock = MediaCodec::FetchLinearBlock(capacity, names);
+        if (!context->mBlock) {
+            jniThrowException(env, "java/io/IOException", nullptr);
+            return;
+        }
+    }
+    env->CallVoidMethod(
+            thiz,
+            gLinearBlockInfo.setInternalStateId,
+            (jlong)context.release(),
+            true /* isMappable */);
+}
+
+static jboolean android_media_MediaCodec_LinearBlock_checkCompatible(
+        JNIEnv *env, jobjectArray codecNames) {
+    std::vector<std::string> names;
+    PopulateNamesVector(env, codecNames, &names);
+    bool isCompatible = false;
+    bool hasSecure = false;
+    bool hasNonSecure = false;
+    for (const std::string &name : names) {
+        if (name.length() >= 7 && name.substr(name.length() - 7) == ".secure") {
+            hasSecure = true;
+        } else {
+            hasNonSecure = true;
+        }
+    }
+    if (hasSecure && hasNonSecure) {
+        return false;
+    }
+    status_t err = MediaCodec::CanFetchLinearBlock(names, &isCompatible);
+    if (err != OK) {
+        throwExceptionAsNecessary(env, err);
+    }
+    return isCompatible;
+}
+
+// MediaCodec.GraphicBlock
+
+template <class T>
+static jobject CreateImage(JNIEnv *env, const std::shared_ptr<T> &view) {
+    bool readOnly = std::is_const<T>::value;
+    const C2PlanarLayout layout = view->layout();
+    jint format = HAL_PIXEL_FORMAT_YCBCR_420_888;
+    switch (layout.type) {
+        case C2PlanarLayout::TYPE_YUV: {
+            if (layout.numPlanes != 3) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            const C2PlaneInfo & yPlane = layout.planes[C2PlanarLayout::PLANE_Y];
+            const C2PlaneInfo & uPlane = layout.planes[C2PlanarLayout::PLANE_U];
+            const C2PlaneInfo & vPlane = layout.planes[C2PlanarLayout::PLANE_V];
+            if (yPlane.rowSampling != 1 || yPlane.colSampling != 1) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            if (uPlane.rowSampling != vPlane.rowSampling
+                    || uPlane.colSampling != vPlane.colSampling) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            if (uPlane.rowSampling == 2 && uPlane.colSampling == 2) {
+                format = HAL_PIXEL_FORMAT_YCBCR_420_888;
+                break;
+            } else if (uPlane.rowSampling == 1 && uPlane.colSampling == 2) {
+                format = HAL_PIXEL_FORMAT_YCBCR_422_888;
+                break;
+            } else if (uPlane.rowSampling == 1 && uPlane.colSampling == 1) {
+                format = HAL_PIXEL_FORMAT_YCBCR_444_888;
+                break;
+            }
+            throwExceptionAsNecessary(env, INVALID_OPERATION);
+            return nullptr;
+        }
+        case C2PlanarLayout::TYPE_RGB: {
+            if (layout.numPlanes != 3) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            format = HAL_PIXEL_FORMAT_FLEX_RGB_888;
+            break;
+        }
+        case C2PlanarLayout::TYPE_RGBA: {
+            if (layout.numPlanes != 4) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            format = HAL_PIXEL_FORMAT_FLEX_RGBA_8888;
+            break;
+        }
+        case C2PlanarLayout::TYPE_YUVA:
+            [[fallthrough]];
+        default:
+            throwExceptionAsNecessary(env, INVALID_OPERATION);
+            return nullptr;
+    }
+
+    ScopedLocalRef<jclass> planeClazz(
+            env, env->FindClass("android/media/MediaCodec$MediaImage$MediaPlane"));
+    ScopedLocalRef<jobjectArray> planeArray{
+            env, env->NewObjectArray(layout.numPlanes, planeClazz.get(), NULL)};
+    CHECK(planeClazz.get() != NULL);
+    jmethodID planeConstructID = env->GetMethodID(planeClazz.get(), "<init>",
+            "([Ljava/nio/ByteBuffer;IIIII)V");
+
+    // plane indices are happened to be Y-U-V and R-G-B(-A) order.
+    for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+        const C2PlaneInfo &plane = layout.planes[i];
+        if (plane.rowInc < 0 || plane.colInc < 0) {
+            throwExceptionAsNecessary(env, INVALID_OPERATION);
+            return nullptr;
+        }
+        ssize_t minOffset = plane.minOffset(view->width(), view->height());
+        ssize_t maxOffset = plane.maxOffset(view->width(), view->height());
+        ScopedLocalRef<jobject> byteBuffer{env, CreateByteBuffer(
+                env,
+                view->data()[plane.rootIx] + plane.offset + minOffset,
+                maxOffset - minOffset + 1,
+                0,
+                maxOffset - minOffset + 1,
+                readOnly,
+                true)};
+
+        ScopedLocalRef<jobject> jPlane{env, env->NewObject(
+                planeClazz.get(), planeConstructID,
+                byteBuffer.get(), plane.rowInc, plane.colInc)};
+    }
+
+    ScopedLocalRef<jclass> imageClazz(
+            env, env->FindClass("android/media/MediaCodec$MediaImage"));
+    CHECK(imageClazz.get() != NULL);
+
+    jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>",
+            "([Landroid/media/Image$Plane;IIIZJIILandroid/graphics/Rect;)V");
+
+    jobject img = env->NewObject(imageClazz.get(), imageConstructID,
+            planeArray.get(),
+            view->width(),
+            view->height(),
+            format,
+            (jboolean)readOnly /* readOnly */,
+            (jlong)0 /* timestamp */,
+            (jint)0 /* xOffset */, (jint)0 /* yOffset */, nullptr /* cropRect */);
+
+    // if MediaImage creation fails, return null
+    if (env->ExceptionCheck()) {
+        env->ExceptionDescribe();
+        env->ExceptionClear();
+        return nullptr;
+    }
+
+    return img;
+}
+
+static jobject android_media_MediaCodec_GraphicBlock_native_map(
+        JNIEnv *env, jobject thiz) {
+    JMediaCodecGraphicBlock *context =
+        (JMediaCodecGraphicBlock *)env->GetLongField(thiz, gGraphicBlockInfo.contextId);
+    if (context->mBuffer) {
+        std::shared_ptr<C2Buffer> buffer = context->mBuffer;
+        if (!context->mReadonlyMapping) {
+            const C2BufferData data = buffer->data();
+            if (data.type() != C2BufferData::GRAPHIC) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            if (data.graphicBlocks().size() != 1u) {
+                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                return nullptr;
+            }
+            C2ConstGraphicBlock block = data.graphicBlocks().front();
+            context->mReadonlyMapping =
+                std::make_shared<const C2GraphicView>(block.map().get());
+        }
+        return CreateImage(env, context->mReadonlyMapping);
+    } else if (context->mBlock) {
+        std::shared_ptr<C2GraphicBlock> block = context->mBlock;
+        if (!context->mReadWriteMapping) {
+            context->mReadWriteMapping =
+                std::make_shared<C2GraphicView>(block->map().get());
+        }
+        return CreateImage(env, context->mReadWriteMapping);
+    } else if (context->mLegacyBuffer) {
+    }
+    throwExceptionAsNecessary(env, INVALID_OPERATION);
+    return nullptr;
+}
+
+static void android_media_MediaCodec_GraphicBlock_native_recycle(
+        JNIEnv *env, jobject thiz) {
+    JMediaCodecGraphicBlock *context =
+        (JMediaCodecGraphicBlock *)env->GetLongField(thiz, gGraphicBlockInfo.contextId);
+    env->CallVoidMethod(thiz, gGraphicBlockInfo.setInternalStateId, 0, false /* isMappable */);
+    delete context;
+}
+
+static void android_media_MediaCodec_GraphicBlock_native_obtain(
+        JNIEnv *env, jobject thiz,
+        jint width, jint height, jint format, jlong usage, jobjectArray codecNames) {
+    std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
+    std::vector<std::string> names;
+    PopulateNamesVector(env, codecNames, &names);
+    context->mBlock = MediaCodec::FetchGraphicBlock(width, height, format, usage, names);
+    if (!context->mBlock) {
+        jniThrowException(env, "java/io/IOException", nullptr);
+        return;
+    }
+    env->CallVoidMethod(
+            thiz,
+            gGraphicBlockInfo.setInternalStateId,
+            (jlong)context.release(),
+            true /*isMappable */);
+}
+
+static jboolean android_media_MediaCodec_GraphicBlock_checkCompatible(
+        JNIEnv *env, jobjectArray codecNames) {
+    std::vector<std::string> names;
+    PopulateNamesVector(env, codecNames, &names);
+    bool isCompatible = false;
+    status_t err = MediaCodec::CanFetchGraphicBlock(names, &isCompatible);
+    if (err != OK) {
+        throwExceptionAsNecessary(env, err);
+    }
+    return isCompatible;
+}
+
 static const JNINativeMethod gMethods[] = {
     { "native_release", "()V", (void *)android_media_MediaCodec_release },
 
@@ -2200,6 +3081,19 @@
     { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
       (void *)android_media_MediaCodec_queueSecureInputBuffer },
 
+    { "native_queueLinearBlock",
+      "(ILandroid/media/MediaCodec$LinearBlock;IILandroid/media/MediaCodec$CryptoInfo;JI"
+      "Ljava/util/ArrayList;Ljava/util/ArrayList;)V",
+      (void *)android_media_MediaCodec_native_queueLinearBlock },
+
+    { "native_queueGraphicBlock",
+      "(ILandroid/media/MediaCodec$GraphicBlock;JILjava/util/ArrayList;Ljava/util/ArrayList;)V",
+      (void *)android_media_MediaCodec_native_queueGraphicBlock },
+
+    { "native_getOutputFrame",
+      "(Landroid/media/MediaCodec$OutputFrame;I)V",
+      (void *)android_media_MediaCodec_native_getOutputFrame },
+
     { "native_dequeueInputBuffer", "(J)I",
       (void *)android_media_MediaCodec_dequeueInputBuffer },
 
@@ -2254,7 +3148,50 @@
       (void *)android_media_MediaCodec_native_finalize },
 };
 
+static const JNINativeMethod gLinearBlockMethods[] = {
+    { "native_map", "()Ljava/nio/ByteBuffer;",
+      (void *)android_media_MediaCodec_LinearBlock_native_map },
+
+    { "native_recycle", "()V",
+      (void *)android_media_MediaCodec_LinearBlock_native_recycle },
+
+    { "native_obtain", "(I[Ljava/lang/String;)V",
+      (void *)android_media_MediaCodec_LinearBlock_native_obtain },
+
+    { "native_checkCompatible", "([Ljava/lang/String;)Z",
+      (void *)android_media_MediaCodec_LinearBlock_checkCompatible },
+};
+
+static const JNINativeMethod gGraphicBlockMethods[] = {
+    { "native_map", "()Landroid/media/Image;",
+      (void *)android_media_MediaCodec_GraphicBlock_native_map },
+
+    { "native_recycle", "()V",
+      (void *)android_media_MediaCodec_GraphicBlock_native_recycle },
+
+    { "native_obtain", "(IIIJ[Ljava/lang/String;)V",
+      (void *)android_media_MediaCodec_GraphicBlock_native_obtain },
+
+    { "native_checkCompatible", "([Ljava/lang/String;)Z",
+      (void *)android_media_MediaCodec_GraphicBlock_checkCompatible },
+};
+
 int register_android_media_MediaCodec(JNIEnv *env) {
-    return AndroidRuntime::registerNativeMethods(env,
+    int result = AndroidRuntime::registerNativeMethods(env,
                 "android/media/MediaCodec", gMethods, NELEM(gMethods));
+    if (result != JNI_OK) {
+        return result;
+    }
+    result = AndroidRuntime::registerNativeMethods(env,
+                "android/media/MediaCodec$LinearBlock",
+                gLinearBlockMethods,
+                NELEM(gLinearBlockMethods));
+    if (result != JNI_OK) {
+        return result;
+    }
+    result = AndroidRuntime::registerNativeMethods(env,
+                "android/media/MediaCodec$GraphicBlock",
+                gGraphicBlockMethods,
+                NELEM(gGraphicBlockMethods));
+    return result;
 }
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index ce1c805..1d12e77 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -27,6 +27,8 @@
 #include <media/stagefright/foundation/AHandler.h>
 #include <utils/Errors.h>
 
+class C2Buffer;
+
 namespace android {
 
 struct ABuffer;
@@ -39,6 +41,7 @@
 struct PersistentSurface;
 class Surface;
 namespace hardware {
+class HidlMemory;
 namespace cas {
 namespace native {
 namespace V1_0 {
@@ -97,6 +100,26 @@
             uint32_t flags,
             AString *errorDetailMsg);
 
+    status_t queueBuffer(
+            size_t index, const std::shared_ptr<C2Buffer> &buffer,
+            int64_t timeUs, uint32_t flags, const sp<AMessage> &tunings,
+            AString *errorDetailMsg);
+
+    status_t queueEncryptedLinearBlock(
+            size_t index,
+            const sp<hardware::HidlMemory> &buffer,
+            size_t offset,
+            const CryptoPlugin::SubSample *subSamples,
+            size_t numSubSamples,
+            const uint8_t key[16],
+            const uint8_t iv[16],
+            CryptoPlugin::Mode mode,
+            const CryptoPlugin::Pattern &pattern,
+            int64_t presentationTimeUs,
+            uint32_t flags,
+            const sp<AMessage> &tunings,
+            AString *errorDetailMsg);
+
     status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs);
 
     status_t dequeueOutputBuffer(
@@ -120,6 +143,9 @@
     status_t getImage(
             JNIEnv *env, bool input, size_t index, jobject *image) const;
 
+    status_t getOutputFrame(
+            JNIEnv *env, jobject frame, size_t index) const;
+
     status_t getName(JNIEnv *env, jstring *name) const;
 
     status_t getCodecInfo(JNIEnv *env, jobject *codecInfo) const;
@@ -147,17 +173,10 @@
     jweak mObject;
     sp<Surface> mSurfaceTextureClient;
 
-    // java objects cached
-    jclass mByteBufferClass;
-    jobject mNativeByteOrderObj;
-    jmethodID mByteBufferOrderMethodID;
-    jmethodID mByteBufferPositionMethodID;
-    jmethodID mByteBufferLimitMethodID;
-    jmethodID mByteBufferAsReadOnlyBufferMethodID;
-
     sp<ALooper> mLooper;
     sp<MediaCodec> mCodec;
     AString mNameAtCreation;
+    bool mGraphicOutput{false};
     std::once_flag mReleaseFlag;
 
     sp<AMessage> mCallbackNotification;
@@ -170,8 +189,6 @@
             JNIEnv *env, bool readOnly, bool clearBuffer, const sp<T> &buffer,
             jobject *buf) const;
 
-    void cacheJavaObjects(JNIEnv *env);
-    void deleteJavaObjects(JNIEnv *env);
     void handleCallback(const sp<AMessage> &msg);
     void handleFrameRenderedNotification(const sp<AMessage> &msg);
 
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index ea8a521..b8eb543 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -33,10 +33,14 @@
 
 int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) {
     if (NULL == env || NULL == jbitmap) {
-        return ADATASPACE_UNKNOWN; // Or return a real error?
+        return ADATASPACE_UNKNOWN;
     }
 
     android::graphics::Bitmap bitmap(env, jbitmap);
+    if (!bitmap.isValid()) {
+        return ADATASPACE_UNKNOWN;
+    }
+
     return bitmap.getDataSpace();
 }
 
@@ -76,12 +80,27 @@
     return ANDROID_BITMAP_RESULT_SUCCESS;
 }
 
+int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject jbitmap, AHardwareBuffer** outBuffer) {
+    if (NULL == env || NULL == jbitmap || NULL == outBuffer) {
+        return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+    }
+
+    android::graphics::Bitmap bitmap(env, jbitmap);
+
+    if (!bitmap.isValid()) {
+        return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+    }
+
+    *outBuffer = bitmap.getHardwareBuffer();
+    return *outBuffer == NULL ? ANDROID_BITMAP_RESULT_BAD_PARAMETER : ANDROID_BITMAP_RESULT_SUCCESS;
+}
+
 int AndroidBitmap_compress(const AndroidBitmapInfo* info,
                            int32_t dataSpace,
                            const void* pixels,
                            int32_t format, int32_t quality,
                            void* userContext,
-                           AndroidBitmap_compress_write_fn fn) {
+                           AndroidBitmap_CompressWriteFunc fn) {
     if (NULL == info || NULL == pixels || NULL == fn) {
         return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
     }
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 5143967..1c45ea6 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -18,12 +18,14 @@
 
 #include <android/asset_manager.h>
 #include <android/bitmap.h>
+#include <android/data_space.h>
 #include <android/imagedecoder.h>
 #include <android/graphics/MimeType.h>
 #include <android/rect.h>
 #include <hwui/ImageDecoder.h>
 #include <log/log.h>
 #include <SkAndroidCodec.h>
+#include <utils/Color.h>
 
 #include <fcntl.h>
 #include <optional>
@@ -131,6 +133,10 @@
     return reinterpret_cast<ImageDecoder*>(d);
 }
 
+static const ImageDecoder* toDecoder(const AImageDecoder* d) {
+    return reinterpret_cast<const ImageDecoder*>(d);
+}
+
 // Note: This differs from the version in android_bitmap.cpp in that this
 // version returns kGray_8_SkColorType for ANDROID_BITMAP_FORMAT_A_8. SkCodec
 // allows decoding single channel images to gray, which Android then treats
@@ -161,6 +167,18 @@
             ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
 }
 
+int AImageDecoder_setDataSpace(AImageDecoder* decoder, int32_t dataspace) {
+    sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace);
+    // 0 is ADATASPACE_UNKNOWN. We need an explicit request for an ADataSpace.
+    if (!decoder || !dataspace || !cs) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    ImageDecoder* imageDecoder = toDecoder(decoder);
+    imageDecoder->setOutColorSpace(std::move(cs));
+    return ANDROID_IMAGE_DECODER_SUCCESS;
+}
+
 const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(const AImageDecoder* decoder) {
     return reinterpret_cast<const AImageDecoderHeaderInfo*>(decoder);
 }
@@ -197,6 +215,20 @@
     return toDecoder(info)->mCodec->codec()->getFrameCount() > 1;
 }
 
+int32_t AImageDecoderHeaderInfo_getDataSpace(const AImageDecoderHeaderInfo* info) {
+    if (!info) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    // Note: This recomputes the data space because it's possible the client has
+    // changed the output color space, so we cannot rely on it. Alternatively,
+    // we could store the ADataSpace in the ImageDecoder.
+    const ImageDecoder* imageDecoder = toDecoder(info);
+    SkColorType colorType = imageDecoder->mCodec->computeOutputColorType(kN32_SkColorType);
+    sk_sp<SkColorSpace> colorSpace = imageDecoder->mCodec->computeOutputColorSpace(colorType);
+    return uirenderer::ColorSpaceToADataSpace(colorSpace.get(), colorType);
+}
+
 // FIXME: Share with getFormat in android_bitmap.cpp?
 static AndroidBitmapFormat getFormat(SkColorType colorType) {
     switch (colorType) {
@@ -258,6 +290,18 @@
             ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_SCALE;
 }
 
+int AImageDecoder_computeSampledSize(const AImageDecoder* decoder, int sampleSize,
+                                     int* width, int* height) {
+    if (!decoder || !width || !height || sampleSize < 1) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    SkISize size = toDecoder(decoder)->mCodec->getSampledDimensions(sampleSize);
+    *width = size.width();
+    *height = size.height();
+    return ANDROID_IMAGE_DECODER_SUCCESS;
+}
+
 int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) {
     if (!decoder) {
         return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index 6843e7a..1b396b8 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -6,10 +6,12 @@
     AImageDecoder_delete; # introduced=30
     AImageDecoder_setAndroidBitmapFormat; # introduced=30
     AImageDecoder_setUnpremultipliedRequired; # introduced=30
+    AImageDecoder_setDataSpace; # introduced=30
     AImageDecoder_getHeaderInfo; # introduced=30
     AImageDecoder_getMinimumStride; # introduced=30
     AImageDecoder_decodeImage; # introduced=30
     AImageDecoder_setTargetSize; # introduced=30
+    AImageDecoder_computeSampledSize; # introduced=30
     AImageDecoder_setCrop; # introduced=30
     AImageDecoderHeaderInfo_getWidth; # introduced=30
     AImageDecoderHeaderInfo_getHeight; # introduced=30
@@ -17,11 +19,13 @@
     AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30
     AImageDecoderHeaderInfo_isAnimated; # introduced=30
     AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30
+    AImageDecoderHeaderInfo_getDataSpace; # introduced=30
     AndroidBitmap_getInfo;
     AndroidBitmap_getDataSpace;
     AndroidBitmap_lockPixels;
     AndroidBitmap_unlockPixels;
     AndroidBitmap_compress; # introduced=30
+    AndroidBitmap_getHardwareBuffer; #introduced=30
   local:
     *;
 };
diff --git a/packages/SystemUI/res/drawable/ic_screenrecord.xml b/packages/SystemUI/res/drawable/ic_screenrecord.xml
new file mode 100644
index 0000000..6d8bd0d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screenrecord.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
+        android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 639005b..44a7fda 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -194,6 +194,10 @@
     <!-- Power menu item for taking a screenshot [CHAR LIMIT=20]-->
     <string name="global_action_screenshot">Screenshot</string>
 
+    <!-- text to show in place of RemoteInput images when they cannot be shown.
+         [CHAR LIMIT=50] -->
+    <string name="remote_input_image_insertion_text">Image inserted</string>
+
     <!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
     <string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
     <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
@@ -222,6 +226,8 @@
     <string name="screenrecord_mic_label">Record voiceover</string>
     <!-- Label for the checkbox to enable showing location of touches during screen recording [CHAR LIMIT=NONE]-->
     <string name="screenrecord_taps_label">Show taps</string>
+    <!-- Label for notification that the user can tap to stop and save the screen recording [CHAR LIMIT=NONE] -->
+    <string name="screenrecord_stop_text">Tap to stop</string>
     <!-- Label for notification action to stop and save the screen recording [CHAR LIMIT=35] -->
     <string name="screenrecord_stop_label">Stop</string>
     <!-- Label for notification action to pause screen recording [CHAR LIMIT=35] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 5d35169..f61f585 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -140,6 +140,13 @@
         mLayoutTransition.setAnimateParentHierarchy(false);
     }
 
+    // Temporary workaround to allow KeyguardStatusView to inflate a copy for Universal Smartspace.
+    // Eventually the existing copy will be reparented instead, and we won't need this.
+    public KeyguardSliceView(Context context, AttributeSet attributeSet) {
+        this(context, attributeSet, Dependency.get(ActivityStarter.class),
+                Dependency.get(ConfigurationController.class));
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 5a1c997..61caf3b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -18,10 +18,15 @@
 
 import android.app.ActivityManager;
 import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.PixelFormat;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -30,7 +35,11 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.TypedValue;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
 import android.widget.GridLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -40,6 +49,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.shared.system.UniversalSmartspaceUtils;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.FileDescriptor;
@@ -76,6 +86,7 @@
     private int mIconTopMargin;
     private int mIconTopMarginWithHeader;
     private boolean mShowingHeader;
+    private SurfaceControlViewHost mUniversalSmartspaceViewHost;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -122,6 +133,38 @@
         }
     };
 
+    private BroadcastReceiver mUniversalSmartspaceBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent i) {
+            // TODO(b/148159743): Restrict to Pixel Launcher.
+            if (UniversalSmartspaceUtils.ACTION_REQUEST_SMARTSPACE_VIEW.equals(i.getAction())) {
+                if (mUniversalSmartspaceViewHost != null) {
+                    mUniversalSmartspaceViewHost.die();
+                }
+                SurfaceControl surfaceControl = UniversalSmartspaceUtils.getSurfaceControl(i);
+                if (surfaceControl != null) {
+                    IBinder input = UniversalSmartspaceUtils.getInputToken(i);
+
+                    WindowlessWindowManager windowlessWindowManager =
+                            new WindowlessWindowManager(context.getResources().getConfiguration(),
+                                    surfaceControl, input);
+                    mUniversalSmartspaceViewHost = new SurfaceControlViewHost(context,
+                            context.getDisplay(), windowlessWindowManager);
+                    WindowManager.LayoutParams layoutParams =
+                            new WindowManager.LayoutParams(
+                                    surfaceControl.getWidth(),
+                                    surfaceControl.getHeight(),
+                                    WindowManager.LayoutParams.TYPE_APPLICATION,
+                                    0,
+                                    PixelFormat.TRANSPARENT);
+
+                    mUniversalSmartspaceViewHost.addView(
+                            inflate(context, R.layout.keyguard_status_area, null), layoutParams);
+                }
+            }
+        }
+    };;
+
     public KeyguardStatusView(Context context) {
         this(context, null, 0);
     }
@@ -316,6 +359,8 @@
         super.onAttachedToWindow();
         Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback);
         Dependency.get(ConfigurationController.class).addCallback(this);
+        getContext().registerReceiver(mUniversalSmartspaceBroadcastReceiver,
+                new IntentFilter(UniversalSmartspaceUtils.ACTION_REQUEST_SMARTSPACE_VIEW));
     }
 
     @Override
@@ -323,6 +368,7 @@
         super.onDetachedFromWindow();
         Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback);
         Dependency.get(ConfigurationController.class).removeCallback(this);
+        getContext().unregisterReceiver(mUniversalSmartspaceBroadcastReceiver);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f03648a..f51cd83 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1089,7 +1089,7 @@
                 // keep compatibility with apps that aren't direct boot aware.
                 // SysUI should just ignore this broadcast because it was already received
                 // and processed previously.
-                if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+                if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                     // Guarantee mTelephonyCapable state after SysUI crash and restart
                     if (args.simState == TelephonyManager.SIM_STATE_ABSENT) {
                         mHandler.obtainMessage(MSG_TELEPHONY_CAPABLE, true).sendToTarget();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index f30c181..79a7df2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -111,7 +111,9 @@
 
         final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
         for (String spec : possibleTiles) {
-            // Only add current and stock tiles that can be created from QSFactoryImpl
+            // Only add current and stock tiles that can be created from QSFactoryImpl.
+            // Do not include CustomTile. Those will be created by `addPackageTiles`.
+            if (spec.startsWith(CustomTile.PREFIX)) continue;
             final QSTile tile = host.createTile(spec);
             if (tile == null) {
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 1b32168..b091ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -34,6 +34,7 @@
 import android.media.projection.MediaProjection;
 import android.media.projection.MediaProjectionManager;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.provider.MediaStore;
 import android.provider.Settings;
@@ -72,9 +73,6 @@
 
     private static final String ACTION_START = "com.android.systemui.screenrecord.START";
     private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
-    private static final String ACTION_PAUSE = "com.android.systemui.screenrecord.PAUSE";
-    private static final String ACTION_RESUME = "com.android.systemui.screenrecord.RESUME";
-    private static final String ACTION_CANCEL = "com.android.systemui.screenrecord.CANCEL";
     private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
     private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
 
@@ -94,6 +92,7 @@
 
     private boolean mUseAudio;
     private boolean mShowTaps;
+    private boolean mOriginalShowTaps;
     private File mTempFile;
 
     @Inject
@@ -145,38 +144,11 @@
                 }
                 break;
 
-            case ACTION_CANCEL:
-                stopRecording();
-
-                // Delete temp file
-                if (!mTempFile.delete()) {
-                    Log.e(TAG, "Error canceling screen recording!");
-                    Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
-                            .show();
-                } else {
-                    Toast.makeText(this, R.string.screenrecord_cancel_success, Toast.LENGTH_LONG)
-                            .show();
-                }
-
-                // Close quick shade
-                sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-                break;
-
             case ACTION_STOP:
                 stopRecording();
                 saveRecording(notificationManager);
                 break;
 
-            case ACTION_PAUSE:
-                mMediaRecorder.pause();
-                setNotificationActions(true, notificationManager);
-                break;
-
-            case ACTION_RESUME:
-                mMediaRecorder.resume();
-                setNotificationActions(false, notificationManager);
-                break;
-
             case ACTION_SHARE:
                 Uri shareUri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
 
@@ -233,9 +205,14 @@
      */
     private void startRecording() {
         try {
-            mTempFile = File.createTempFile("temp", ".mp4");
+            File cacheDir = getCacheDir();
+            cacheDir.mkdirs();
+            mTempFile = File.createTempFile("temp", ".mp4", cacheDir);
             Log.d(TAG, "Writing video output to: " + mTempFile.getAbsolutePath());
 
+            mOriginalShowTaps = 1 == Settings.System.getInt(
+                    getApplicationContext().getContentResolver(),
+                    Settings.System.SHOW_TOUCHES, 0);
             setTapsVisible(mShowTaps);
 
             // Set up media recorder
@@ -295,59 +272,42 @@
         NotificationChannel channel = new NotificationChannel(
                 CHANNEL_ID,
                 getString(R.string.screenrecord_name),
-                NotificationManager.IMPORTANCE_LOW);
+                NotificationManager.IMPORTANCE_DEFAULT);
         channel.setDescription(getString(R.string.screenrecord_channel_description));
         channel.enableVibration(true);
         NotificationManager notificationManager =
                 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.createNotificationChannel(channel);
 
+        Bundle extras = new Bundle();
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+                getResources().getString(R.string.screenrecord_name));
+
         mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID)
-                .setSmallIcon(R.drawable.ic_android)
+                .setSmallIcon(R.drawable.ic_screenrecord)
                 .setContentTitle(getResources().getString(R.string.screenrecord_name))
+                .setContentText(getResources().getString(R.string.screenrecord_stop_text))
                 .setUsesChronometer(true)
-                .setOngoing(true);
-        setNotificationActions(false, notificationManager);
+                .setColorized(true)
+                .setColor(getResources().getColor(R.color.GM2_red_700))
+                .setOngoing(true)
+                .setContentIntent(
+                        PendingIntent.getService(
+                                this, REQUEST_CODE, getStopIntent(this),
+                                PendingIntent.FLAG_UPDATE_CURRENT))
+                .addExtras(extras);
+        notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build());
         Notification notification = mRecordingNotificationBuilder.build();
         startForeground(NOTIFICATION_ID, notification);
     }
 
-    private void setNotificationActions(boolean isPaused, NotificationManager notificationManager) {
-        String pauseString = getResources()
-                .getString(isPaused ? R.string.screenrecord_resume_label
-                        : R.string.screenrecord_pause_label);
-        Intent pauseIntent = isPaused ? getResumeIntent(this) : getPauseIntent(this);
-
-        mRecordingNotificationBuilder.setActions(
-                new Notification.Action.Builder(
-                        Icon.createWithResource(this, R.drawable.ic_android),
-                        getResources().getString(R.string.screenrecord_stop_label),
-                        PendingIntent
-                                .getService(this, REQUEST_CODE, getStopIntent(this),
-                                        PendingIntent.FLAG_UPDATE_CURRENT))
-                        .build(),
-                new Notification.Action.Builder(
-                        Icon.createWithResource(this, R.drawable.ic_android), pauseString,
-                        PendingIntent.getService(this, REQUEST_CODE, pauseIntent,
-                                PendingIntent.FLAG_UPDATE_CURRENT))
-                        .build(),
-                new Notification.Action.Builder(
-                        Icon.createWithResource(this, R.drawable.ic_android),
-                        getResources().getString(R.string.screenrecord_cancel_label),
-                        PendingIntent
-                                .getService(this, REQUEST_CODE, getCancelIntent(this),
-                                        PendingIntent.FLAG_UPDATE_CURRENT))
-                        .build());
-        notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build());
-    }
-
     private Notification createSaveNotification(Uri uri) {
         Intent viewIntent = new Intent(Intent.ACTION_VIEW)
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION)
                 .setDataAndType(uri, "video/mp4");
 
         Notification.Action shareAction = new Notification.Action.Builder(
-                Icon.createWithResource(this, R.drawable.ic_android),
+                Icon.createWithResource(this, R.drawable.ic_screenrecord),
                 getResources().getString(R.string.screenrecord_share_label),
                 PendingIntent.getService(
                         this,
@@ -357,7 +317,7 @@
                 .build();
 
         Notification.Action deleteAction = new Notification.Action.Builder(
-                Icon.createWithResource(this, R.drawable.ic_android),
+                Icon.createWithResource(this, R.drawable.ic_screenrecord),
                 getResources().getString(R.string.screenrecord_delete_label),
                 PendingIntent.getService(
                         this,
@@ -366,8 +326,12 @@
                         PendingIntent.FLAG_UPDATE_CURRENT))
                 .build();
 
+        Bundle extras = new Bundle();
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+                getResources().getString(R.string.screenrecord_name));
+
         Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
-                .setSmallIcon(R.drawable.ic_android)
+                .setSmallIcon(R.drawable.ic_screenrecord)
                 .setContentTitle(getResources().getString(R.string.screenrecord_name))
                 .setContentText(getResources().getString(R.string.screenrecord_save_message))
                 .setContentIntent(PendingIntent.getActivity(
@@ -377,7 +341,8 @@
                         Intent.FLAG_GRANT_READ_URI_PERMISSION))
                 .addAction(shareAction)
                 .addAction(deleteAction)
-                .setAutoCancel(true);
+                .setAutoCancel(true)
+                .addExtras(extras);
 
         // Add thumbnail if available
         Bitmap thumbnailBitmap = null;
@@ -400,7 +365,7 @@
     }
 
     private void stopRecording() {
-        setTapsVisible(false);
+        setTapsVisible(mOriginalShowTaps);
         mMediaRecorder.stop();
         mMediaRecorder.release();
         mMediaRecorder = null;
@@ -459,18 +424,6 @@
         return new Intent(context, RecordingService.class).setAction(ACTION_STOP);
     }
 
-    private static Intent getPauseIntent(Context context) {
-        return new Intent(context, RecordingService.class).setAction(ACTION_PAUSE);
-    }
-
-    private static Intent getResumeIntent(Context context) {
-        return new Intent(context, RecordingService.class).setAction(ACTION_RESUME);
-    }
-
-    private static Intent getCancelIntent(Context context) {
-        return new Intent(context, RecordingService.class).setAction(ACTION_CANCEL);
-    }
-
     private static Intent getShareIntent(Context context, String path) {
         return new Intent(context, RecordingService.class).setAction(ACTION_SHARE)
                 .putExtra(EXTRA_PATH, path);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 8cc45f2..4d6764e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -195,6 +195,7 @@
                     new ArrayList<>(),
                     new ArrayList<>(),
                     false,
+                    false,
                     false
             );
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 667e721..f3783c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -25,8 +25,10 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
+import android.app.RemoteInputHistoryItem;
 import android.content.Context;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -345,7 +347,8 @@
         });
         mSmartReplyController.setCallback((entry, reply) -> {
             StatusBarNotification newSbn =
-                    rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */);
+                    rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */,
+                            null /* mimeType */, null /* uri */);
             mEntryManager.updateNotification(newSbn, null /* ranking */);
         });
     }
@@ -527,28 +530,36 @@
     StatusBarNotification rebuildNotificationForCanceledSmartReplies(
             NotificationEntry entry) {
         return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
-                false /* showSpinner */);
+                false /* showSpinner */, null /* mimeType */, null /* uri */);
     }
 
     @VisibleForTesting
     StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry,
-            CharSequence remoteInputText, boolean showSpinner) {
+            CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) {
         StatusBarNotification sbn = entry.getSbn();
 
         Notification.Builder b = Notification.Builder
                 .recoverBuilder(mContext, sbn.getNotification().clone());
-        if (remoteInputText != null) {
-            CharSequence[] oldHistory = sbn.getNotification().extras
-                    .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
-            CharSequence[] newHistory;
-            if (oldHistory == null) {
-                newHistory = new CharSequence[1];
+        if (remoteInputText != null || uri != null) {
+            RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[])
+                    sbn.getNotification().extras.getParcelableArray(
+                            Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+            RemoteInputHistoryItem[] newHistoryItems;
+
+            if (oldHistoryItems == null) {
+                newHistoryItems = new RemoteInputHistoryItem[1];
             } else {
-                newHistory = new CharSequence[oldHistory.length + 1];
-                System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
+                newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1];
+                System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length);
             }
-            newHistory[0] = String.valueOf(remoteInputText);
-            b.setRemoteInputHistory(newHistory);
+            RemoteInputHistoryItem newItem;
+            if (uri != null) {
+                newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText);
+            } else {
+                newItem = new RemoteInputHistoryItem(remoteInputText);
+            }
+            newHistoryItems[0] = newItem;
+            b.setRemoteInputHistory(newHistoryItems);
         }
         b.setShowRemoteInputSpinner(showSpinner);
         b.setHideSmartReplies(true);
@@ -631,8 +642,11 @@
                 if (TextUtils.isEmpty(remoteInputText)) {
                     remoteInputText = entry.remoteInputTextWhenReset;
                 }
+                String remoteInputMimeType = entry.remoteInputMimeType;
+                Uri remoteInputUri = entry.remoteInputUri;
                 StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry,
-                        remoteInputText, false /* showSpinner */);
+                        remoteInputText, false /* showSpinner */, remoteInputMimeType,
+                        remoteInputUri);
                 entry.onRemoteInputInserted();
 
                 if (newSbn == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 2fcfb8c..1f77ec2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -37,8 +37,10 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager.Policy;
 import android.app.Person;
+import android.app.RemoteInputHistoryItem;
 import android.content.Context;
 import android.graphics.drawable.Icon;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService.Ranking;
@@ -120,6 +122,8 @@
     public int targetSdk;
     private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
     public CharSequence remoteInputText;
+    public String remoteInputMimeType;
+    public Uri remoteInputUri;
     private Notification.BubbleMetadata mBubbleMetadata;
 
     /**
@@ -595,8 +599,8 @@
             return false;
         }
         Bundle extras = mSbn.getNotification().extras;
-        CharSequence[] replyTexts = extras.getCharSequenceArray(
-                Notification.EXTRA_REMOTE_INPUT_HISTORY);
+        RemoteInputHistoryItem[] replyTexts = (RemoteInputHistoryItem[]) extras.getParcelableArray(
+                Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
         if (!ArrayUtils.isEmpty(replyTexts)) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index f7fe064..1eeeab3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.collection
 
-import android.app.NotificationChannel
 import android.app.NotificationManager.IMPORTANCE_HIGH
 import android.app.NotificationManager.IMPORTANCE_MIN
 import android.service.notification.NotificationListenerService.Ranking
@@ -192,9 +191,7 @@
     }
 
     private fun NotificationEntry.isPeopleNotification() =
-            sbn.isPeopleNotification(channel)
-    private fun StatusBarNotification.isPeopleNotification(channel: NotificationChannel) =
-            peopleNotificationIdentifier.isPeopleNotification(this, channel)
+            peopleNotificationIdentifier.isPeopleNotification(sbn, ranking)
 
     private fun NotificationEntry.isHighPriority() =
             highPriorityProvider.isHighPriority(this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index ccd7fa3..b496116 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -100,7 +100,7 @@
 
     private boolean isPeopleNotification(NotificationEntry entry) {
         return mPeopleNotificationIdentifier.isPeopleNotification(
-                entry.getSbn(), entry.getChannel());
+                entry.getSbn(), entry.getRanking());
     }
 
     private boolean hasUserSetImportance(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 5c90211..4672de0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -16,31 +16,20 @@
 
 package com.android.systemui.statusbar.notification.people
 
-import android.app.Notification
-import android.content.Context
-import android.app.NotificationChannel
+import android.service.notification.NotificationListenerService.Ranking
 import android.service.notification.StatusBarNotification
-import android.util.FeatureFlagUtils
 import javax.inject.Inject
 import javax.inject.Singleton
 
 interface PeopleNotificationIdentifier {
-    fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel): Boolean
+    fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
 }
 
 @Singleton
 class PeopleNotificationIdentifierImpl @Inject constructor(
-    private val personExtractor: NotificationPersonExtractor,
-    private val context: Context
+    private val personExtractor: NotificationPersonExtractor
 ) : PeopleNotificationIdentifier {
 
-    override fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel) =
-            ((sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
-                    (sbn.notification.shortcutId != null ||
-                            FeatureFlagUtils.isEnabled(
-                                    context,
-                                    FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ
-                            ))) ||
-                    personExtractor.isPersonNotification(sbn)) &&
-                    !channel.isDemoted
+    override fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) =
+            ranking.isConversation || personExtractor.isPersonNotification(sbn)
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index a3e1305..fa4bc2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -80,7 +80,15 @@
     public Drawable loadImage(Uri uri) {
         Drawable result = null;
         try {
-            result = hasCache() ? mImageCache.get(uri) : resolveImage(uri);
+            if (hasCache()) {
+                // if the uri isn't currently cached, try caching it first
+                if (!mImageCache.hasEntry(uri)) {
+                    mImageCache.preload((uri));
+                }
+                result = mImageCache.get(uri);
+            } else {
+                result = resolveImage(uri);
+            }
         } catch (IOException | SecurityException ex) {
             Log.d(TAG, "loadImage: Can't load image from " + uri, ex);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 00e38f8..7558022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -616,8 +616,7 @@
                     break;
                 case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                     // Avoid rebroadcast because SysUI is direct boot aware.
-                    if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,
-                            false)) {
+                    if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                         break;
                     }
                     updateSimState(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index cca100f..a76d41c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -27,6 +27,7 @@
 import android.os.Message;
 import android.provider.Settings.Global;
 import android.telephony.Annotation;
+import android.telephony.CdmaEriInformation;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
 import android.telephony.NetworkRegistrationInfo;
@@ -41,7 +42,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.EriInfo;
 import com.android.settingslib.Utils;
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.net.SignalStrengthUtil;
@@ -415,10 +415,10 @@
             return false;
         }
         if (isCdma() && mServiceState != null) {
-            final int iconMode = mServiceState.getCdmaEriIconMode();
-            return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF
-                    && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
-                    || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
+            final int iconMode = mPhone.getCdmaEriInformation().getEriIconMode();
+            return mPhone.getCdmaEriInformation().getEriIconIndex() != CdmaEriInformation.ERI_OFF
+                    && (iconMode == CdmaEriInformation.ERI_ICON_MODE_NORMAL
+                    || iconMode == CdmaEriInformation.ERI_ICON_MODE_FLASH);
         } else {
             return mServiceState != null && mServiceState.getRoaming();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 6b3c5dc..899279d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -526,7 +526,7 @@
                 break;
             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                 // Avoid rebroadcast because SysUI is direct boot aware.
-                if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+                if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                     break;
                 }
                 // Might have different subscriptions now.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 307e3bc..408b3a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -161,6 +161,11 @@
         Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
                 results);
+
+        mEntry.remoteInputText = mEditText.getText();
+        mEntry.remoteInputUri = null;
+        mEntry.remoteInputMimeType = null;
+
         if (mEntry.editedSuggestionInfo == null) {
             RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT);
         } else {
@@ -177,6 +182,10 @@
         Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);
 
+        mEntry.remoteInputText = mContext.getString(R.string.remote_input_image_insertion_text);
+        mEntry.remoteInputMimeType = contentType;
+        mEntry.remoteInputUri = data;
+
         return fillInIntent;
     }
 
@@ -184,7 +193,6 @@
         mEditText.setEnabled(false);
         mSendButton.setVisibility(INVISIBLE);
         mProgressBar.setVisibility(VISIBLE);
-        mEntry.remoteInputText = mEditText.getText();
         mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
         mController.addSpinning(mEntry.getKey(), mToken);
         mController.removeRemoteInput(mEntry, mToken);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index b3c2ba3..59eb6c5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -170,7 +170,7 @@
         Assert.assertTrue("onSimStateChanged not called",
                 mKeyguardUpdateMonitor.hasSimStateJustChanged());
 
-        intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
+        intent.putExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, true);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
         mTestableLooper.processAllMessages();
         Assert.assertFalse("onSimStateChanged should have been skipped",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 2ecc8ea..b3071f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -19,8 +19,11 @@
 import android.content.Context;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 
 public class TestableDependency extends Dependency {
+    private static final String TAG = "TestableDependency";
+
     private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
     private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>();
 
@@ -44,7 +47,7 @@
 
     public <T> void injectTestDependency(Class<T> key, T obj) {
         if (mInstantiatedObjects.contains(key)) {
-            throw new IllegalStateException(key + " was already initialized");
+            Log.d(TAG, key + " was already initialized but overriding with testDependency.");
         }
         mObjs.put(key, obj);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 8a9a7a2..4c68102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -77,12 +78,14 @@
     private static final Set<String> FACTORY_TILES = new ArraySet<>();
     private static final String TEST_PKG = "test_pkg";
     private static final String TEST_CLS = "test_cls";
+    private static final String CUSTOM_TILE = "custom(" + TEST_PKG + "/" + TEST_CLS + ")";
 
     static {
         FACTORY_TILES.addAll(Arrays.asList(
                 new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work",
                         "rotation", "flashlight", "location", "cast", "hotspot", "user", "battery",
                         "saver", "night", "nfc"}));
+        FACTORY_TILES.add(CUSTOM_TILE);
     }
 
     @Mock
@@ -223,6 +226,15 @@
     }
 
     @Test
+    public void testCustomTileNotCreated() {
+        Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+                CUSTOM_TILE);
+        mTileQueryHelper.queryTiles(mQSTileHost);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
+        verify(mQSTileHost, never()).createTile(CUSTOM_TILE);
+    }
+
+    @Test
     public void testThirdPartyTilesInactive() {
         ResolveInfo resolveInfo = new ResolveInfo();
         ServiceInfo serviceInfo = mock(ServiceInfo.class, Answers.RETURNS_MOCKS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 64b10c8..1117646 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -10,7 +10,9 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
+import android.app.RemoteInputHistoryItem;
 import android.content.Context;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
@@ -150,13 +152,30 @@
     }
 
     @Test
+    public void testRebuildWithRemoteInput_noExistingInput_image() {
+        Uri uri = mock(Uri.class);
+        String mimeType  = "image/jpeg";
+        String text = "image inserted";
+        StatusBarNotification newSbn =
+                mRemoteInputManager.rebuildNotificationWithRemoteInput(
+                        mEntry, text, false, mimeType, uri);
+        RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+                .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+        assertEquals(1, messages.length);
+        assertEquals(text, messages[0].getText());
+        assertEquals(mimeType, messages[0].getMimeType());
+        assertEquals(uri, messages[0].getUri());
+    }
+
+    @Test
     public void testRebuildWithRemoteInput_noExistingInputNoSpinner() {
         StatusBarNotification newSbn =
-                mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
-        CharSequence[] messages = newSbn.getNotification().extras
-                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+                mRemoteInputManager.rebuildNotificationWithRemoteInput(
+                        mEntry, "A Reply", false, null, null);
+        RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+                .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
         assertEquals(1, messages.length);
-        assertEquals("A Reply", messages[0]);
+        assertEquals("A Reply", messages[0].getText());
         assertFalse(newSbn.getNotification().extras
                 .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
         assertTrue(newSbn.getNotification().extras
@@ -166,11 +185,12 @@
     @Test
     public void testRebuildWithRemoteInput_noExistingInputWithSpinner() {
         StatusBarNotification newSbn =
-                mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", true);
-        CharSequence[] messages = newSbn.getNotification().extras
-                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+                mRemoteInputManager.rebuildNotificationWithRemoteInput(
+                        mEntry, "A Reply", true, null, null);
+        RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+                .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
         assertEquals(1, messages.length);
-        assertEquals("A Reply", messages[0]);
+        assertEquals("A Reply", messages[0].getText());
         assertTrue(newSbn.getNotification().extras
                 .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
         assertTrue(newSbn.getNotification().extras
@@ -181,18 +201,45 @@
     public void testRebuildWithRemoteInput_withExistingInput() {
         // Setup a notification entry with 1 remote input.
         StatusBarNotification newSbn =
-                mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
+                mRemoteInputManager.rebuildNotificationWithRemoteInput(
+                        mEntry, "A Reply", false, null, null);
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setSbn(newSbn)
                 .build();
 
         // Try rebuilding to add another reply.
-        newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
-        CharSequence[] messages = newSbn.getNotification().extras
-                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+        newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(
+                entry, "Reply 2", true, null, null);
+        RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+                .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
         assertEquals(2, messages.length);
-        assertEquals("Reply 2", messages[0]);
-        assertEquals("A Reply", messages[1]);
+        assertEquals("Reply 2", messages[0].getText());
+        assertEquals("A Reply", messages[1].getText());
+    }
+
+    @Test
+    public void testRebuildWithRemoteInput_withExistingInput_image() {
+        // Setup a notification entry with 1 remote input.
+        Uri uri = mock(Uri.class);
+        String mimeType  = "image/jpeg";
+        String text = "image inserted";
+        StatusBarNotification newSbn =
+                mRemoteInputManager.rebuildNotificationWithRemoteInput(
+                        mEntry, text, false, mimeType, uri);
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setSbn(newSbn)
+                .build();
+
+        // Try rebuilding to add another reply.
+        newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(
+                entry, "Reply 2", true, null, null);
+        RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+                .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+        assertEquals(2, messages.length);
+        assertEquals("Reply 2", messages[0].getText());
+        assertEquals(text, messages[1].getText());
+        assertEquals(mimeType, messages[1].getMimeType());
+        assertEquals(uri, messages[1].getUri());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index 5310dd8..16f105d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -52,6 +52,7 @@
     private ArrayList<CharSequence> mSmartReplies = new ArrayList<>();
     private boolean mCanBubble = false;
     private boolean mIsVisuallyInterruptive = false;
+    private boolean mIsConversation = false;
 
     public RankingBuilder() {
     }
@@ -77,6 +78,7 @@
         mSmartReplies = copyList(ranking.getSmartReplies());
         mCanBubble = ranking.canBubble();
         mIsVisuallyInterruptive = ranking.visuallyInterruptive();
+        mIsConversation = ranking.isConversation();
     }
 
     public Ranking build() {
@@ -101,7 +103,8 @@
                 mSmartActions,
                 mSmartReplies,
                 mCanBubble,
-                mIsVisuallyInterruptive);
+                mIsVisuallyInterruptive,
+                mIsConversation);
         return ranking;
     }
 
@@ -181,6 +184,11 @@
         return this;
     }
 
+    public RankingBuilder setIsConversation(boolean isConversation) {
+        mIsConversation = isConversation;
+        return this;
+    }
+
     public RankingBuilder setImportance(@Importance int importance) {
         mImportance = importance;
         return this;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 4b2ce01..296d0cef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -45,7 +45,6 @@
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
@@ -88,7 +87,6 @@
 import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -99,6 +97,7 @@
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.leak.LeakDetector;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -162,7 +161,8 @@
                     0,
                     IMPORTANCE_DEFAULT,
                     null, null,
-                    null, null, null, true, sentiment, false, -1, false, null, null, false, false);
+                    null, null, null, true, sentiment, false, -1, false, null, null, false, false,
+                    false);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
     }
@@ -181,7 +181,7 @@
                     null, null,
                     null, null, null, true,
                     Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
-                    false, smartActions, null, false, false);
+                    false, smartActions, null, false, false, false);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
     }
@@ -189,15 +189,14 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        if (!mDependency.hasInstantiatedDependency(SmartReplyController.class)) {
-            mDependency.injectMockDependency(SmartReplyController.class);
-        }
+        mDependency.injectMockDependency(SmartReplyController.class);
         mDependency.injectMockDependency(NotificationMediaManager.class);
 
         mCountDownLatch = new CountDownLatch(1);
 
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
-                Handler.createAsync(Looper.myLooper()));
+                Handler.createAsync(TestableLooper.get(this).getLooper()));
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
         when(mListContainer.getViewParentForNotification(any())).thenReturn(
                 new FrameLayout(mContext));
@@ -207,9 +206,10 @@
 
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
-        NotificationRowContentBinder contentBinder = new NotificationContentInflater(
+        NotificationContentInflater contentBinder = new NotificationContentInflater(
                 mock(NotifRemoteViewCache.class),
                 mRemoteInputManager);
+        contentBinder.setInflateSynchronously(true);
 
         when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
                 .thenReturn(mNotificationRowComponentBuilder);
@@ -262,6 +262,12 @@
                 mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
     }
 
+    @After
+    public void tearDown() {
+        // CLEAN UP inflation tasks so they don't callback in a future test
+        mEntry.abortTask();
+    }
+
     @Test
     public void testAddNotification() throws Exception {
         TestableLooper.get(this).processAllMessages();
@@ -347,9 +353,6 @@
 
     @Test
     public void testRemoveNotification() {
-        // Row inflation happens off thread, so pretend that this test looper is main
-        Assert.sMainLooper = TestableLooper.get(this).getLooper();
-
         mEntry.setRow(mRow);
         mEntryManager.addActiveNotificationForTest(mEntry);
 
@@ -467,9 +470,6 @@
 
     @Test
     public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() {
-        // Row inflation happens off thread, so pretend that this test looper is main
-        Assert.sMainLooper = TestableLooper.get(this).getLooper();
-
         // GIVEN an entry manager with a notification whose life has been extended
         mEntryManager.addActiveNotificationForTest(mEntry);
         final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
@@ -560,9 +560,6 @@
 
     @Test
     public void testRemoveInterceptor_notInterceptedGetsRemoved() {
-        // Row inflation happens off thread, so pretend that this test looper is main
-        Assert.sMainLooper = TestableLooper.get(this).getLooper();
-
         // GIVEN an entry manager with a notification
         mEntryManager.addActiveNotificationForTest(mEntry);
 
@@ -625,7 +622,6 @@
 
     @Test
     public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
-        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         Notification.Builder n = new Notification.Builder(mContext, "di")
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 7c3665b..e4865b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -60,7 +60,7 @@
         final NotificationEntry entry = new NotificationEntryBuilder()
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
                 .thenReturn(false);
 
         // THEN it has high priority
@@ -76,7 +76,7 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_LOW)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
                 .thenReturn(true);
 
         // THEN it has high priority
@@ -92,7 +92,7 @@
         final NotificationEntry entry = new NotificationEntryBuilder()
                 .setNotification(notification)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
                 .thenReturn(false);
 
         // THEN it has high priority
@@ -109,7 +109,7 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_LOW)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
                 .thenReturn(false);
 
         // THEN it has high priority
@@ -126,7 +126,7 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_MIN)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
                 .thenReturn(false);
 
         // THEN it does NOT have high priority
@@ -149,7 +149,7 @@
                 .setNotification(notification)
                 .setChannel(channel)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
                 .thenReturn(true);
 
         // THEN it does NOT have high priority
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 13f3a5f..cc3c3cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -42,6 +42,7 @@
 import android.os.Handler;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.telephony.CdmaEriInformation;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
@@ -111,6 +112,8 @@
     private NetworkCapabilities mNetCapabilities;
     private ConnectivityManager.NetworkCallback mNetworkCallback;
 
+    private CdmaEriInformation mEriInformation;
+
     @Rule
     public TestWatcher failWatcher = new TestWatcher() {
         @Override
@@ -155,6 +158,10 @@
         mSignalStrength = mock(SignalStrength.class);
         mServiceState = mock(ServiceState.class);
 
+        mEriInformation = new CdmaEriInformation(CdmaEriInformation.ERI_OFF,
+                CdmaEriInformation.ERI_ICON_MODE_NORMAL);
+        when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation);
+
         mConfig = new Config();
         mConfig.hspaDataDistinguishable = true;
         mCallbackHandler = mock(CallbackHandler.class);
@@ -305,11 +312,9 @@
     }
 
     public void setCdmaRoaming(boolean isRoaming) {
-        when(mServiceState.getCdmaEriIconIndex()).thenReturn(isRoaming ?
-                EriInfo.ROAMING_INDICATOR_ON : EriInfo.ROAMING_INDICATOR_OFF);
-        when(mServiceState.getCdmaEriIconMode()).thenReturn(isRoaming ?
-                EriInfo.ROAMING_ICON_MODE_NORMAL : -1);
-        updateServiceState();
+        mEriInformation.setEriIconIndex(isRoaming ?
+                CdmaEriInformation.ERI_ON : CdmaEriInformation.ERI_OFF);
+        when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation);
     }
 
     public void setVoiceRegState(int voiceRegState) {
diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp
index 61f8143..ab21a76 100644
--- a/packages/services/PacProcessor/jni/Android.bp
+++ b/packages/services/PacProcessor/jni/Android.bp
@@ -39,8 +39,5 @@
     ],
     sanitize: {
         cfi: true,
-        diag: {
-            cfi: true,
-        },
     },
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 02d4f94..1691a96 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -83,6 +83,7 @@
         ":storaged_aidl",
         ":vold_aidl",
         ":platform-compat-config",
+        ":display-device-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/wm/EventLogTags.logtags",
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 32830ae..4b4ce34 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1701,8 +1701,15 @@
         if (mIsFuseEnabled != settingsFuseFlag) {
             Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag);
             SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag));
-            // Perform hard reboot to kick policy into place
-            mContext.getSystemService(PowerManager.class).reboot("fuse_prop");
+
+            PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+            if (powerManager.isRebootingUserspaceSupported()) {
+                // Perform userspace reboot to kick policy into place
+                powerManager.reboot(PowerManager.REBOOT_USERSPACE);
+            } else {
+                // Perform hard reboot to kick policy into place
+                powerManager.reboot("fuse_prop");
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 3f0e2ce..53a967b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2391,6 +2391,8 @@
             return UsageStatsManager.STANDBY_BUCKET_FREQUENT;
         } else if (lower.startsWith("ra")) {
             return UsageStatsManager.STANDBY_BUCKET_RARE;
+        } else if (lower.startsWith("re")) {
+            return UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
         } else if (lower.startsWith("ne")) {
             return UsageStatsManager.STANDBY_BUCKET_NEVER;
         } else {
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 6e135d6..bab133f 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -189,12 +189,14 @@
     }
 
     void onSystemReady() {
-        // Read the sysprop set by lmkd and set this to persist so app could read it.
-        SystemProperties.set("persist.sys.lmk.reportkills",
-                Boolean.toString(SystemProperties.getBoolean("sys.lmk.reportkills", false)));
         registerForUserRemoval();
         registerForPackageRemoval();
-        IoThread.getHandler().post(this::loadExistingProcessExitInfo);
+        IoThread.getHandler().post(() -> {
+            // Read the sysprop set by lmkd and set this to persist so app could read it.
+            SystemProperties.set("persist.sys.lmk.reportkills",
+                    Boolean.toString(SystemProperties.getBoolean("sys.lmk.reportkills", false)));
+            loadExistingProcessExitInfo();
+        });
     }
 
     @GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 63331fa..0429782 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -125,7 +125,7 @@
     static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd";
 
     /**
-     * Flag {@link Context#BIND_INCLUDE_CAPABILITIES} is used
+     * Flag {@link android.content.Context#BIND_INCLUDE_CAPABILITIES} is used
      * to pass while-in-use capabilities from client process to bound service. In targetSdkVersion
      * R and above, if client is a TOP activity, when this flag is present, bound service gets all
      * while-in-use capabilities; when this flag is not present, bound service gets no while-in-use
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index a0c8e23..a0573db 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -118,14 +118,16 @@
         private int mError;
         // Only valid if mError is ERROR_VENDOR
         private int mVendorError;
+        private int mUser;
 
         AuthenticationEvent(long startTime, long latency, boolean authenticated, int error,
-                int vendorError) {
+                int vendorError, int user) {
             mStartTime = startTime;
             mLatency = latency;
             mAuthenticated = authenticated;
             mError = error;
             mVendorError = vendorError;
+            mUser = user;
         }
 
         public String toString(Context context) {
@@ -134,6 +136,7 @@
                     + "\tAuthenticated: " + mAuthenticated
                     + "\tError: " + mError
                     + "\tVendorCode: " + mVendorError
+                    + "\tUser: " + mUser
                     + "\t" + FaceManager.getErrorString(context, mError, mVendorError);
         }
     }
@@ -242,7 +245,8 @@
                     System.currentTimeMillis() - getStartTimeMs() /* latency */,
                     authenticated,
                     0 /* error */,
-                    0 /* vendorError */));
+                    0 /* vendorError */,
+                    getTargetUserId()));
 
             // For face, the authentication lifecycle ends either when
             // 1) Authenticated == true
@@ -260,7 +264,8 @@
                     System.currentTimeMillis() - getStartTimeMs() /* latency */,
                     false /* authenticated */,
                     error,
-                    vendorCode));
+                    vendorCode,
+                    getTargetUserId()));
 
             return super.onError(deviceId, error, vendorCode);
         }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 28f67fe..aa39926 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -109,11 +109,17 @@
         return levels;
     }
 
-    private static float[] getFloatArray(TypedArray array) {
+    /**
+     * Extracts a float array from the specified {@link TypedArray}.
+     *
+     * @param array The array to convert.
+     * @return the given array as a float array.
+     */
+    public static float[] getFloatArray(TypedArray array) {
         final int N = array.length();
         float[] vals = new float[N];
         for (int i = 0; i < N; i++) {
-            vals[i] = array.getFloat(i, -1.0f);
+            vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT);
         }
         array.recycle();
         return vals;
@@ -335,7 +341,7 @@
         }
     }
 
-    protected float normalizeAbsoluteBrightness(int brightness) {
+    protected static float normalizeAbsoluteBrightness(int brightness) {
         brightness = MathUtils.constrain(brightness,
                 PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
         return (float) brightness / PowerManager.BRIGHTNESS_ON;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
new file mode 100644
index 0000000..4796edf
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.os.Environment;
+import android.util.Slog;
+
+import com.android.server.display.config.DisplayConfiguration;
+import com.android.server.display.config.Point;
+import com.android.server.display.config.XmlParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+/**
+ * Reads and stores display-specific configurations.
+ */
+public class DisplayDeviceConfig {
+    private static final String TAG = "DisplayDeviceConfig";
+
+    private static final String ETC_DIR = "etc";
+    private static final String DISPLAY_CONFIG_DIR = "displayconfig";
+    private static final String CONFIG_FILE_FORMAT = "display_%d.xml";
+
+    private float[] mNits;
+    private float[] mBrightness;
+
+    private DisplayDeviceConfig() {
+    }
+
+    /**
+     * Creates an instance for the specified display.
+     *
+     * @param physicalDisplayId The display ID for which to load the configuration.
+     * @return A configuration instance for the specified display.
+     */
+    public static DisplayDeviceConfig create(long physicalDisplayId) {
+        final DisplayDeviceConfig config = new DisplayDeviceConfig();
+        final String filename = String.format(CONFIG_FILE_FORMAT, physicalDisplayId);
+
+        config.initFromFile(Environment.buildPath(
+                Environment.getProductDirectory(), ETC_DIR, DISPLAY_CONFIG_DIR, filename));
+        return config;
+    }
+
+    /**
+     * Return the brightness mapping nits array if one is defined in the configuration file.
+     *
+     * @return The brightness mapping nits array.
+     */
+    public float[] getNits() {
+        return mNits;
+    }
+
+    /**
+     * Return the brightness mapping value array if one is defined in the configuration file.
+     *
+     * @return The brightness mapping value array.
+     */
+    public float[] getBrightness() {
+        return mBrightness;
+    }
+
+    private void initFromFile(File configFile) {
+        if (!configFile.exists()) {
+            // Display configuration files aren't required to exist.
+            return;
+        }
+
+        if (!configFile.isFile()) {
+            Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping");
+            return;
+        }
+
+        try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+            final DisplayConfiguration config = XmlParser.read(in);
+            loadBrightnessMap(config);
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
+                    + configFile, e);
+        }
+    }
+
+    private void loadBrightnessMap(DisplayConfiguration config) {
+        final List<Point> points = config.getScreenBrightnessMap().getPoint();
+        final int size = points.size();
+
+        float[] nits = new float[size];
+        float[] backlight = new float[size];
+
+        int i = 0;
+        for (Point point : points) {
+            nits[i] = point.getNits().floatValue();
+            backlight[i] = point.getValue().floatValue();
+            if (i > 0) {
+                if (nits[i] < nits[i - 1]) {
+                    Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
+                            + " of configuration. Nits: " +  nits[i] + " < " + nits[i - 1]);
+                    return;
+                }
+
+                if (backlight[i] < backlight[i - 1]) {
+                    Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
+                            + " of configuration. Value: " +  backlight[i] + " < "
+                            + backlight[i - 1]);
+                    return;
+                }
+            }
+            ++i;
+        }
+
+        mNits = nits;
+        mBrightness = backlight;
+    }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index fb8a419..1f17f9f 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -30,6 +30,7 @@
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.Spline;
 import android.view.Display;
 import android.view.DisplayAddress;
 import android.view.DisplayCutout;
@@ -37,6 +38,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.lights.Light;
@@ -187,8 +189,10 @@
         private boolean mGameContentTypeRequested;
         private boolean mSidekickActive;
         private SidekickInternal mSidekickInternal;
-
         private SurfaceControl.PhysicalDisplayInfo[] mDisplayInfos;
+        private Spline mSystemBrightnessToNits;
+        private Spline mNitsToHalBrightness;
+        private boolean mHalBrightnessSupport;
 
         LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
@@ -210,6 +214,11 @@
             mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
             mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
             mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
+            mHalBrightnessSupport = SurfaceControl.getDisplayBrightnessSupport(displayToken);
+
+            // Defer configuration file loading
+            BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
+                    LocalDisplayDevice::loadDisplayConfigurationBrightnessMapping, this));
         }
 
         @Override
@@ -338,6 +347,41 @@
             return true;
         }
 
+        private void loadDisplayConfigurationBrightnessMapping() {
+            Spline nitsToHal = null;
+            Spline sysToNits = null;
+
+            // Load the mapping from nits to HAL brightness range (display-device-config.xml)
+            DisplayDeviceConfig config = DisplayDeviceConfig.create(mPhysicalDisplayId);
+            if (config == null) {
+                return;
+            }
+            final float[] halNits = config.getNits();
+            final float[] halBrightness = config.getBrightness();
+            if (halNits == null || halBrightness == null) {
+                return;
+            }
+            nitsToHal = Spline.createSpline(halNits, halBrightness);
+
+            // Load the mapping from system brightness range to nits (config.xml)
+            final Resources res = getOverlayContext().getResources();
+            final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray(
+                            com.android.internal.R.array.config_screenBrightnessNits));
+            final int[] sysBrightness = res.getIntArray(
+                    com.android.internal.R.array.config_screenBrightnessBacklight);
+            if (sysNits.length == 0 || sysBrightness.length != sysNits.length) {
+                return;
+            }
+            final float[] sysBrightnessFloat = new float[sysBrightness.length];
+            for (int i = 0; i < sysBrightness.length; i++) {
+                sysBrightnessFloat[i] = sysBrightness[i];
+            }
+            sysToNits = Spline.createSpline(sysBrightnessFloat, sysNits);
+
+            mNitsToHalBrightness = nitsToHal;
+            mSystemBrightnessToNits = sysToNits;
+        }
+
         private boolean updateColorModesLocked(int[] colorModes,
                 int activeColorMode) {
             List<Integer> pendingColorModes = new ArrayList<>();
@@ -628,13 +672,37 @@
                         Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
                                 + "id=" + physicalDisplayId + ", brightness=" + brightness + ")");
                         try {
-                            mBacklight.setBrightness(brightness);
+                            if (mHalBrightnessSupport) {
+                                mBacklight.setBrightnessFloat(
+                                        displayBrightnessToHalBrightness(brightness));
+                            } else {
+                                mBacklight.setBrightness(brightness);
+                            }
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
                                     "ScreenBrightness", brightness);
                         } finally {
                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
                         }
                     }
+
+                    /**
+                     * Converts brightness range from the framework's brightness space to the
+                     * Hal brightness space if the HAL brightness space has been provided via
+                     * a display device configuration file.
+                     */
+                    private float displayBrightnessToHalBrightness(int brightness) {
+                        if (mSystemBrightnessToNits == null || mNitsToHalBrightness == null) {
+                            return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+                        }
+
+                        if (brightness == 0) {
+                            return PowerManager.BRIGHTNESS_OFF_FLOAT;
+                        }
+
+                        final float nits = mSystemBrightnessToNits.interpolate(brightness);
+                        final float halBrightness = mNitsToHalBrightness.interpolate(nits);
+                        return halBrightness;
+                    }
                 };
             }
             return null;
diff --git a/services/core/java/com/android/server/lights/Light.java b/services/core/java/com/android/server/lights/Light.java
index 717e3da..998c7c6 100644
--- a/services/core/java/com/android/server/lights/Light.java
+++ b/services/core/java/com/android/server/lights/Light.java
@@ -49,6 +49,12 @@
      */
     public abstract void setBrightness(int brightness, int brightnessMode);
 
+    /**
+     * Set the brightness of a display using the brightness range defines in a
+     * display-device-configuration file.
+     */
+    public abstract void setBrightnessFloat(float brightness);
+
     public abstract void setColor(int color);
     public abstract void setFlashing(int color, int mode, int onMS, int offMS);
     public abstract void pulse();
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index eaae2ed..8e6e1d6 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -58,12 +58,23 @@
         }
 
         @Override
+        public void setBrightnessFloat(float brightness) {
+            if (!Float.isNaN(brightness)) {
+                setBrightness(brightness, 0, BRIGHTNESS_MODE_USER);
+            }
+        }
+
+        @Override
         public void setBrightness(int brightness) {
             setBrightness(brightness, BRIGHTNESS_MODE_USER);
         }
 
         @Override
         public void setBrightness(int brightness, int brightnessMode) {
+            setBrightness(Float.NaN, brightness, brightnessMode);
+        }
+
+        private void setBrightness(float brightnessFloat, int brightness, int brightnessMode) {
             synchronized (this) {
                 // LOW_PERSISTENCE cannot be manually set
                 if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
@@ -84,7 +95,10 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Using new setBrightness path!");
                     }
-                    if (brightness == 0) {
+
+                    if (!Float.isNaN(brightnessFloat)) {
+                        SurfaceControl.setDisplayBrightness(mDisplayToken, brightnessFloat);
+                    } else if (brightness == 0) {
                         SurfaceControl.setDisplayBrightness(mDisplayToken, -1.0f);
                     } else {
                         SurfaceControl.setDisplayBrightness(mDisplayToken,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b5b3923..6f43952 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8256,7 +8256,8 @@
                     record.getSystemGeneratedSmartActions(),
                     record.getSmartReplies(),
                     record.canBubble(),
-                    record.isInterruptive()
+                    record.isInterruptive(),
+                    record.isConversation()
             );
             rankings.add(ranking);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f344a84..2bea218 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -54,6 +54,7 @@
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -164,6 +165,7 @@
     private boolean mShowBadge;
     private boolean mAllowBubble;
     private Light mLight;
+    private boolean mIsNotConversationOverride;
     /**
      * This list contains system generated smart actions from NAS, app-generated smart actions are
      * stored in Notification.actions with isContextual() set to true.
@@ -660,6 +662,10 @@
                 if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) {
                     mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE);
                 }
+                if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) {
+                    mIsNotConversationOverride = signals.getBoolean(
+                            Adjustment.KEY_NOT_CONVERSATION);
+                }
                 if (!signals.isEmpty() && adjustment.getIssuer() != null) {
                     mAdjustmentIssuer = adjustment.getIssuer();
                 }
@@ -1310,6 +1316,25 @@
         return hasCustomRemoteView && !hasDecoratedStyle;
     }
 
+    /** Whether this notification is a conversation notification. */
+    public boolean isConversation() {
+        Notification notification = getNotification();
+        if (mChannel.isDemoted()
+                || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
+            return false;
+        }
+        if (notification.getShortcutId() == null
+                && !FeatureFlagUtils.isEnabled(
+                        mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) {
+            return false;
+        }
+        if (mIsNotConversationOverride) {
+            return false;
+        }
+        // STOPSHIP b/137397357: Check shortcut to make a further decision
+        return true;
+    }
+
     @VisibleForTesting
     static final class Light {
         public final int color;
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 8460ede..df8e30f 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -46,6 +46,7 @@
 import android.os.Vibrator;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.StatsLog;
@@ -671,6 +672,8 @@
             }
             mUserActivityPending = false;
         }
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        tm.notifyUserActivity();
         mPolicy.userActivity();
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 03139d2e..36e9775 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2180,6 +2180,47 @@
         }
     }
 
+    /**
+     * Called when the wallpaper needs to zoom out.
+     *
+     * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in.
+     * @param callingPackage package name calling this API.
+     * @param displayId id of the display whose zoom is updating.
+     */
+    public void setWallpaperZoomOut(float zoom, String callingPackage, int displayId) {
+        if (!isWallpaperSupported(callingPackage)) {
+            return;
+        }
+        synchronized (mLock) {
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
+            int userId = UserHandle.getCallingUserId();
+            if (mCurrentUserId != userId) {
+                return; // Don't change the properties now
+            }
+            WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+            if (zoom < 0 || zoom > 1f) {
+                throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom);
+            }
+
+            if (wallpaper.connection != null) {
+                final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+                        .getDisplayConnectorOrCreate(displayId);
+                final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
+                if (engine != null) {
+                    try {
+                        engine.setZoomOut(zoom);
+                    } catch (RemoteException e) {
+                        if (DEBUG) {
+                            Slog.w(TAG, "Couldn't set wallpaper zoom", e);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     @Deprecated
     @Override
     public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d3da500..0b54245 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2004,7 +2004,7 @@
                 final ActivityStack stack = display.getStackAt(stackNdx);
                 stack.switchUser(userId);
                 Task task = stack.getTopMostTask();
-                if (task != null) {
+                if (task != null && task != stack) {
                     stack.positionChildAtTop(task);
                 }
             }
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index 8b2cbbd..b7d6424 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -12,3 +12,11 @@
     api_dir: "platform-compat-schema",
     package_name: "com.android.server.compat.config",
 }
+
+
+xsd_config {
+    name: "display-device-config",
+    srcs: ["display-device-config/display-device-config.xsd"],
+    api_dir: "display-device-config/schema",
+    package_name: "com.android.server.display.config",
+}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
new file mode 100644
index 0000000..c26629d
--- /dev/null
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- This defines the format of the XML file generated by
+  ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from
+  ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java.
+-->
+<xs:schema version="2.0"
+           elementFormDefault="qualified"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:element name="displayConfiguration">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element type="nitsMap" name="screenBrightnessMap"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <!-- Type definitions -->
+
+    <xs:complexType name="nitsMap">
+        <xs:sequence>
+            <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2"/>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="point">
+        <xs:sequence>
+            <xs:element type="nonNegativeDecimal" name="value"/>
+            <xs:element type="nonNegativeDecimal" name="nits"/>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:simpleType name="nonNegativeDecimal">
+        <xs:restriction base="xs:decimal">
+            <xs:minInclusive value="0.0"/>
+        </xs:restriction>
+    </xs:simpleType>
+</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
new file mode 100644
index 0000000..e2b1b9b
--- /dev/null
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -0,0 +1,31 @@
+// Signature format: 2.0
+package com.android.server.display.config {
+
+  public class DisplayConfiguration {
+    ctor public DisplayConfiguration();
+    method public com.android.server.display.config.NitsMap getScreenBrightnessMap();
+    method public void setScreenBrightnessMap(com.android.server.display.config.NitsMap);
+  }
+
+  public class NitsMap {
+    ctor public NitsMap();
+    method public java.util.List<com.android.server.display.config.Point> getPoint();
+  }
+
+  public class Point {
+    ctor public Point();
+    method public java.math.BigDecimal getNits();
+    method public java.math.BigDecimal getValue();
+    method public void setNits(java.math.BigDecimal);
+    method public void setValue(java.math.BigDecimal);
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static com.android.server.display.config.DisplayConfiguration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/services/core/xsd/display-device-config/schema/last_current.txt b/services/core/xsd/display-device-config/schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/display-device-config/schema/last_current.txt
diff --git a/services/core/xsd/display-device-config/schema/last_removed.txt b/services/core/xsd/display-device-config/schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/display-device-config/schema/last_removed.txt
diff --git a/services/core/xsd/display-device-config/schema/removed.txt b/services/core/xsd/display-device-config/schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/core/xsd/display-device-config/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
index 6f2de7f..07b6556 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
@@ -22,6 +22,7 @@
 import com.google.android.icing.proto.DocumentProto;
 import com.google.android.icing.proto.PropertyProto;
 import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.StatusProto;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -117,6 +118,7 @@
     private static List<String> queryGetUris(FakeIcing icing, String term) {
         List<String> uris = new ArrayList<>();
         SearchResultProto results = icing.query(term);
+        assertThat(results.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
         for (SearchResultProto.ResultProto result : results.getResultsList()) {
             uris.add(result.getDocument().getUri());
         }
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 4a13dce..7af3ec6 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,18 +16,18 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import android.os.FileUtils;
 import android.test.AndroidTestCase;
 
@@ -37,10 +37,12 @@
 
     File mStorageDir;
 
-    final static String PACKAGE_1 = "com.android.testpackage1";
-    final static String PACKAGE_2 = "com.android.testpackage2";
+    private static final String PACKAGE_1 = "com.android.testpackage1";
+    private static final String PACKAGE_2 = "com.android.testpackage2";
+    private static final String PACKAGE_3 = "com.android.testpackage3";
+    private static final String PACKAGE_4 = "com.android.testpackage4";
 
-    final static int USER_ID = 0;
+    private static final int USER_ID = 0;
 
     @Override
     protected void setUp() throws Exception {
@@ -100,16 +102,27 @@
 
         aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
                 REASON_MAIN_USAGE);
+        aih.setAppStandbyBucket(PACKAGE_3, USER_ID, 2500, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+        aih.setAppStandbyBucket(PACKAGE_4, USER_ID, 2750, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_USER);
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
                 REASON_MAIN_TIMEOUT);
 
         assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
         assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
+        assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
+        assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
+                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+        assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
+                REASON_MAIN_FORCED_BY_USER);
 
-        // RARE is considered idle
+        // RARE and RESTRICTED are considered idle
         assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
         assertFalse(aih.isIdle(PACKAGE_2, USER_ID, 3000));
+        assertTrue(aih.isIdle(PACKAGE_3, USER_ID, 3000));
+        assertTrue(aih.isIdle(PACKAGE_4, USER_ID, 3000));
 
         // Check persistence
         aih.writeAppIdleDurations();
@@ -118,6 +131,11 @@
         assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
         assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
+        assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
+        assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
+                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+        assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
+                REASON_MAIN_FORCED_BY_USER);
 
         assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
         assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 6aca58f..03dc213 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -28,11 +28,17 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
 import static org.junit.Assert.assertEquals;
@@ -46,6 +52,8 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
+import android.annotation.NonNull;
+import android.app.ActivityManager;
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.UsageEvents;
 import android.appwidget.AppWidgetManager;
@@ -124,6 +132,13 @@
         public PackageManager getPackageManager() {
             return mockPm;
         }
+
+        public Object getSystemService(@NonNull String name) {
+            if (Context.ACTIVITY_SERVICE.equals(name)) {
+                return mock(ActivityManager.class);
+            }
+            return super.getSystemService(name);
+        }
     }
 
     static class MyInjector extends AppStandbyController.Injector {
@@ -253,8 +268,11 @@
 
         doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
         try {
+            doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt());
             doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt());
             doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
+                    anyInt());
+            doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
                     anyInt(), anyInt());
             doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName),
                     anyInt());
@@ -468,7 +486,7 @@
     }
 
     @Test
-    public void testPredictionTimedout() throws Exception {
+    public void testPredictionTimedOut() throws Exception {
         // Set it to timeout or usage, so that prediction can override it
         mInjector.mElapsedRealtime = HOUR_MS;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
@@ -532,6 +550,79 @@
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
                 REASON_MAIN_PREDICTED);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+
+        // Prediction can't remove from RESTRICTED
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_USER);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_PREDICTED);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_PREDICTED);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+        // Force from user can remove from RESTRICTED
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_USER);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+        // Force from system can remove from RESTRICTED if it was put it in due to system
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_USER);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_PREDICTED);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+        // Non-user usage can't remove from RESTRICTED
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_USAGE);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_USAGE | REASON_SUB_USAGE_SYSTEM_INTERACTION);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_USAGE | REASON_SUB_USAGE_SYNC_ADAPTER);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_USAGE | REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+        // Explicit user usage can remove from RESTRICTED
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_USER);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
     }
 
     @Test
@@ -556,6 +647,55 @@
         assertBucket(STANDBY_BUCKET_RARE);
     }
 
+    /**
+     * Test that setAppStandbyBucket to RESTRICTED doesn't change the bucket until the usage
+     * timeout has passed.
+     */
+    @Test
+    public void testTimeoutBeforeRestricted() throws Exception {
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        // Bucket shouldn't change
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // bucketing works after timeout
+        mInjector.mElapsedRealtime += DAY_MS;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+        // Way past all timeouts. Make sure timeout processing doesn't raise bucket.
+        mInjector.mElapsedRealtime += RARE_THRESHOLD * 4;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+    }
+
+    /**
+     * Test that an app is put into the RESTRICTED bucket after enough time has passed.
+     */
+    @Test
+    public void testRestrictedDelay() throws Exception {
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        mInjector.mElapsedRealtime += mInjector.getRestrictedBucketDelayMs() - 5000;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        // Bucket shouldn't change
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // bucketing works after timeout
+        mInjector.mElapsedRealtime += 6000;
+
+        Thread.sleep(6000);
+        // Enough time has passed. The app should automatically be put into the RESTRICTED bucket.
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+    }
+
     @Test
     public void testCascadingTimeouts() throws Exception {
         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index a9fe1a6..da0e03d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -117,6 +117,7 @@
             assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
             assertEquals(canBubble(i), ranking.canBubble());
             assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive());
+            assertEquals(isConversation(i), ranking.isConversation());
         }
     }
 
@@ -184,7 +185,8 @@
                 (ArrayList) tweak.getSmartActions(),
                 (ArrayList) tweak.getSmartReplies(),
                 tweak.canBubble(),
-                tweak.visuallyInterruptive()
+                tweak.visuallyInterruptive(),
+                tweak.isConversation()
         );
         assertNotEquals(nru, nru2);
     }
@@ -261,7 +263,8 @@
                     getSmartActions(key, i),
                     getSmartReplies(key, i),
                     canBubble(i),
-                    visuallyInterruptive(i)
+                    visuallyInterruptive(i),
+                    isConversation(i)
             );
             rankings[i] = ranking;
         }
@@ -370,6 +373,10 @@
         return index % 4 == 0;
     }
 
+    private boolean isConversation(int index) {
+        return index % 4 == 0;
+    }
+
     private void assertActionsEqual(
             List<Notification.Action> expecteds, List<Notification.Action> actuals) {
         assertEquals(expecteds.size(), actuals.size());
@@ -403,6 +410,7 @@
         assertEquals(comment, a.isNoisy(), b.isNoisy());
         assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
         assertEquals(comment, a.canBubble(), b.canBubble());
+        assertEquals(comment, a.isConversation(), b.isConversation());
         assertActionsEqual(a.getSmartActions(), b.getSmartActions());
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index fab6b7f..2d4b5a7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -20,6 +20,7 @@
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_NOT_CONVERSATION;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
@@ -38,12 +39,15 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.app.Person;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -58,6 +62,7 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
+import android.util.FeatureFlagUtils;
 import android.widget.RemoteViews;
 
 import androidx.test.filters.SmallTest;
@@ -83,6 +88,7 @@
 
     private final Context mMockContext = mock(Context.class);
     @Mock private PackageManager mPm;
+    @Mock private ContentResolver mContentResolver;
 
     private final String pkg = PKG_N_MR1;
     private final int uid = 9583;
@@ -116,6 +122,9 @@
 
         when(mMockContext.getResources()).thenReturn(getContext().getResources());
         when(mMockContext.getPackageManager()).thenReturn(mPm);
+        when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
+        Settings.Global.putString(mContentResolver,
+                FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.targetSdkVersion = Build.VERSION_CODES.O;
         when(mMockContext.getApplicationInfo()).thenReturn(appInfo);
@@ -194,6 +203,21 @@
         return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
     }
 
+    private StatusBarNotification getMessagingStyleNotification(@Nullable String shortcutId) {
+        final Builder builder = new Builder(mMockContext)
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        Person person = new Person.Builder().setName("Bob").build();
+        builder.setStyle(new Notification.MessagingStyle(person));
+        if (shortcutId != null) {
+            builder.setShortcutId(shortcutId);
+        }
+
+        Notification n = builder.build();
+        return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
+    }
+
     //
     // Tests
     //
@@ -1095,4 +1119,55 @@
 
         assertTrue("false negative detection", record.hasUndecoratedRemoteView());
     }
+
+    @Test
+    public void testIsConversation() {
+        StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue(record.isConversation());
+    }
+
+    @Test
+    public void testIsConversation_nullShortcutId() {
+        StatusBarNotification sbn = getMessagingStyleNotification(null);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse(record.isConversation());
+    }
+
+    @Test
+    public void testIsConversation_bypassShortcutFlagEnabled() {
+        Settings.Global.putString(mContentResolver,
+                FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+        StatusBarNotification sbn = getMessagingStyleNotification(null);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue(record.isConversation());
+    }
+
+    @Test
+    public void testIsConversation_channelDemoted() {
+        StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+        channel.setDemoted(true);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse(record.isConversation());
+    }
+
+    @Test
+    public void testIsConversation_withAdjustmentOverride() {
+        StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(KEY_NOT_CONVERSATION, true);
+        Adjustment adjustment = new Adjustment(
+                PKG_O, record.getKey(), bundle, "", record.getUser().getIdentifier());
+
+        record.addAdjustment(adjustment);
+        record.applyAdjustments();
+
+        assertFalse(record.isConversation());
+    }
 }
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index d2c973a..a9474c1 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -19,5 +19,6 @@
         "android.hardware.usb-V1.1-java",
         "android.hardware.usb-V1.2-java",
         "android.hardware.usb.gadget-V1.0-java",
+        "android.hardware.usb.gadget-V1.1-java",
     ],
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 9f3b07b..6407ec7 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -161,6 +161,7 @@
     private static final int MSG_GET_CURRENT_USB_FUNCTIONS = 16;
     private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
     private static final int MSG_GADGET_HAL_REGISTERED = 18;
+    private static final int MSG_RESET_USB_GADGET = 19;
 
     private static final int AUDIO_MODE_SOURCE = 1;
 
@@ -1846,6 +1847,23 @@
                         }
                     }
                     break;
+                case MSG_RESET_USB_GADGET:
+                    synchronized (mGadgetProxyLock) {
+                        if (mGadgetProxy == null) {
+                            Slog.e(TAG, "reset Usb Gadget mGadgetProxy is null");
+                            break;
+                        }
+
+                        try {
+                            android.hardware.usb.gadget.V1_1.IUsbGadget gadgetProxy =
+                                    android.hardware.usb.gadget.V1_1.IUsbGadget
+                                            .castFrom(mGadgetProxy);
+                            gadgetProxy.reset();
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "reset Usb Gadget failed", e);
+                        }
+                    }
+                    break;
                 default:
                     super.handleMessage(msg);
             }
@@ -2054,6 +2072,17 @@
         mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions);
     }
 
+    /**
+     * Resets the USB Gadget.
+     */
+    public void resetUsbGadget() {
+        if (DEBUG) {
+            Slog.d(TAG, "reset Usb Gadget");
+        }
+
+        mHandler.sendMessage(MSG_RESET_USB_GADGET, null);
+    }
+
     private void onAdbEnabled(boolean enabled) {
         mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
     }
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 0a03f14..61f2c50 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -638,6 +638,19 @@
     }
 
     @Override
+    public void resetUsbGadget() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        Preconditions.checkNotNull(mDeviceManager, "DeviceManager must not be null");
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mDeviceManager.resetUsbGadget();
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
     public List<ParcelableUsbPort> getPorts() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 826a89e..acf51f3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -547,8 +547,14 @@
          */
         public static final int PROPERTY_VOIP_AUDIO_MODE = 0x00001000;
 
+        /**
+         * Indicates that the call is an adhoc conference call. This property can be set for both
+         * incoming and outgoing calls.
+         */
+        public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00002000
+        // Next PROPERTY value: 0x00004000
         //******************************************************************************************
 
         private final String mTelecomCallId;
@@ -726,6 +732,9 @@
             if (hasProperty(properties, PROPERTY_VOIP_AUDIO_MODE)) {
                 builder.append(" PROPERTY_VOIP_AUDIO_MODE");
             }
+            if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) {
+                builder.append(" PROPERTY_IS_ADHOC_CONFERENCE");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 456290c..6b0845f 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -69,6 +69,7 @@
         public void onConnectionEvent(Conference c, String event, Bundle extras) {}
         public void onCallerDisplayNameChanged(
                 Conference c, String callerDisplayName, int presentation) {}
+        public void onRingbackRequested(Conference c, boolean ringback) {}
     }
 
     private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -97,6 +98,7 @@
     private int mAddressPresentation;
     private String mCallerDisplayName;
     private int mCallerDisplayNamePresentation;
+    private boolean mRingbackRequested = false;
 
     private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
         @Override
@@ -170,6 +172,14 @@
     }
 
     /**
+     * Returns whether this conference is requesting that the system play a ringback tone
+     * on its behalf.
+     */
+    public final boolean isRingbackRequested() {
+        return mRingbackRequested;
+    }
+
+    /**
      * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
      * {@link Connection} for valid values.
      *
@@ -308,6 +318,35 @@
     public void onConnectionAdded(Connection connection) {}
 
     /**
+     * Notifies this Conference, which is in {@code STATE_RINGING}, of
+     * a request to accept.
+     * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+     * the default dialer's {@link InCallService}.
+     *
+     * @param videoState The video state in which to answer the connection.
+     */
+    public void onAnswer(int videoState) {}
+
+    /**
+     * Notifies this Conference, which is in {@code STATE_RINGING}, of
+     * a request to accept.
+     * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+     * the default dialer's {@link InCallService}.
+     * @hide
+     */
+    public final void onAnswer() {
+         onAnswer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    /**
+     * Notifies this Conference, which is in {@code STATE_RINGING}, of
+     * a request to reject.
+     * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
+     * the default dialer's {@link InCallService}.
+     */
+    public void onReject() {}
+
+    /**
      * Sets state to be on hold.
      */
     public final void setOnHold() {
@@ -322,9 +361,17 @@
     }
 
     /**
+     * Sets state to be ringing.
+     */
+    public final void setRinging() {
+        setState(Connection.STATE_RINGING);
+    }
+
+    /**
      * Sets state to be active.
      */
     public final void setActive() {
+        setRingbackRequested(false);
         setState(Connection.STATE_ACTIVE);
     }
 
@@ -436,6 +483,21 @@
     }
 
     /**
+     * Requests that the framework play a ringback tone. This is to be invoked by implementations
+     * that do not play a ringback tone themselves in the conference's audio stream.
+     *
+     * @param ringback Whether the ringback tone is to be played.
+     */
+    public final void setRingbackRequested(boolean ringback) {
+        if (mRingbackRequested != ringback) {
+            mRingbackRequested = ringback;
+            for (Listener l : mListeners) {
+                l.onRingbackRequested(this, ringback);
+            }
+        }
+    }
+
+    /**
      * Set the video state for the conference.
      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
      * {@link VideoProfile#STATE_BIDIRECTIONAL},
@@ -640,14 +702,6 @@
     }
 
     private void setState(int newState) {
-        if (newState != Connection.STATE_ACTIVE &&
-                newState != Connection.STATE_HOLDING &&
-                newState != Connection.STATE_DISCONNECTED) {
-            Log.w(this, "Unsupported state transition for Conference call.",
-                    Connection.stateToString(newState));
-            return;
-        }
-
         if (mState != newState) {
             int oldState = mState;
             mState = newState;
@@ -657,6 +711,37 @@
         }
     }
 
+    private static class FailureSignalingConference extends Conference {
+        private boolean mImmutable = false;
+        public FailureSignalingConference(DisconnectCause disconnectCause,
+                PhoneAccountHandle phoneAccount) {
+            super(phoneAccount);
+            setDisconnected(disconnectCause);
+            mImmutable = true;
+        }
+        public void checkImmutable() {
+            if (mImmutable) {
+                throw new UnsupportedOperationException("Conference is immutable");
+            }
+        }
+    }
+
+    /**
+     * Return a {@code Conference} which represents a failed conference attempt. The returned
+     * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified,
+     * and a {@link #getState()} of {@code STATE_DISCONNECTED}.
+     * <p>
+     * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate,
+     * so users of this method need not maintain a reference to its return value to destroy it.
+     *
+     * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
+     * @return A {@code Conference} which indicates failure.
+     */
+    public @NonNull static Conference createFailedConference(
+            @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
+        return new FailureSignalingConference(disconnectCause, phoneAccount);
+    }
+
     private final void clearConferenceableList() {
         for (Connection c : mConferenceableConnections) {
             c.removeConnectionListener(mConnectionDeathListener);
@@ -667,11 +752,13 @@
     @Override
     public String toString() {
         return String.format(Locale.US,
-                "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
+                "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s,"
+                + "isRingbackRequested: %s, ThisObject %s]",
                 Connection.stateToString(mState),
                 Call.Details.capabilitiesToString(mConnectionCapabilities),
                 getVideoState(),
                 getVideoProvider(),
+                isRingbackRequested() ? "Y" : "N",
                 super.toString());
     }
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index f205ec6..c934625 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -497,8 +497,17 @@
     @TestApi
     public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11;
 
+    /**
+     * Set by the framework to indicate that it is an adhoc conference call.
+     * <p>
+     * This is used for Outgoing and incoming conference calls.
+     *
+     */
+    public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12;
+
+
     //**********************************************************************************************
-    // Next PROPERTY value: 1<<12
+    // Next PROPERTY value: 1<<13
     //**********************************************************************************************
 
     /**
@@ -1018,6 +1027,10 @@
             builder.append(isLong ? " PROPERTY_REMOTELY_HOSTED" : " remote_hst");
         }
 
+        if ((properties & PROPERTY_IS_ADHOC_CONFERENCE) == PROPERTY_IS_ADHOC_CONFERENCE) {
+            builder.append(isLong ? " PROPERTY_IS_ADHOC_CONFERENCE" : " adhoc_conf");
+        }
+
         builder.append("]");
         return builder.toString();
     }
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 221f8f1..6d7ceca 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -26,6 +26,9 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Simple data container encapsulating a request to some entity to
  * create a new {@link Connection}.
@@ -46,6 +49,8 @@
         private boolean mShouldShowIncomingCallUi = false;
         private ParcelFileDescriptor mRttPipeToInCall;
         private ParcelFileDescriptor mRttPipeFromInCall;
+        private List<Uri> mParticipants;
+        private boolean mIsAdhocConference = false;
 
         public Builder() { }
 
@@ -59,6 +64,15 @@
         }
 
         /**
+         * Sets the participants for the resulting {@link ConnectionRequest}
+         * @param participants The participants to which the {@link Connection} is to connect.
+         */
+        public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+            this.mParticipants = participants;
+            return this;
+        }
+
+        /**
          * Sets the address for the resulting {@link ConnectionRequest}
          * @param address The address(e.g., phone number) to which the {@link Connection} is to
          *                connect.
@@ -108,6 +122,16 @@
         }
 
         /**
+         * Sets isAdhocConference for the resulting {@link ConnectionRequest}
+         * @param isAdhocConference {@code true} if it is a adhoc conference call
+         *                          {@code false}, if not a adhoc conference call
+         */
+        public @NonNull Builder setIsAdhocConferenceCall(boolean isAdhocConference) {
+            this.mIsAdhocConference = isAdhocConference;
+            return this;
+        }
+
+        /**
          * Sets the RTT pipe for transferring text into the {@link ConnectionService} for the
          * resulting {@link ConnectionRequest}
          * @param rttPipeFromInCall The data pipe to read from.
@@ -141,7 +165,9 @@
                     mTelecomCallId,
                     mShouldShowIncomingCallUi,
                     mRttPipeFromInCall,
-                    mRttPipeToInCall);
+                    mRttPipeToInCall,
+                    mParticipants,
+                    mIsAdhocConference);
         }
     }
 
@@ -155,6 +181,8 @@
     private final ParcelFileDescriptor mRttPipeFromInCall;
     // Cached return value of getRttTextStream -- we don't want to wrap it more than once.
     private Connection.RttTextStream mRttTextStream;
+    private List<Uri> mParticipants;
+    private final boolean mIsAdhocConference;
 
     /**
      * @param accountHandle The accountHandle which should be used to place the call.
@@ -214,6 +242,21 @@
             boolean shouldShowIncomingCallUi,
             ParcelFileDescriptor rttPipeFromInCall,
             ParcelFileDescriptor rttPipeToInCall) {
+        this(accountHandle, handle, extras, videoState, telecomCallId,
+                shouldShowIncomingCallUi, rttPipeFromInCall, rttPipeToInCall, null, false);
+    }
+
+    private ConnectionRequest(
+            PhoneAccountHandle accountHandle,
+            Uri handle,
+            Bundle extras,
+            int videoState,
+            String telecomCallId,
+            boolean shouldShowIncomingCallUi,
+            ParcelFileDescriptor rttPipeFromInCall,
+            ParcelFileDescriptor rttPipeToInCall,
+            List<Uri> participants,
+            boolean isAdhocConference) {
         mAccountHandle = accountHandle;
         mAddress = handle;
         mExtras = extras;
@@ -222,6 +265,8 @@
         mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
         mRttPipeFromInCall = rttPipeFromInCall;
         mRttPipeToInCall = rttPipeToInCall;
+        mParticipants = participants;
+        mIsAdhocConference = isAdhocConference;
     }
 
     private ConnectionRequest(Parcel in) {
@@ -233,6 +278,11 @@
         mShouldShowIncomingCallUi = in.readInt() == 1;
         mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader());
         mRttPipeToInCall = in.readParcelable(getClass().getClassLoader());
+
+        mParticipants = new ArrayList<Uri>();
+        in.readList(mParticipants, getClass().getClassLoader());
+
+        mIsAdhocConference = in.readInt() == 1;
     }
 
     /**
@@ -246,6 +296,11 @@
     public Uri getAddress() { return mAddress; }
 
     /**
+     * The participants to which the {@link Connection} is to connect.
+     */
+    public @Nullable List<Uri> getParticipants() { return mParticipants; }
+
+    /**
      * Application-specific extra data. Used for passing back information from an incoming
      * call {@code Intent}, and for any proprietary extensions arranged between a client
      * and servant {@code ConnectionService} which agree on a vocabulary for such data.
@@ -290,6 +345,13 @@
     }
 
     /**
+     * @return {@code true} if the call is a adhoc conference call else @return {@code false}
+     */
+    public boolean isAdhocConferenceCall() {
+        return mIsAdhocConference;
+    }
+
+    /**
      * Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the connection
      * service to the in-call UI. In order to obtain an
      * {@link java.io.InputStream} from this {@link ParcelFileDescriptor}, use
@@ -345,11 +407,12 @@
 
     @Override
     public String toString() {
-        return String.format("ConnectionRequest %s %s",
+        return String.format("ConnectionRequest %s %s isAdhocConf: %s",
                 mAddress == null
                         ? Uri.EMPTY
                         : Connection.toLogSafePhoneNumber(mAddress.toString()),
-                bundleToString(mExtras));
+                bundleToString(mExtras),
+                isAdhocConferenceCall() ? "Y" : "N");
     }
 
     private static String bundleToString(Bundle extras){
@@ -406,5 +469,7 @@
         destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0);
         destination.writeParcelable(mRttPipeFromInCall, 0);
         destination.writeParcelable(mRttPipeToInCall, 0);
+        destination.writeList(mParticipants);
+        destination.writeInt(mIsAdhocConference ? 1 : 0);
     }
 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 3a0494e..440f044 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -154,6 +154,9 @@
     private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
     private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
     private static final String SESSION_HANDOVER_FAILED = "CS.haF";
+    private static final String SESSION_CREATE_CONF = "CS.crConf";
+    private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC";
+    private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF";
 
     private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
     private static final int MSG_CREATE_CONNECTION = 2;
@@ -188,6 +191,9 @@
     private static final int MSG_HANDOVER_FAILED = 32;
     private static final int MSG_HANDOVER_COMPLETE = 33;
     private static final int MSG_DEFLECT = 34;
+    private static final int MSG_CREATE_CONFERENCE = 35;
+    private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
+    private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
 
     private static Connection sNullConnection;
 
@@ -291,6 +297,63 @@
         }
 
         @Override
+        public void createConference(
+                PhoneAccountHandle connectionManagerPhoneAccount,
+                String id,
+                ConnectionRequest request,
+                boolean isIncoming,
+                boolean isUnknown,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CREATE_CONF);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = connectionManagerPhoneAccount;
+                args.arg2 = id;
+                args.arg3 = request;
+                args.arg4 = Log.createSubsession();
+                args.argi1 = isIncoming ? 1 : 0;
+                args.argi2 = isUnknown ? 1 : 0;
+                mHandler.obtainMessage(MSG_CREATE_CONFERENCE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void createConferenceComplete(String id, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CREATE_CONF_COMPLETE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = id;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void createConferenceFailed(
+                PhoneAccountHandle connectionManagerPhoneAccount,
+                String callId,
+                ConnectionRequest request,
+                boolean isIncoming,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CREATE_CONF_FAILED);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = request;
+                args.arg3 = Log.createSubsession();
+                args.arg4 = connectionManagerPhoneAccount;
+                args.argi1 = isIncoming ? 1 : 0;
+                mHandler.obtainMessage(MSG_CREATE_CONFERENCE_FAILED, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void handoverFailed(String callId, ConnectionRequest request, int reason,
                                    Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED);
@@ -802,6 +865,106 @@
                     }
                     break;
                 }
+                case MSG_CREATE_CONFERENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
+                    try {
+                        final PhoneAccountHandle connectionManagerPhoneAccount =
+                                (PhoneAccountHandle) args.arg1;
+                        final String id = (String) args.arg2;
+                        final ConnectionRequest request = (ConnectionRequest) args.arg3;
+                        final boolean isIncoming = args.argi1 == 1;
+                        final boolean isUnknown = args.argi2 == 1;
+                        if (!mAreAccountsInitialized) {
+                            Log.d(this, "Enqueueing pre-initconference request %s", id);
+                            mPreInitializationConnectionRequests.add(
+                                    new android.telecom.Logging.Runnable(
+                                            SESSION_HANDLER + SESSION_CREATE_CONF + ".pIConfR",
+                                            null /*lock*/) {
+                                @Override
+                                public void loggedRun() {
+                                    createConference(connectionManagerPhoneAccount,
+                                            id,
+                                            request,
+                                            isIncoming,
+                                            isUnknown);
+                                }
+                            }.prepare());
+                        } else {
+                            createConference(connectionManagerPhoneAccount,
+                                    id,
+                                    request,
+                                    isIncoming,
+                                    isUnknown);
+                        }
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+                case MSG_CREATE_CONFERENCE_COMPLETE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2,
+                            SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE);
+                    try {
+                        final String id = (String) args.arg1;
+                        if (!mAreAccountsInitialized) {
+                            Log.d(this, "Enqueueing pre-init conference request %s", id);
+                            mPreInitializationConnectionRequests.add(
+                                    new android.telecom.Logging.Runnable(
+                                            SESSION_HANDLER + SESSION_CREATE_CONF_COMPLETE
+                                                    + ".pIConfR",
+                                            null /*lock*/) {
+                                        @Override
+                                        public void loggedRun() {
+                                            notifyCreateConferenceComplete(id);
+                                        }
+                                    }.prepare());
+                        } else {
+                            notifyCreateConferenceComplete(id);
+                        }
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+                case MSG_CREATE_CONFERENCE_FAILED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg3, SESSION_HANDLER +
+                            SESSION_CREATE_CONN_FAILED);
+                    try {
+                        final String id = (String) args.arg1;
+                        final ConnectionRequest request = (ConnectionRequest) args.arg2;
+                        final boolean isIncoming = args.argi1 == 1;
+                        final PhoneAccountHandle connectionMgrPhoneAccount =
+                                (PhoneAccountHandle) args.arg4;
+                        if (!mAreAccountsInitialized) {
+                            Log.d(this, "Enqueueing pre-init conference request %s", id);
+                            mPreInitializationConnectionRequests.add(
+                                    new android.telecom.Logging.Runnable(
+                                            SESSION_HANDLER + SESSION_CREATE_CONF_FAILED
+                                                    + ".pIConfR",
+                                            null /*lock*/) {
+                                        @Override
+                                        public void loggedRun() {
+                                            createConferenceFailed(connectionMgrPhoneAccount, id,
+                                                    request, isIncoming);
+                                        }
+                                    }.prepare());
+                        } else {
+                            Log.i(this, "createConferenceFailed %s", id);
+                            createConferenceFailed(connectionMgrPhoneAccount, id, request,
+                                    isIncoming);
+                        }
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+
                 case MSG_HANDOVER_FAILED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     Log.continueSession((Session) args.arg3, SESSION_HANDLER +
@@ -1162,6 +1325,12 @@
         public void onStateChanged(Conference conference, int oldState, int newState) {
             String id = mIdByConference.get(conference);
             switch (newState) {
+                case Connection.STATE_RINGING:
+                    mAdapter.setRinging(id);
+                    break;
+                case Connection.STATE_DIALING:
+                    mAdapter.setDialing(id);
+                    break;
                 case Connection.STATE_ACTIVE:
                     mAdapter.setActive(id);
                     break;
@@ -1292,6 +1461,13 @@
                 mAdapter.onConnectionEvent(id, event, extras);
             }
         }
+
+        @Override
+        public void onRingbackRequested(Conference c, boolean ringback) {
+            String id = mIdByConference.get(c);
+            Log.d(this, "Adapter conference onRingback %b", ringback);
+            mAdapter.setRingbackRequested(id, ringback);
+        }
     };
 
     private final Connection.Listener mConnectionListener = new Connection.Listener() {
@@ -1534,6 +1710,70 @@
         return super.onUnbind(intent);
     }
 
+
+    /**
+     * This can be used by telecom to either create a new outgoing conference call or attach
+     * to an existing incoming conference call. In either case, telecom will cycle through a
+     * set of services and call createConference until a connection service cancels the process
+     * or completes it successfully.
+     */
+    private void createConference(
+            final PhoneAccountHandle callManagerAccount,
+            final String callId,
+            final ConnectionRequest request,
+            boolean isIncoming,
+            boolean isUnknown) {
+
+        Conference conference = null;
+        conference = isIncoming ? onCreateIncomingConference(callManagerAccount, request)
+                    : onCreateOutgoingConference(callManagerAccount, request);
+
+        Log.d(this, "createConference, conference: %s", conference);
+        if (conference == null) {
+            Log.i(this, "createConference, implementation returned null conference.");
+            conference = Conference.createFailedConference(
+                    new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"),
+                    request.getAccountHandle());
+        }
+        if (conference.getExtras() != null) {
+            conference.getExtras().putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+        }
+        mConferenceById.put(callId, conference);
+        mIdByConference.put(conference, callId);
+        conference.addListener(mConferenceListener);
+        ParcelableConference parcelableConference = new ParcelableConference(
+                request.getAccountHandle(),
+                conference.getState(),
+                conference.getConnectionCapabilities(),
+                conference.getConnectionProperties(),
+                Collections.<String>emptyList(), //connectionIds
+                conference.getVideoProvider() == null ?
+                        null : conference.getVideoProvider().getInterface(),
+                conference.getVideoState(),
+                conference.getConnectTimeMillis(),
+                conference.getConnectionStartElapsedRealTime(),
+                conference.getStatusHints(),
+                conference.getExtras(),
+                conference.getAddress(),
+                conference.getAddressPresentation(),
+                conference.getCallerDisplayName(),
+                conference.getCallerDisplayNamePresentation(),
+                conference.getDisconnectCause(),
+                conference.isRingbackRequested());
+        if (conference.getState() != Connection.STATE_DISCONNECTED) {
+            conference.setTelecomCallId(callId);
+            mAdapter.setVideoProvider(callId, conference.getVideoProvider());
+            mAdapter.setVideoState(callId, conference.getVideoState());
+            onConferenceAdded(conference);
+        }
+
+        Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId);
+        mAdapter.handleCreateConferenceComplete(
+                callId,
+                request,
+                parcelableConference);
+    }
+
     /**
      * This can be used by telecom to either create a new outgoing call or attach to an existing
      * incoming call. In either case, telecom will cycle through a set of services and call
@@ -1645,6 +1885,18 @@
         }
     }
 
+    private void createConferenceFailed(final PhoneAccountHandle callManagerAccount,
+                                        final String callId, final ConnectionRequest request,
+                                        boolean isIncoming) {
+
+        Log.i(this, "createConferenceFailed %s", callId);
+        if (isIncoming) {
+            onCreateIncomingConferenceFailed(callManagerAccount, request);
+        } else {
+            onCreateOutgoingConferenceFailed(callManagerAccount, request);
+        }
+    }
+
     private void handoverFailed(final String callId, final ConnectionRequest request,
                                         int reason) {
 
@@ -1669,6 +1921,24 @@
                 "notifyCreateConnectionComplete"));
     }
 
+    /**
+     * Called by Telecom when the creation of a new Conference has completed and it is now added
+     * to Telecom.
+     * @param callId The ID of the connection.
+     */
+    private void notifyCreateConferenceComplete(final String callId) {
+        Log.i(this, "notifyCreateConferenceComplete %s", callId);
+        if (callId == null) {
+            // This could happen if the conference fails quickly and is removed from the
+            // ConnectionService before Telecom sends the create conference complete callback.
+            Log.w(this, "notifyCreateConferenceComplete: callId is null.");
+            return;
+        }
+        onCreateConferenceComplete(findConferenceForAction(callId,
+                "notifyCreateConferenceComplete"));
+    }
+
+
     private void abort(String callId) {
         Log.d(this, "abort %s", callId);
         findConnectionForAction(callId, "abort").onAbort();
@@ -1676,12 +1946,20 @@
 
     private void answerVideo(String callId, int videoState) {
         Log.d(this, "answerVideo %s", callId);
-        findConnectionForAction(callId, "answer").onAnswer(videoState);
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "answer").onAnswer(videoState);
+        } else {
+            findConferenceForAction(callId, "answer").onAnswer(videoState);
+        }
     }
 
     private void answer(String callId) {
         Log.d(this, "answer %s", callId);
-        findConnectionForAction(callId, "answer").onAnswer();
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "answer").onAnswer();
+        } else {
+            findConferenceForAction(callId, "answer").onAnswer();
+        }
     }
 
     private void deflect(String callId, Uri address) {
@@ -1691,7 +1969,11 @@
 
     private void reject(String callId) {
         Log.d(this, "reject %s", callId);
-        findConnectionForAction(callId, "reject").onReject();
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "reject").onReject();
+        } else {
+            findConferenceForAction(callId, "reject").onReject();
+        }
     }
 
     private void reject(String callId, String rejectWithMessage) {
@@ -2198,6 +2480,21 @@
             ConnectionRequest request) {
         return null;
     }
+    /**
+     * Create a {@code Connection} given an incoming request. This is used to attach to existing
+     * incoming conference call.
+     *
+     * @param connectionManagerPhoneAccount See description at
+     *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+     * @param request Details about the incoming call.
+     * @return The {@code Connection} object to satisfy this call, or {@code null} to
+     *         not handle the call.
+     */
+    public @Nullable Conference onCreateIncomingConference(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @Nullable ConnectionRequest request) {
+        return null;
+    }
 
     /**
      * Called after the {@link Connection} returned by
@@ -2212,6 +2509,19 @@
     }
 
     /**
+     * Called after the {@link Conference} returned by
+     * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)}
+     * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been
+     * added to the {@link ConnectionService} and sent to Telecom.
+     *
+     * @param conference the {@link Conference}.
+     * @hide
+     */
+    public void onCreateConferenceComplete(Conference conference) {
+    }
+
+
+    /**
      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
      * incoming {@link Connection} was denied.
      * <p>
@@ -2250,6 +2560,47 @@
     }
 
     /**
+     * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+     * incoming {@link Conference} was denied.
+     * <p>
+     * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
+     * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time.
+     * The {@link ConnectionService} is responsible for silently rejecting the new incoming
+     * {@link Conference}.
+     * <p>
+     * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
+     *
+     * @param connectionManagerPhoneAccount See description at
+     *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+     * @param request The incoming connection request.
+     */
+    public void onCreateIncomingConferenceFailed(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @Nullable ConnectionRequest request) {
+    }
+
+    /**
+     * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+     * outgoing {@link Conference} was denied.
+     * <p>
+     * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
+     * {@link Conference}, but Telecom has determined that the call cannot be placed at this time.
+     * The {@link ConnectionService} is responisible for informing the user that the
+     * {@link Conference} cannot be made at this time.
+     * <p>
+     * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
+     *
+     * @param connectionManagerPhoneAccount See description at
+     *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+     * @param request The outgoing connection request.
+     */
+    public void onCreateOutgoingConferenceFailed(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @Nullable ConnectionRequest request) {
+    }
+
+
+    /**
      * Trigger recalculate functinality for conference calls. This is used when a Telephony
      * Connection is part of a conference controller but is not yet added to Connection
      * Service and hence cannot be added to the conference call.
@@ -2289,6 +2640,36 @@
     }
 
     /**
+     * Create a {@code Conference} given an outgoing request. This is used to initiate new
+     * outgoing conference call.
+     *
+     * @param connectionManagerPhoneAccount The connection manager account to use for managing
+     *         this call.
+     *         <p>
+     *         If this parameter is not {@code null}, it means that this {@code ConnectionService}
+     *         has registered one or more {@code PhoneAccount}s having
+     *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
+     *         one of these {@code PhoneAccount}s, while the {@code request} will contain another
+     *         (usually but not always distinct) {@code PhoneAccount} to be used for actually
+     *         making the connection.
+     *         <p>
+     *         If this parameter is {@code null}, it means that this {@code ConnectionService} is
+     *         being asked to make a direct connection. The
+     *         {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
+     *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
+     *         making the connection.
+     * @param request Details about the outgoing call.
+     * @return The {@code Conference} object to satisfy this call, or the result of an invocation
+     *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+     */
+    public @Nullable Conference onCreateOutgoingConference(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @Nullable ConnectionRequest request) {
+        return null;
+    }
+
+
+    /**
      * Called by Telecom to request that a {@link ConnectionService} creates an instance of an
      * outgoing handover {@link Connection}.
      * <p>
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 04e930c..8f27323 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -100,6 +100,19 @@
         }
     }
 
+    void handleCreateConferenceComplete(
+            String id,
+            ConnectionRequest request,
+            ParcelableConference conference) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.handleCreateConferenceComplete(id, request, conference,
+                        Log.getExternalSession());
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /**
      * Sets a call's state to active (e.g., an ongoing call where two parties can actively
      * communicate).
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 60b2172..79ad51b 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -75,6 +75,7 @@
     private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34;
     private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
     private static final int MSG_SET_CONFERENCE_STATE = 36;
+    private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -103,6 +104,19 @@
                     }
                     break;
                 }
+                case MSG_HANDLE_CREATE_CONFERENCE_COMPLETE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.handleCreateConferenceComplete(
+                                (String) args.arg1,
+                                (ConnectionRequest) args.arg2,
+                                (ParcelableConference) args.arg3,
+                                null /*Session.Info*/);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
                 case MSG_SET_ACTIVE:
                     mDelegate.setActive((String) msg.obj, null /*Session.Info*/);
                     break;
@@ -366,6 +380,20 @@
         }
 
         @Override
+        public void handleCreateConferenceComplete(
+                String id,
+                ConnectionRequest request,
+                ParcelableConference conference,
+                Session.Info sessionInfo) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = id;
+            args.arg2 = request;
+            args.arg3 = conference;
+            mHandler.obtainMessage(MSG_HANDLE_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
+        }
+
+
+        @Override
         public void setActive(String connectionId, Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget();
         }
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index ede0594..90b69a3 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -47,6 +47,34 @@
     private final int mAddressPresentation;
     private final String mCallerDisplayName;
     private final int mCallerDisplayNamePresentation;
+    private DisconnectCause mDisconnectCause;
+    private boolean mRingbackRequested;
+
+    public ParcelableConference(
+            PhoneAccountHandle phoneAccount,
+            int state,
+            int connectionCapabilities,
+            int connectionProperties,
+            List<String> connectionIds,
+            IVideoProvider videoProvider,
+            int videoState,
+            long connectTimeMillis,
+            long connectElapsedTimeMillis,
+            StatusHints statusHints,
+            Bundle extras,
+            Uri address,
+            int addressPresentation,
+            String callerDisplayName,
+            int callerDisplayNamePresentation,
+            DisconnectCause disconnectCause,
+            boolean ringbackRequested) {
+        this(phoneAccount, state, connectionCapabilities, connectionProperties, connectionIds,
+                videoProvider, videoState, connectTimeMillis, connectElapsedTimeMillis,
+                statusHints, extras, address, addressPresentation, callerDisplayName,
+                callerDisplayNamePresentation);
+        mDisconnectCause = disconnectCause;
+        mRingbackRequested = ringbackRequested;
+    }
 
     public ParcelableConference(
             PhoneAccountHandle phoneAccount,
@@ -79,6 +107,8 @@
         mAddressPresentation = addressPresentation;
         mCallerDisplayName = callerDisplayName;
         mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+        mDisconnectCause = null;
+        mRingbackRequested = false;
     }
 
     @Override
@@ -100,6 +130,10 @@
                 .append(mVideoState)
                 .append(", VideoProvider: ")
                 .append(mVideoProvider)
+                .append(", isRingbackRequested: ")
+                .append(mRingbackRequested)
+                .append(", disconnectCause: ")
+                .append(mDisconnectCause)
                 .toString();
     }
 
@@ -151,6 +185,13 @@
         return mAddress;
     }
 
+    public final DisconnectCause getDisconnectCause() {
+        return mDisconnectCause;
+    }
+
+    public boolean isRingbackRequested() {
+        return mRingbackRequested;
+    }
     public int getHandlePresentation() {
         return mAddressPresentation;
     }
@@ -177,11 +218,14 @@
             int addressPresentation = source.readInt();
             String callerDisplayName = source.readString();
             int callerDisplayNamePresentation = source.readInt();
+            DisconnectCause disconnectCause = source.readParcelable(classLoader);
+            boolean isRingbackRequested = source.readInt() == 1;
 
             return new ParcelableConference(phoneAccount, state, capabilities, properties,
                     connectionIds, videoCallProvider, videoState, connectTimeMillis,
                     connectElapsedTimeMillis, statusHints, extras, address, addressPresentation,
-                    callerDisplayName, callerDisplayNamePresentation);
+                    callerDisplayName, callerDisplayNamePresentation, disconnectCause,
+                    isRingbackRequested);
         }
 
         @Override
@@ -215,5 +259,7 @@
         destination.writeInt(mAddressPresentation);
         destination.writeString(mCallerDisplayName);
         destination.writeInt(mCallerDisplayNamePresentation);
+        destination.writeParcelable(mDisconnectCause, 0);
+        destination.writeInt(mRingbackRequested ? 1 : 0);
     }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index bb858cb..abb210f 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -331,7 +331,17 @@
      */
     public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000;
 
-    /* NEXT CAPABILITY: 0x4000 */
+    /**
+     * An adhoc conference call is established by providing a list of addresses to
+     * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the
+     * {@link ConnectionService} is responsible for connecting all indicated participants
+     * to a conference simultaneously.
+     * This is in contrast to conferences formed by merging calls together (e.g. using
+     * {@link android.telecom.Call#mergeConference()}).
+     */
+    public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 0x4000;
+
+    /* NEXT CAPABILITY: 0x8000 */
 
     /**
      * URI scheme for telephone number URIs.
@@ -1054,6 +1064,9 @@
         if (hasCapabilities(CAPABILITY_RTT)) {
             sb.append("Rtt");
         }
+        if (hasCapabilities(CAPABILITY_ADHOC_CONFERENCE_CALLING)) {
+            sb.append("AdhocConf");
+        }
         return sb.toString();
     }
 
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 1e73bd6..76640e0 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -101,6 +101,14 @@
         }
 
         @Override
+        public void handleCreateConferenceComplete(
+                String id,
+                ConnectionRequest request,
+                ParcelableConference parcel,
+                Session.Info info) {
+        }
+
+        @Override
         public void setActive(String callId, Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "setActive")
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index c3fb510..49b74c6 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1810,6 +1810,45 @@
     }
 
     /**
+     * Registers a new incoming conference. A {@link ConnectionService} should invoke this method
+     * when it has an incoming conference. For managed {@link ConnectionService}s, the specified
+     * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
+     * the user must have enabled the corresponding {@link PhoneAccount}.  This can be checked using
+     * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have
+     * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call.
+     * <p>
+     * The incoming conference you are adding is assumed to have a video state of
+     * {@link VideoProfile#STATE_AUDIO_ONLY}, unless the extra value
+     * {@link #EXTRA_INCOMING_VIDEO_STATE} is specified.
+     * <p>
+     * Once invoked, this method will cause the system to bind to the {@link ConnectionService}
+     * associated with the {@link PhoneAccountHandle} and request additional information about the
+     * call (See {@link ConnectionService#onCreateIncomingConference}) before starting the incoming
+     * call UI.
+     * <p>
+     * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either
+     * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or
+     * the associated {@link PhoneAccount} is not currently enabled by the user.
+     *
+     * @param phoneAccount A {@link PhoneAccountHandle} registered with
+     *            {@link #registerPhoneAccount}.
+     * @param extras A bundle that will be passed through to
+     *            {@link ConnectionService#onCreateIncomingConference}.
+     */
+
+    public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount,
+            @NonNull Bundle extras) {
+        try {
+            if (isServiceConnected()) {
+                getTelecomService().addNewIncomingConference(
+                        phoneAccount, extras == null ? new Bundle() : extras);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e);
+        }
+    }
+
+    /**
      * Registers a new unknown call with Telecom. This can only be called by the system Telephony
      * service. This is invoked when Telephony detects a new unknown connection that was neither
      * a new incoming call, nor an user-initiated outgoing call.
@@ -2014,6 +2053,42 @@
         }
     }
 
+
+    /**
+     * Place a new conference call with the provided participants using the system telecom service
+     * This method doesn't support placing of emergency calls.
+     *
+     * An adhoc conference call is established by providing a list of addresses to
+     * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the
+     * {@link ConnectionService} is responsible for connecting all indicated participants
+     * to a conference simultaneously.
+     * This is in contrast to conferences formed by merging calls together (e.g. using
+     * {@link android.telecom.Call#mergeConference()}).
+     *
+     * The following keys are supported in the supplied extras.
+     * <ul>
+     *   <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
+     *   <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
+     *   <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
+     * </ul>
+     *
+     * @param participants List of participants to start conference with
+     * @param extras Bundle of extras to use with the call
+     */
+    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+    public void startConference(@NonNull List<Uri> participants,
+            @NonNull Bundle extras) {
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                service.startConference(participants, extras,
+                        mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error calling ITelecomService#placeCall", e);
+            }
+        }
+    }
+
     /**
      * Enables and disables specified phone account.
      *
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index e35093c..96f2483 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -53,6 +53,20 @@
     void createConnectionFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
             in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
 
+    void createConference(
+            in PhoneAccountHandle connectionManagerPhoneAccount,
+            String callId,
+            in ConnectionRequest request,
+            boolean isIncoming,
+            boolean isUnknown,
+            in Session.Info sessionInfo);
+
+    void createConferenceComplete(String callId, in Session.Info sessionInfo);
+
+    void createConferenceFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
+            in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
+
+
     void abort(String callId, in Session.Info sessionInfo);
 
     void answerVideo(String callId, int videoState, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 9cf098c..4f63e08 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -44,6 +44,12 @@
             in ParcelableConnection connection,
             in Session.Info sessionInfo);
 
+    void handleCreateConferenceComplete(
+            String callId,
+            in ConnectionRequest request,
+            in ParcelableConference connection,
+            in Session.Info sessionInfo);
+
     void setActive(String callId, in Session.Info sessionInfo);
 
     void setRinging(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index c54da6b..285cf43 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -246,11 +246,22 @@
     void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
 
     /**
+     * @see TelecomServiceImpl#addNewIncomingConference
+     */
+    void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras);
+
+    /**
      * @see TelecomServiceImpl#addNewUnknownCall
      */
     void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
 
     /**
+     * @see TelecomServiceImpl#startConference
+     */
+    void startConference(in List<Uri> participants, in Bundle extras,
+            String callingPackage);
+
+    /**
      * @see TelecomServiceImpl#placeCall
      */
     void placeCall(in Uri handle, in Bundle extras, String callingPackage, String callingFeatureId);
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index b1d647f..610eef8 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -317,6 +317,159 @@
         public static final int BAND_260 = 260;
         public static final int BAND_261 = 261;
 
+        /**
+         * NR Bands
+         *
+         * @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"BAND_"},
+                value = {BAND_1,
+                        BAND_2,
+                        BAND_3,
+                        BAND_5,
+                        BAND_7,
+                        BAND_8,
+                        BAND_12,
+                        BAND_14,
+                        BAND_18,
+                        BAND_20,
+                        BAND_25,
+                        BAND_28,
+                        BAND_29,
+                        BAND_30,
+                        BAND_34,
+                        BAND_38,
+                        BAND_39,
+                        BAND_40,
+                        BAND_41,
+                        BAND_48,
+                        BAND_50,
+                        BAND_51,
+                        BAND_65,
+                        BAND_66,
+                        BAND_70,
+                        BAND_71,
+                        BAND_74,
+                        BAND_75,
+                        BAND_76,
+                        BAND_77,
+                        BAND_78,
+                        BAND_79,
+                        BAND_80,
+                        BAND_81,
+                        BAND_82,
+                        BAND_83,
+                        BAND_84,
+                        BAND_86,
+                        BAND_90,
+                        BAND_257,
+                        BAND_258,
+                        BAND_260,
+                        BAND_261})
+        public @interface NgranBand {}
+
+        /**
+         * Unknown NR frequency.
+         *
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0;
+
+        /**
+         * NR frequency group 1 defined in 3GPP TS 38.101-1 table 5.2-1
+         *
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static final int FREQUENCY_RANGE_GROUP_1 = 1;
+
+        /**
+         * NR frequency group 2 defined in 3GPP TS 38.101-2 table 5.2-1
+         *
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static final int FREQUENCY_RANGE_GROUP_2 = 2;
+
+        /**
+         * Radio frequency range group
+         *
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"FREQUENCY_RANGE_GROUP_"},
+                value = {
+                        FREQUENCY_RANGE_GROUP_UNKNOWN,
+                        FREQUENCY_RANGE_GROUP_1,
+                        FREQUENCY_RANGE_GROUP_2})
+        public @interface FrequencyRangeGroup {}
+
+        /**
+         * Get frequency range group
+         *
+         * @param band NR band
+         * @return The frequency range group
+         *
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static @FrequencyRangeGroup int getFrequencyRangeGroup(@NgranBand int band) {
+            switch (band) {
+                case BAND_1:
+                case BAND_2:
+                case BAND_3:
+                case BAND_5:
+                case BAND_7:
+                case BAND_8:
+                case BAND_12:
+                case BAND_14:
+                case BAND_18:
+                case BAND_20:
+                case BAND_25:
+                case BAND_28:
+                case BAND_29:
+                case BAND_30:
+                case BAND_34:
+                case BAND_38:
+                case BAND_39:
+                case BAND_40:
+                case BAND_41:
+                case BAND_48:
+                case BAND_50:
+                case BAND_51:
+                case BAND_65:
+                case BAND_66:
+                case BAND_70:
+                case BAND_71:
+                case BAND_74:
+                case BAND_75:
+                case BAND_76:
+                case BAND_77:
+                case BAND_78:
+                case BAND_79:
+                case BAND_80:
+                case BAND_81:
+                case BAND_82:
+                case BAND_83:
+                case BAND_84:
+                case BAND_86:
+                case BAND_90:
+                    return FREQUENCY_RANGE_GROUP_1;
+                case BAND_257:
+                case BAND_258:
+                case BAND_260:
+                case BAND_261:
+                    return FREQUENCY_RANGE_GROUP_2;
+                default:
+                    return FREQUENCY_RANGE_GROUP_UNKNOWN;
+            }
+        };
+
         /** @hide */
         private NgranBands() {}
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7abad72..822e55b 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1080,6 +1080,14 @@
     public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
             "ignore_rtt_mode_setting_bool";
 
+
+    /**
+     * Determines whether adhoc conference calls are supported by a carrier.  When {@code true},
+     * adhoc conference calling is supported, {@code false otherwise}.
+     */
+    public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL =
+            "support_adhoc_conference_calls_bool";
+
     /**
      * Determines whether conference calls are supported by a carrier.  When {@code true},
      * conference calling is supported, {@code false otherwise}.
@@ -2967,7 +2975,6 @@
     /**
      * Controls hysteresis time in milli seconds for which OpportunisticNetworkService
      * will wait before switching data from opportunistic network to primary network.
-     * @hide
      */
     public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG =
             "opportunistic_network_data_switch_exit_hysteresis_time_long";
@@ -2975,14 +2982,12 @@
     /**
      * Controls whether to do ping test before switching data to opportunistic network.
      * This carrier config is used to disable this feature.
-     * @hide
      */
     public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL =
             "ping_test_before_data_switch_bool";
 
     /**
      * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
-     * @hide
      */
     public static final String KEY_5G_WATCHDOG_TIME_MS_LONG =
             "5g_watchdog_time_long";
@@ -2991,7 +2996,6 @@
      * if primary is out of service. This control only affects system or 1st party app
      * initiated data switch, but will not override data switch initiated by privileged carrier apps
      * This carrier config is used to disable this feature.
-     * @hide
      */
     public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL =
             "switch_data_to_primary_if_primary_is_oos_bool";
@@ -3003,7 +3007,6 @@
      * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT within
      * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG of switching to opportunistic network,
      * it will be determined as ping pong situation by system app or 1st party app.
-     * @hide
      */
     public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG =
             "opportunistic_network_ping_pong_time_long";
@@ -3017,7 +3020,6 @@
      * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG.
      * If ping pong situation continuous #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG
      * will be added to previously determined hysteresis time.
-     * @hide
      */
     public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG =
             "opportunistic_network_backoff_time_long";
@@ -3029,7 +3031,6 @@
      * continuous ping pong situation or not as described in
      * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG and
      * #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG.
-     * @hide
      */
     public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG =
             "opportunistic_network_max_backoff_time_long";
@@ -3076,7 +3077,6 @@
      * validation result, this value defines customized value of how long we wait for validation
      * success before we fail and revoke the switch.
      * Time out is in milliseconds.
-     * @hide
      */
     public static final String KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG =
             "data_switch_validation_timeout_long";
@@ -3948,6 +3948,7 @@
         sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
         sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
+        sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 0034a05..40927a1 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
+import android.telephony.AccessNetworkConstants.NgranBands.NgranBand;
 import android.telephony.gsm.GsmCellLocation;
 
 import java.util.Collections;
@@ -41,6 +42,7 @@
     private final int mPci;
     private final int mTac;
     private final long mNci;
+    private final int mBand;
 
     // a list of additional PLMN-IDs reported for this cell
     private final List<String> mAdditionalPlmns;
@@ -50,6 +52,7 @@
      * @param pci Physical Cell Id in range [0, 1007].
      * @param tac 16-bit Tracking Area Code.
      * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165].
+     * @param band Band number defined in 3GPP TS 38.101-1 and TS 38.101-2.
      * @param mccStr 3-digit Mobile Country Code in string format.
      * @param mncStr 2 or 3-digit Mobile Network Code in string format.
      * @param nci The 36-bit NR Cell Identity in range [0, 68719476735].
@@ -59,25 +62,28 @@
      *
      * @hide
      */
-    public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr,
-            long nci, String alphal, String alphas, List<String> additionalPlmns) {
+    public CellIdentityNr(int pci, int tac, int nrArfcn, @NgranBand int band, String mccStr,
+            String mncStr, long nci, String alphal, String alphas, List<String> additionalPlmns) {
         super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
+        mBand = inRangeOrUnavailable(band, AccessNetworkConstants.NgranBands.BAND_1,
+                AccessNetworkConstants.NgranBands.BAND_261);
         mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
         mAdditionalPlmns = additionalPlmns;
     }
 
     /** @hide */
     public CellIdentityNr(android.hardware.radio.V1_4.CellIdentityNr cid) {
-        this(cid.pci, cid.tac, cid.nrarfcn, cid.mcc, cid.mnc, cid.nci, cid.operatorNames.alphaLong,
-                cid.operatorNames.alphaShort, Collections.emptyList());
+        this(cid.pci, cid.tac, cid.nrarfcn, 0, cid.mcc, cid.mnc, cid.nci,
+                cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
+                Collections.emptyList());
     }
 
     /** @hide */
     public CellIdentityNr(android.hardware.radio.V1_5.CellIdentityNr cid) {
-        this(cid.base.pci, cid.base.tac, cid.base.nrarfcn, cid.base.mcc, cid.base.mnc,
+        this(cid.base.pci, cid.base.tac, cid.base.nrarfcn, cid.band, cid.base.mcc, cid.base.mnc,
                 cid.base.nci, cid.base.operatorNames.alphaLong,
                 cid.base.operatorNames.alphaShort, cid.additionalPlmns);
     }
@@ -85,8 +91,9 @@
     /** @hide */
     @Override
     public @NonNull CellIdentityNr sanitizeLocationInfo() {
-        return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
-                mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort, mAdditionalPlmns);
+        return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mNrArfcn,
+                mBand, mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort,
+                mAdditionalPlmns);
     }
 
     /**
@@ -102,7 +109,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(super.hashCode(), mPci, mTac,
-                mNrArfcn, mNci, mAdditionalPlmns.hashCode());
+                mNrArfcn, mBand, mNci, mAdditionalPlmns.hashCode());
     }
 
     @Override
@@ -113,7 +120,8 @@
 
         CellIdentityNr o = (CellIdentityNr) other;
         return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn
-                && mNci == o.mNci && mAdditionalPlmns.equals(o.mAdditionalPlmns);
+                && mBand == o.mBand && mNci == o.mNci
+                && mAdditionalPlmns.equals(o.mAdditionalPlmns);
     }
 
     /**
@@ -140,6 +148,19 @@
     }
 
     /**
+     * Get band of the cell
+     *
+     * Reference: TS 38.101-1 table 5.2-1
+     * Reference: TS 38.101-2 table 5.2-1
+     *
+     * @return band number or {@link CellInfo@UNAVAILABLE} if not available.
+     */
+    @NgranBand
+    public int getBand() {
+        return mBand;
+    }
+
+    /**
      * Get the physical cell id.
      * @return Integer value in range [0, 1007] or {@link CellInfo#UNAVAILABLE} if unknown.
      */
@@ -193,6 +214,7 @@
                 .append(" mPci = ").append(mPci)
                 .append(" mTac = ").append(mTac)
                 .append(" mNrArfcn = ").append(mNrArfcn)
+                .append(" mBand = ").append(mBand)
                 .append(" mMcc = ").append(mMccStr)
                 .append(" mMnc = ").append(mMncStr)
                 .append(" mNci = ").append(mNci)
@@ -209,6 +231,7 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mNrArfcn);
+        dest.writeInt(mBand);
         dest.writeLong(mNci);
         dest.writeList(mAdditionalPlmns);
     }
@@ -219,6 +242,7 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mNrArfcn = in.readInt();
+        mBand = in.readInt();
         mNci = in.readLong();
         mAdditionalPlmns = in.readArrayList(null);
     }
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index c706d28..9b4292f 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -54,6 +54,43 @@
             "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
 
     /**
+     * An intent action indicating that IMS registration for WiFi calling has resulted in an error.
+     * Contains error information that should be displayed to the user.
+     * <p>
+     * This intent will contain the following extra key/value pairs:
+     * {@link #EXTRA_WFC_REGISTRATION_FAILURE_TITLE}
+     * and {@link #EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE}, which contain carrier specific
+     * error information that should be displayed to the user.
+     * <p>
+     * Usage: This intent is sent as an ordered broadcast. If the settings application is going
+     * to show the error information specified to the user, it should respond to
+     * {@link android.content.BroadcastReceiver#setResultCode(int)} with
+     * {@link android.app.Activity#RESULT_CANCELED}, which will signal to the framework that the
+     * event was handled. If the framework does not receive a response to the ordered broadcast,
+     * it will then show a notification to the user indicating that there was a registration
+     * failure.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_WFC_IMS_REGISTRATION_ERROR =
+            "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+
+    /**
+     * An extra key corresponding to a String value which contains the carrier specific title to be
+     * displayed as part of the message shown to the user when there is an error registering for
+     * WiFi calling.
+     */
+    public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE =
+            "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
+
+    /**
+     * An extra key corresponding to a String value which contains the carrier specific message to
+     * be displayed as part of the message shown to the user when there is an error registering for
+     * WiFi calling.
+     */
+    public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
+            "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+
+    /**
      * Use {@link Context#getSystemService(String)} to get an instance of this class.
      * @hide
      */
diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
deleted file mode 100644
index c0833af..0000000
--- a/telephony/java/android/telephony/ModemInfo.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * Information of a single logical modem indicating
- * its id, supported rats and whether it supports voice or data, etc.
- * @hide
- */
-public class ModemInfo implements Parcelable {
-    public final int modemId;
-    public final int rat; /* bitset */
-    public final boolean isVoiceSupported;
-    public final boolean isDataSupported;
-
-    // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
-    public ModemInfo(int modemId) {
-        this(modemId, 0, true, true);
-    }
-
-    public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
-        this.modemId = modemId;
-        this.rat = rat;
-        this.isVoiceSupported = isVoiceSupported;
-        this.isDataSupported = isDataSupported;
-    }
-
-    public ModemInfo(Parcel in) {
-        modemId = in.readInt();
-        rat = in.readInt();
-        isVoiceSupported = in.readBoolean();
-        isDataSupported = in.readBoolean();
-    }
-
-    @Override
-    public String toString() {
-        return "modemId=" + modemId + " rat=" + rat + " isVoiceSupported:" + isVoiceSupported
-                + " isDataSupported:" + isDataSupported;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(modemId, rat, isVoiceSupported, isDataSupported);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == null || !(o instanceof ModemInfo) || hashCode() != o.hashCode()) {
-            return false;
-        }
-
-        if (this == o) {
-            return true;
-        }
-
-        ModemInfo s = (ModemInfo) o;
-
-        return (modemId == s.modemId
-                && rat == s.rat
-                && isVoiceSupported == s.isVoiceSupported
-                && isDataSupported == s.isDataSupported);
-    }
-
-    /**
-     * {@link Parcelable#describeContents}
-     */
-    public @ContentsFlags int describeContents() {
-        return 0;
-    }
-
-    /**
-     * {@link Parcelable#writeToParcel}
-     */
-    public void writeToParcel(Parcel dest, @WriteFlags int flags) {
-        dest.writeInt(modemId);
-        dest.writeInt(rat);
-        dest.writeBoolean(isVoiceSupported);
-        dest.writeBoolean(isDataSupported);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<ModemInfo> CREATOR = new Parcelable.Creator() {
-        public ModemInfo createFromParcel(Parcel in) {
-            return new ModemInfo(in);
-        }
-
-        public ModemInfo[] newArray(int size) {
-            return new ModemInfo[size];
-        }
-    };
-}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 075b56b..a1f3cef 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -50,7 +50,6 @@
 import android.net.NetworkStats;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -161,8 +160,8 @@
      * into the ResultReceiver Bundle.
      * @hide
      */
-    public static final String MODEM_ACTIVITY_RESULT_KEY =
-            BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
+    @SystemApi
+    public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
 
     /**
      * The process name of the Phone app as well as many other apps that use this process name, such
@@ -1245,6 +1244,80 @@
     public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
 
     /**
+     * Broadcast Action: The Service Provider string(s) have been updated. Activities or
+     * services that use these strings should update their display.
+     *
+     * <p>The intent will have the following extra values:
+     * <dl>
+     *   <dt>{@link #EXTRA_SHOW_PLMN}</dt>
+     *   <dd>Boolean that indicates whether the PLMN should be shown.</dd>
+     *   <dt>{@link #EXTRA_PLMN}</dt>
+     *   <dd>The operator name of the registered network, as a string.</dd>
+     *   <dt>{@link #EXTRA_SHOW_SPN}</dt>
+     *   <dd>Boolean that indicates whether the SPN should be shown.</dd>
+     *   <dt>{@link #EXTRA_SPN}</dt>
+     *   <dd>The service provider name, as a string.</dd>
+     *   <dt>{@link #EXTRA_DATA_SPN}</dt>
+     *   <dd>The service provider name for data service, as a string.</dd>
+     * </dl>
+     *
+     * Note that {@link #EXTRA_SHOW_PLMN} may indicate that {@link #EXTRA_PLMN} should be displayed,
+     * even though the value for {@link #EXTRA_PLMN} is null. This can happen, for example, if the
+     * phone has not registered to a network yet. In this case the receiver may substitute an
+     * appropriate placeholder string (eg, "No service").
+     *
+     * It is recommended to display {@link #EXTRA_PLMN} before / above {@link #EXTRA_SPN} if
+     * both are displayed.
+     *
+     * <p>Note: this is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SERVICE_PROVIDERS_UPDATED =
+            "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * whether the PLMN should be shown.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * the operator name of the registered network.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * whether the PLMN should be shown.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * the service provider name.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SPN = "android.telephony.extra.SPN";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * the service provider name for data service.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
+
+    /**
      * Broadcast intent action indicating that when data stall recovery is attempted by Telephony,
      * intended for report every data stall recovery step attempted.
      *
@@ -7939,6 +8012,36 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @param operatorNumeric the PLMN ID of the network to select.
+     * @param ran the initial suggested radio access network type.
+     *         If registration fails, the RAN is not available after, the RAN is not within the
+     *         network types specified by {@link #setPreferredNetworkTypeBitmask}, or the value is
+     *         {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
+     *         the next best RAN for network registration.
+     * @param persistSelection whether the selection will persist until reboot.
+     *         If true, only allows attaching to the selected PLMN until reboot; otherwise,
+     *         attach to the chosen PLMN and resume normal network selection next time.
+     * @return {@code true} on success; {@code false} on any failure.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public boolean setNetworkSelectionModeManual(@NonNull String operatorNumeric,
+            @AccessNetworkConstants.RadioAccessNetworkType int ran, boolean persistSelection) {
+        return setNetworkSelectionModeManual(new OperatorInfo("" /* operatorAlphaLong */,
+                "" /* operatorAlphaShort */, operatorNumeric, ran), persistSelection);
+    }
+
+    /**
+     * Ask the radio to connect to the input network and change selection mode to manual.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param operatorInfo included the PLMN id, long name, short name of the operator to attach to.
      * @param persistSelection whether the selection will persist until reboot. If true, only allows
      * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
@@ -10206,7 +10309,8 @@
      * Requests the modem activity info. The recipient will place the result
      * in `result`.
      * @param result The object on which the recipient will send the resulting
-     * {@link android.telephony.ModemActivityInfo} object.
+     * {@link android.telephony.ModemActivityInfo} object with key of
+     * {@link #MODEM_ACTIVITY_RESULT_KEY}.
      * @hide
      */
     @SystemApi
@@ -12810,4 +12914,22 @@
         }
         return 0;
     }
+
+    /**
+     * Called when userActivity is signalled in the power manager.
+     * This should only be called from system Uid.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyUserActivity() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.userActivity();
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9b45539..0bc6640 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2186,4 +2186,15 @@
     int setIccLockEnabled(int subId, boolean enabled, String password);
 
     int changeIccLockPassword(int subId, String oldPassword, String newPassword);
+
+    /**
+     * Request for receiving user activity notification
+     */
+    oneway void requestUserActivityNotification();
+
+    /**
+     * Called when userActivity is signalled in the power manager.
+     * This is safe to call from any thread, with any window manager locks held or not.
+     */
+    oneway void userActivity();
 }
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index 64d7863..2ca4598 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -20,6 +20,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
 
 /**
  * @hide
@@ -43,6 +44,7 @@
 
     @UnsupportedAppUsage
     private State mState = State.UNKNOWN;
+    private int mRan = AccessNetworkType.UNKNOWN;
 
 
     @UnsupportedAppUsage
@@ -69,6 +71,10 @@
         return mState;
     }
 
+    public int getRan() {
+        return mRan;
+    }
+
     @UnsupportedAppUsage
     OperatorInfo(String operatorAlphaLong,
                 String operatorAlphaShort,
@@ -82,6 +88,14 @@
         mState = state;
     }
 
+    OperatorInfo(String operatorAlphaLong,
+                String operatorAlphaShort,
+                String operatorNumeric,
+                State state,
+                int ran) {
+        this (operatorAlphaLong, operatorAlphaShort, operatorNumeric, state);
+        mRan = ran;
+    }
 
     @UnsupportedAppUsage
     public OperatorInfo(String operatorAlphaLong,
@@ -92,6 +106,14 @@
                 operatorNumeric, rilStateToState(stateString));
     }
 
+    public OperatorInfo(String operatorAlphaLong,
+                String operatorAlphaShort,
+                String operatorNumeric,
+                int ran) {
+        this (operatorAlphaLong, operatorAlphaShort, operatorNumeric);
+        mRan = ran;
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public OperatorInfo(String operatorAlphaLong,
             String operatorAlphaShort,
@@ -124,7 +146,8 @@
         return "OperatorInfo " + mOperatorAlphaLong
                 + "/" + mOperatorAlphaShort
                 + "/" + mOperatorNumeric
-                + "/" + mState;
+                + "/" + mState
+                + "/" + mRan;
     }
 
     /**
@@ -150,6 +173,7 @@
         dest.writeString(mOperatorAlphaShort);
         dest.writeString(mOperatorNumeric);
         dest.writeSerializable(mState);
+        dest.writeInt(mRan);
     }
 
     /**
@@ -158,20 +182,21 @@
      */
     @UnsupportedAppUsage
     public static final Creator<OperatorInfo> CREATOR =
-        new Creator<OperatorInfo>() {
-            @Override
-            public OperatorInfo createFromParcel(Parcel in) {
-                OperatorInfo opInfo = new OperatorInfo(
-                        in.readString(), /*operatorAlphaLong*/
-                        in.readString(), /*operatorAlphaShort*/
-                        in.readString(), /*operatorNumeric*/
-                        (State) in.readSerializable()); /*state*/
-                return opInfo;
-            }
+            new Creator<OperatorInfo>() {
+                @Override
+                public OperatorInfo createFromParcel(Parcel in) {
+                    OperatorInfo opInfo = new OperatorInfo(
+                            in.readString(), /*operatorAlphaLong*/
+                            in.readString(), /*operatorAlphaShort*/
+                            in.readString(), /*operatorNumeric*/
+                            (State) in.readSerializable(), /*state*/
+                            in.readInt()); /*ran*/
+                    return opInfo;
+                }
 
-            @Override
-            public OperatorInfo[] newArray(int size) {
-                return new OperatorInfo[size];
-            }
-        };
+                @Override
+                public OperatorInfo[] newArray(int size) {
+                    return new OperatorInfo[size];
+                }
+            };
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index a15f73c..b697d58 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -180,8 +180,6 @@
     public static final String ACTION_SIM_STATE_CHANGED
             = Intent.ACTION_SIM_STATE_CHANGED;
 
-    public static final String EXTRA_REBROADCAST_ON_UNLOCK= "rebroadcastOnUnlock";
-
     /**
      * <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms
      * <p class="note">.
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index 592aa3a..153ca79 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -58,4 +58,31 @@
                 ambientModeChangedCount[0], 2);
     }
 
+    @Test
+    public void testDeliversZoomChanged() {
+        int[] zoomChangedCount = {0};
+        WallpaperService service = new WallpaperService() {
+            @Override
+            public Engine onCreateEngine() {
+                return new Engine() {
+                    @Override
+                    public void onZoomChanged(float zoom) {
+                        super.onZoomChanged(zoom);
+                        zoomChangedCount[0]++;
+                    }
+                };
+            }
+        };
+        WallpaperService.Engine engine = service.onCreateEngine();
+        engine.setCreated(true);
+
+        engine.setZoom(.5f);
+        assertEquals("engine scale was not updated", .5f, engine.getZoom(), .001f);
+        assertEquals("onZoomChanged should have been called", 1, zoomChangedCount[0]);
+
+        engine.setZoom(0);
+        assertEquals("engine scale was not updated", 0, engine.getZoom(), .001f);
+        assertEquals("onAmbientModeChanged should have been called", 2, zoomChangedCount[0]);
+    }
+
 }
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 8eac3ea..fe0224a 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -234,7 +234,9 @@
             try {
                 mLooper = setAsMain ? Looper.getMainLooper() : createLooper();
                 mTestableLooper = new TestableLooper(mLooper, false);
-                mTestableLooper.getLooper().getThread().setName(test.getClass().getName());
+                if (!setAsMain) {
+                    mTestableLooper.getLooper().getThread().setName(test.getClass().getName());
+                }
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0b5969a..d13a625 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -260,4 +260,9 @@
     boolean setWifiConnectedNetworkScorer(in IBinder binder, in IWifiConnectedNetworkScorer scorer);
 
     void clearWifiConnectedNetworkScorer();
+
+    /**
+     * Return the Map of {@link WifiNetworkSuggestion} and the list of <ScanResult>
+     */
+    Map getMatchingScanResults(in List<WifiNetworkSuggestion> networkSuggestions, in List<ScanResult> scanResults, String callingPackage, String callingFeatureId);
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 208ce9c..f6e3ff0 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2685,6 +2685,34 @@
     }
 
     /**
+     * Return the filtered ScanResults which may be authenticated by the suggested network
+     * configurations.
+     * @param networkSuggestions The list of {@link WifiNetworkSuggestion}
+     * @param scanResults The scan results to be filtered, this is optional, if it is null or
+     * empty, wifi system would use the recent scan results in the system.
+     * @return The map of {@link WifiNetworkSuggestion} and the list of {@link ScanResult} which
+     * may be authenticated by the corresponding network configuration.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
+    @NonNull
+    public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults(
+            @NonNull List<WifiNetworkSuggestion> networkSuggestions,
+            @Nullable List<ScanResult> scanResults) {
+        if (networkSuggestions == null) {
+            throw new IllegalArgumentException("networkSuggestions must not be null.");
+        }
+        try {
+            return mService.getMatchingScanResults(
+                    networkSuggestions, scanResults,
+                    mContext.getOpPackageName(), mContext.getFeatureId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check if scanning is always available.
      *
      * If this return {@code true}, apps can issue {@link #startScan} and fetch scan results
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 56fa6e2..19d09d1 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -617,4 +617,12 @@
     public void clearWifiConnectedNetworkScorer() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults(
+            List<WifiNetworkSuggestion> networkSuggestions,
+            List<ScanResult> scanResults,
+            String callingPackage, String callingFeatureId) {
+        throw new UnsupportedOperationException();
+    }
 }