Merge "Suppress cert-dcl50-cpp tidy warnings."
diff --git a/api/current.txt b/api/current.txt
index 00d4cb5..80fea7b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -44336,6 +44336,7 @@
     method public int describeContents();
     method public int getCdmaDbm();
     method public int getCdmaEcio();
+    method public java.util.List<android.telephony.CellSignalStrength> getCellSignalStrengths();
     method public int getEvdoDbm();
     method public int getEvdoEcio();
     method public int getEvdoSnr();
@@ -44778,6 +44779,9 @@
   public static abstract class TelephonyManager.CellInfoCallback {
     ctor public TelephonyManager.CellInfoCallback();
     method public abstract void onCellInfo(java.util.List<android.telephony.CellInfo>);
+    method public void onError(int, java.lang.Throwable);
+    field public static final int ERROR_MODEM_ERROR = 2; // 0x2
+    field public static final int ERROR_TIMEOUT = 1; // 0x1
   }
 
   public static abstract class TelephonyManager.UssdResponseCallback {
diff --git a/api/system-current.txt b/api/system-current.txt
index f686604..6c764b4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4880,6 +4880,14 @@
     field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
   }
 
+  public final class RuntimePermissionUsageInfo implements android.os.Parcelable {
+    ctor public RuntimePermissionUsageInfo(java.lang.CharSequence, int);
+    method public int describeContents();
+    method public java.lang.CharSequence getName();
+    method public int getAppAccessCount();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionUsageInfo> CREATOR;
+  }
 }
 
 package android.permissionpresenterservice {
@@ -6202,6 +6210,40 @@
     field public static final int WWAN = 1; // 0x1
   }
 
+  public class CallAttributes implements android.os.Parcelable {
+    ctor public CallAttributes(android.telephony.PreciseCallState, int, android.telephony.CallQuality);
+    method public int describeContents();
+    method public android.telephony.CallQuality getCallQuality();
+    method public int getNetworkType();
+    method public android.telephony.PreciseCallState getPreciseCallState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
+  }
+
+  public final class CallQuality implements android.os.Parcelable {
+    ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+    method public int describeContents();
+    method public int getAverageRelativeJitter();
+    method public int getAverageRoundTripTime();
+    method public int getCallDuration();
+    method public int getCodecType();
+    method public int getDownlinkCallQualityLevel();
+    method public int getMaxRelativeJitter();
+    method public int getNumRtpPacketsNotReceived();
+    method public int getNumRtpPacketsReceived();
+    method public int getNumRtpPacketsTransmitted();
+    method public int getNumRtpPacketsTransmittedLost();
+    method public int getUplinkCallQualityLevel();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CALL_QUALITY_BAD = 4; // 0x4
+    field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
+    field public static final int CALL_QUALITY_FAIR = 2; // 0x2
+    field public static final int CALL_QUALITY_GOOD = 1; // 0x1
+    field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5
+    field public static final int CALL_QUALITY_POOR = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
+  }
+
   public class CarrierConfigManager {
     method public static android.os.PersistableBundle getDefaultConfig();
     method public void overrideConfig(int, android.os.PersistableBundle);
@@ -6467,12 +6509,14 @@
   }
 
   public class PhoneStateListener {
+    method public void onCallAttributesChanged(android.telephony.CallAttributes);
     method public void onCallDisconnectCauseChanged(int, int);
     method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
     method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
+    field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
     field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
     field public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
diff --git a/api/test-current.txt b/api/test-current.txt
index 55705b6..6d40e69 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1252,6 +1252,71 @@
 
 }
 
+package android.service.autofill.augmented {
+
+  public abstract class AugmentedAutofillService extends android.app.Service {
+    ctor public AugmentedAutofillService();
+    method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method protected void dump(java.io.PrintWriter, java.lang.String[]);
+    method public void onFillRequest(android.service.autofill.augmented.FillRequest, android.os.CancellationSignal, android.service.autofill.augmented.FillController, android.service.autofill.augmented.FillCallback);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
+  }
+
+  public final class FillCallback {
+    method public void onSuccess(android.service.autofill.augmented.FillResponse);
+  }
+
+  public final class FillController {
+    method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+  }
+
+  public final class FillRequest {
+    method public android.content.ComponentName getActivityComponent();
+    method public android.view.autofill.AutofillId getFocusedId();
+    method public android.view.autofill.AutofillValue getFocusedValue();
+    method public android.service.autofill.augmented.PresentationParams getPresentationParams();
+    method public int getTaskId();
+  }
+
+  public final class FillResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.augmented.FillResponse> CREATOR;
+  }
+
+  public static final class FillResponse.Builder {
+    ctor public FillResponse.Builder();
+    method public android.service.autofill.augmented.FillResponse build();
+    method public android.service.autofill.augmented.FillResponse.Builder setFillWindow(android.service.autofill.augmented.FillWindow);
+    method public android.service.autofill.augmented.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+  }
+
+  public final class FillWindow implements java.lang.AutoCloseable {
+    ctor public FillWindow();
+    method public void destroy();
+    method public boolean update(android.service.autofill.augmented.PresentationParams.Area, android.view.View, long);
+    field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
+  }
+
+  public abstract class PresentationParams {
+    method public int getFlags();
+    method public android.service.autofill.augmented.PresentationParams.Area getFullArea();
+    method public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
+    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+    field public static final int FLAG_HOST_IME = 16; // 0x10
+    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+  }
+
+  public static abstract class PresentationParams.Area {
+    method public android.graphics.Rect getBounds();
+    method public android.service.autofill.augmented.PresentationParams.Area getSubArea(android.graphics.Rect);
+  }
+
+}
+
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
@@ -1853,6 +1918,10 @@
     ctor public AutofillId(android.view.autofill.AutofillId, int);
   }
 
+  public final class AutofillManager {
+    field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0
+  }
+
 }
 
 package android.view.inputmethod {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 347670d..98c5a0fb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1023,7 +1023,9 @@
      *
      * @return The content capture manager
      */
-    @NonNull private ContentCaptureManager getContentCaptureManager() {
+    @Nullable private ContentCaptureManager getContentCaptureManager() {
+        // ContextCapture disabled for system apps
+        if (getApplicationInfo().isSystemApp()) return null;
         if (mContentCaptureManager == null) {
             mContentCaptureManager = getSystemService(ContentCaptureManager.class);
         }
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index b520d2c..f5d288e 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.hdmi;
 
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -27,6 +29,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -264,6 +267,10 @@
     private final boolean mHasTvDevice;
     // True if we have a logical device of type audio system hosted in the system.
     private final boolean mHasAudioSystemDevice;
+    // True if we have a logical device of type audio system hosted in the system.
+    private final boolean mHasSwitchDevice;
+    // True if it's a switch device.
+    private final boolean mIsSwitchDevice;
 
     /**
      * {@hide} - hide this constructor because it has a parameter of type IHdmiControlService,
@@ -283,6 +290,9 @@
         mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV);
         mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK);
         mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
+        mIsSwitchDevice = SystemProperties.getBoolean(
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
     }
 
     private static boolean hasDeviceType(int[] types, int type) {
@@ -319,6 +329,9 @@
                 return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null;
             case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
                 return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null;
+            case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
+                return (mHasSwitchDevice || mIsSwitchDevice)
+                    ? new HdmiSwitchClient(mService) : null;
             default:
                 return null;
         }
@@ -373,6 +386,24 @@
     }
 
     /**
+     * Gets an object that represents an HDMI-CEC logical device of type switch on the system.
+     *
+     * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also
+     * possible to communicate with other logical devices hosted in the same system if the system is
+     * configured to host more than one type of HDMI-CEC logical devices.
+     *
+     * @return {@link HdmiSwitchClient} instance. {@code null} on failure.
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    @Nullable
+    @SuppressLint("Doclava125")
+    public HdmiSwitchClient getSwitchClient() {
+        return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
+    }
+
+    /**
      * Controls standby mode of the system. It will also try to turn on/off the connected devices if
      * necessary.
      *
diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
new file mode 100644
index 0000000..1ac2973
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
@@ -0,0 +1,137 @@
+/*
+ * 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.hardware.hdmi;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * HdmiSwitchClient represents HDMI-CEC logical device of type Switch in the Android system which
+ * acts as switch.
+ *
+ * <p>HdmiSwitchClient has a CEC device type of HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ * but it is used by all Android TV devices that have multiple HDMI inputs,
+ * even if it is not a "pure" swicth and has another device type like TV or Player.
+ *
+ * @hide
+ * TODO(b/110094868): unhide and add @SystemApi for Q
+ */
+public class HdmiSwitchClient extends HdmiClient {
+
+    private static final String TAG = "HdmiSwitchClient";
+
+    /* package */ HdmiSwitchClient(IHdmiControlService service) {
+        super(service);
+    }
+
+    private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
+        return new IHdmiControlCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+                callback.onComplete(result);
+            }
+        };
+    }
+
+    /** @hide */
+    // TODO(b/110094868): unhide for Q
+    @Override
+    public int getDeviceType() {
+        return HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH;
+    }
+
+    /**
+     * Selects a CEC logical device to be a new active source.
+     *
+     * @param logicalAddress logical address of the device to select
+     * @param callback callback to get the result with
+     * @throws {@link IllegalArgumentException} if the {@code callback} is null
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null.");
+        }
+        try {
+            mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to select device: ", e);
+        }
+    }
+
+    /**
+     * Selects a HDMI port to be a new route path.
+     *
+     * @param portId HDMI port to select
+     * @param callback callback to get the result with
+     * @throws {@link IllegalArgumentException} if the {@code callback} is null
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public void portSelect(int portId, @NonNull SelectCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Callback must not be null");
+        }
+        try {
+            mService.portSelect(portId, getCallbackWrapper(callback));
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to select port: ", e);
+        }
+    }
+
+    /**
+     * Returns all the CEC devices connected to the device.
+     *
+     * <p>This only applies to device with multiple HDMI inputs
+     *
+     * @return list of {@link HdmiDeviceInfo} for connected CEC devices. Empty list is returned if
+     * there is none.
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public List<HdmiDeviceInfo> getDeviceList() {
+        try {
+            return mService.getDeviceList();
+        } catch (RemoteException e) {
+            Log.e("TAG", "Failed to call getDeviceList():", e);
+            return Collections.<HdmiDeviceInfo>emptyList();
+        }
+    }
+
+    /**
+     * Callback interface used to get the result of {@link #deviceSelect} or {@link #portSelect}.
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public interface SelectCallback {
+
+        /**
+         * Called when the operation is finished.
+         *
+         * @param result the result value of {@link #deviceSelect} or {@link #portSelect}.
+         */
+        void onComplete(int result);
+    }
+}
diff --git a/core/java/android/os/ParcelableException.aidl b/core/java/android/os/ParcelableException.aidl
new file mode 100644
index 0000000..d214922
--- /dev/null
+++ b/core/java/android/os/ParcelableException.aidl
@@ -0,0 +1,18 @@
+/* Copyright 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.os;
+
+parcelable ParcelableException;
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 0e18b44..7a7bd83 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -31,4 +31,5 @@
     void revokeRuntimePermission(String packageName, String permissionName);
     void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
             boolean countSystem, in RemoteCallback callback);
+    void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback);
 }
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index d7332ae..0865b62 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -18,6 +18,7 @@
 
 import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
 
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkNotNull;
 
@@ -146,6 +147,20 @@
         void onCountPermissionApps(int numApps);
     }
 
+    /**
+     * Callback for delivering the result of {@link #getPermissionUsages}.
+     *
+     * @hide
+     */
+    public interface OnPermissionUsageResultCallback {
+        /**
+         * The result for {@link #getPermissionUsages}.
+         *
+         * @param users The users list.
+         */
+        void onPermissionUsageResult(@NonNull List<RuntimePermissionUsageInfo> users);
+    }
+
     private final @NonNull Context mContext;
 
     /**
@@ -264,6 +279,28 @@
     }
 
     /**
+     * Count how many apps have used permissions.
+     *
+     * @param countSystem Also count system apps
+     * @param numMillis The number of milliseconds in the past to check for uses
+     * @param executor Executor on which to invoke the callback
+     * @param callback Callback to receive the result
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    public void getPermissionUsages(boolean countSystem, long numMillis,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPermissionUsageResultCallback callback) {
+        checkArgumentNonnegative(numMillis);
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        sRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(sRemoteService,
+                countSystem, numMillis, executor, callback));
+    }
+
+    /**
      * A connection to the remote service
      */
     static final class RemoteService extends
@@ -326,6 +363,7 @@
         private final boolean mDoDryRun;
         private final int mReason;
         private final @NonNull String mCallingPackage;
+        private final @NonNull Executor mExecutor;
         private final @NonNull OnRevokeRuntimePermissionsCallback mCallback;
 
         private final @NonNull RemoteCallback mRemoteCallback;
@@ -341,6 +379,7 @@
             mDoDryRun = doDryRun;
             mReason = reason;
             mCallingPackage = callingPackage;
+            mExecutor = executor;
             mCallback = callback;
 
             mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
@@ -375,7 +414,13 @@
 
         @Override
         protected void onTimeout(RemoteService remoteService) {
-            mCallback.onRevokeRuntimePermissions(Collections.emptyMap());
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(
+                        () -> mCallback.onRevokeRuntimePermissions(Collections.emptyMap()));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
@@ -521,4 +566,61 @@
             }
         }
     }
+
+    /**
+     * Request for {@link #getPermissionUsages}
+     */
+    private static final class PendingGetPermissionUsagesRequest extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+        private final @NonNull OnPermissionUsageResultCallback mCallback;
+        private final boolean mCountSystem;
+        private final long mNumMillis;
+
+        private final @NonNull RemoteCallback mRemoteCallback;
+
+        private PendingGetPermissionUsagesRequest(@NonNull RemoteService service,
+                boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnPermissionUsageResultCallback callback) {
+            super(service);
+
+            mCountSystem = countSystem;
+            mNumMillis = numMillis;
+            mCallback = callback;
+
+            mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    final List<RuntimePermissionUsageInfo> reportedUsers;
+                    List<RuntimePermissionUsageInfo> users = null;
+                    if (result != null) {
+                        users = result.getParcelableArrayList(KEY_RESULT);
+                    } else {
+                        users = Collections.emptyList();
+                    }
+                    reportedUsers = users;
+
+                    callback.onPermissionUsageResult(reportedUsers);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+
+                    finish();
+                }
+            }), null);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mCallback.onPermissionUsageResult(Collections.emptyList());
+        }
+
+        @Override
+        public void run() {
+            try {
+                getService().getServiceInterface().getPermissionUsages(mCountSystem, mNumMillis,
+                        mRemoteCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error counting permission users", e);
+            }
+        }
+    }
 }
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index f621737..75d61e6 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -17,6 +17,7 @@
 package android.permission;
 
 import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -112,6 +113,17 @@
     public abstract int onCountPermissionApps(@NonNull List<String> permissionNames,
             boolean countOnlyGranted, boolean countSystem);
 
+    /**
+     * Count how many apps have used permissions.
+     *
+     * @param countSystem Also count system apps
+     * @param numMillis The number of milliseconds in the past to check for uses
+     *
+     * @return descriptions of the users of permissions
+     */
+    public abstract @NonNull List<RuntimePermissionUsageInfo>
+            onPermissionUsageResult(boolean countSystem, long numMillis);
+
     @Override
     public final IBinder onBind(Intent intent) {
         return new IPermissionController.Stub() {
@@ -187,6 +199,20 @@
                                 PermissionControllerService.this, permissionNames, countOnlyGranted,
                                 countSystem, callback));
             }
+
+            @Override
+            public void getPermissionUsages(boolean countSystem, long numMillis,
+                    RemoteCallback callback) {
+                checkArgumentNonnegative(numMillis);
+                checkNotNull(callback, "callback");
+
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
+                mHandler.sendMessage(
+                        obtainMessage(PermissionControllerService::getPermissionUsages,
+                                PermissionControllerService.this, countSystem, numMillis,
+                                callback));
+            }
         };
     }
 
@@ -230,4 +256,17 @@
         result.putInt(PermissionControllerManager.KEY_RESULT, numApps);
         callback.sendResult(result);
     }
+
+    private void getPermissionUsages(boolean countSystem, long numMillis,
+            @NonNull RemoteCallback callback) {
+        List<RuntimePermissionUsageInfo> users =
+                onPermissionUsageResult(countSystem, numMillis);
+        if (users != null && !users.isEmpty()) {
+            Bundle result = new Bundle();
+            result.putParcelableList(PermissionControllerManager.KEY_RESULT, users);
+            callback.sendResult(result);
+        } else {
+            callback.sendResult(null);
+        }
+    }
 }
diff --git a/core/java/android/permission/RuntimePermissionUsageInfo.aidl b/core/java/android/permission/RuntimePermissionUsageInfo.aidl
new file mode 100644
index 0000000..88820dd
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionUsageInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.permission;
+
+parcelable RuntimePermissionUsageInfo;
\ No newline at end of file
diff --git a/core/java/android/permission/RuntimePermissionUsageInfo.java b/core/java/android/permission/RuntimePermissionUsageInfo.java
new file mode 100644
index 0000000..af1a1be
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionUsageInfo.java
@@ -0,0 +1,96 @@
+/*
+ * 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.permission;
+
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is used. A single runtime permission presented to the user may
+ * correspond to multiple platform defined permissions, e.g. the
+ * location permission may control both the coarse and fine platform
+ * permissions.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RuntimePermissionUsageInfo implements Parcelable {
+    private final @NonNull CharSequence mName;
+    private final int mNumUsers;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name The permission group name.
+     * @param numUsers The number of apps that have used this permission.
+     */
+    public RuntimePermissionUsageInfo(@NonNull CharSequence name, int numUsers) {
+        checkNotNull(name);
+        checkArgumentNonnegative(numUsers);
+
+        mName = name;
+        mNumUsers = numUsers;
+    }
+
+    private RuntimePermissionUsageInfo(Parcel parcel) {
+        this(parcel.readCharSequence(), parcel.readInt());
+    }
+
+    /**
+     * @return The number of apps that accessed this permission
+     */
+    public int getAppAccessCount() {
+        return mNumUsers;
+    }
+
+    /**
+     * Gets the permission group name.
+     *
+     * @return The name.
+     */
+    public @NonNull CharSequence getName() {
+        return mName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeCharSequence(mName);
+        parcel.writeInt(mNumUsers);
+    }
+
+    public static final Creator<RuntimePermissionUsageInfo> CREATOR =
+            new Creator<RuntimePermissionUsageInfo>() {
+        public RuntimePermissionUsageInfo createFromParcel(Parcel source) {
+            return new RuntimePermissionUsageInfo(source);
+        }
+
+        public RuntimePermissionUsageInfo[] newArray(int size) {
+            return new RuntimePermissionUsageInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index a9f4034..0116961 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -58,6 +59,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+// in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class AugmentedAutofillService extends Service {
 
     private static final String TAG = AugmentedAutofillService.class.getSimpleName();
@@ -268,7 +272,6 @@
          *
          * <p>Used to make sure the SmartSuggestionsParams is updated when a new fields is focused.
          */
-        // TODO(b/111330312): might not be needed when using IME
         @GuardedBy("mLock")
         private AutofillId mLastShownId;
 
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 620ec59..bfb4aad 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
 
@@ -29,6 +30,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillCallback {
 
     private static final String TAG = FillCallback.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index e65cf47..d7bc893 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -19,6 +19,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
@@ -36,6 +37,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillController {
     private static final String TAG = "FillController";
 
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index 57d2cc8..dad5067 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.view.autofill.AutofillId;
@@ -29,6 +30,9 @@
  */
 @SystemApi
 // TODO(b/111330312): pass a requestId and/or sessionId
+@TestApi
+// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+// in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillRequest {
 
     final AutofillProxy mProxy;
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 1ecfab4..5285132 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.AutofillId;
@@ -30,6 +31,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillResponse implements Parcelable {
 
     private final FillWindow mFillWindow;
@@ -50,6 +54,9 @@
      * @hide
      */
     @SystemApi
+    @TestApi
+    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+    //in the same package as the test, and that module is compiled with SDK=test_current
     public static final class Builder {
 
         private FillWindow mFillWindow;
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index bad7ddd..33b88e42 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -20,6 +20,7 @@
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Dialog;
 import android.graphics.Rect;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
@@ -61,6 +62,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillWindow implements AutoCloseable {
     private static final String TAG = "FillWindow";
 
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index 0124ecc..b60064e 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.DebugUtils;
@@ -48,6 +49,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class PresentationParams {
 
     /**
@@ -147,8 +151,11 @@
      * Area associated with a {@link PresentationParams Smart Suggestions} provider.
      *
      * @hide
-     * */
+     */
     @SystemApi
+    @TestApi
+    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+    //in the same package as the test, and that module is compiled with SDK=test_current
     public abstract static class Area {
 
         /** @hide */
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index cb1d89c..9c935af 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -15,6 +15,7 @@
  */
 package android.view.autofill;
 
+import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -25,33 +26,47 @@
  */
 public final class AutofillId implements Parcelable {
 
+    /** @hide */
+    public static final int NO_SESSION = 0;
+
+    private static final int FLAG_IS_VIRTUAL = 0x1;
+    private static final int FLAG_HAS_SESSION = 0x2;
+
     private final int mViewId;
-    private final boolean mVirtual;
+    private final int mFlags;
     private final int mVirtualId;
+    private final int mSessionId;
 
     /** @hide */
     @TestApi
     public AutofillId(int id) {
-        mVirtual = false;
-        mViewId = id;
-        mVirtualId = View.NO_ID;
+        this(/* flags= */ 0, id, View.NO_ID, NO_SESSION);
     }
 
     /** @hide */
     @TestApi
-    public AutofillId(AutofillId parent, int virtualChildId) {
-        mVirtual = true;
-        mViewId = parent.mViewId;
-        mVirtualId = virtualChildId;
+    public AutofillId(@NonNull AutofillId parent, int virtualChildId) {
+        this(FLAG_IS_VIRTUAL, parent.mViewId, virtualChildId, NO_SESSION);
     }
 
     /** @hide */
     public AutofillId(int parentId, int virtualChildId) {
-        mVirtual = true;
+        this(FLAG_IS_VIRTUAL, parentId, virtualChildId, NO_SESSION);
+    }
+
+    /** @hide */
+    public AutofillId(@NonNull AutofillId parent, int virtualChildId, int sessionId) {
+        this(FLAG_IS_VIRTUAL | FLAG_HAS_SESSION, parent.mViewId, virtualChildId, sessionId);
+    }
+
+    private AutofillId(int flags, int parentId, int virtualChildId, int sessionId) {
+        mFlags = flags;
         mViewId = parentId;
         mVirtualId = virtualChildId;
+        mSessionId = sessionId;
     }
 
+
     /** @hide */
     public int getViewId() {
         return mViewId;
@@ -64,7 +79,16 @@
 
     /** @hide */
     public boolean isVirtual() {
-        return mVirtual;
+        return (mFlags & FLAG_IS_VIRTUAL) != 0;
+    }
+
+    private boolean hasSession() {
+        return (mFlags & FLAG_HAS_SESSION) != 0;
+    }
+
+    /** @hide */
+    public int getSessionId() {
+        return mSessionId;
     }
 
     /////////////////////////////////
@@ -77,6 +101,7 @@
         int result = 1;
         result = prime * result + mViewId;
         result = prime * result + mVirtualId;
+        result = prime * result + mSessionId;
         return result;
     }
 
@@ -88,15 +113,19 @@
         final AutofillId other = (AutofillId) obj;
         if (mViewId != other.mViewId) return false;
         if (mVirtualId != other.mVirtualId) return false;
+        if (mSessionId != other.mSessionId) return false;
         return true;
     }
 
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder().append(mViewId);
-        if (mVirtual) {
+        if (isVirtual()) {
             builder.append(':').append(mVirtualId);
         }
+        if (hasSession()) {
+            builder.append('@').append(mSessionId);
+        }
         return builder.toString();
     }
 
@@ -108,21 +137,24 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mViewId);
-        parcel.writeInt(mVirtual ? 1 : 0);
-        parcel.writeInt(mVirtualId);
-    }
-
-    private AutofillId(Parcel parcel) {
-        mViewId = parcel.readInt();
-        mVirtual = parcel.readInt() == 1;
-        mVirtualId = parcel.readInt();
+        parcel.writeInt(mFlags);
+        if (isVirtual()) {
+            parcel.writeInt(mVirtualId);
+        }
+        if (hasSession()) {
+            parcel.writeInt(mSessionId);
+        }
     }
 
     public static final Parcelable.Creator<AutofillId> CREATOR =
             new Parcelable.Creator<AutofillId>() {
         @Override
         public AutofillId createFromParcel(Parcel source) {
-            return new AutofillId(source);
+            final int viewId = source.readInt();
+            final int flags = source.readInt();
+            final int virtualId = (flags & FLAG_IS_VIRTUAL) != 0 ? source.readInt() : View.NO_ID;
+            final int sessionId = (flags & FLAG_HAS_SESSION) != 0 ? source.readInt() : NO_SESSION;
+            return new AutofillId(flags, viewId, virtualId, sessionId);
         }
 
         @Override
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 90ccc25..93941d0 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -329,6 +330,12 @@
     private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
 
     /**
+     * @hide
+     */
+    @TestApi
+    public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+    /**
      * Makes an authentication id from a request id and a dataset id.
      *
      * @param requestId The request id.
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 6890beaf..d9a8416 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -28,6 +28,7 @@
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import dalvik.system.CloseGuard;
@@ -107,7 +108,7 @@
 
     /** @hide */
     @Nullable
-    protected final String mId = UUID.randomUUID().toString();
+    protected final String mId;
 
     private int mState = STATE_UNKNOWN;
 
@@ -123,6 +124,13 @@
 
     /** @hide */
     protected ContentCaptureSession() {
+        this(UUID.randomUUID().toString());
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public ContentCaptureSession(@NonNull String id) {
+        mId = Preconditions.checkNotNull(id);
         mCloseGuard.open("destroy");
     }
 
@@ -140,6 +148,13 @@
         return mContentCaptureSessionId;
     }
 
+    /** @hide */
+    @VisibleForTesting
+    public int getIdAsInt() {
+        // TODO(b/121197119): use sessionId instead of hashcode once it's changed to int
+        return mId.hashCode();
+    }
+
     /**
      * Creates a new {@link ContentCaptureSession}.
      *
@@ -315,9 +330,7 @@
     public @NonNull AutofillId newAutofillId(@NonNull AutofillId parentId, int virtualChildId) {
         Preconditions.checkNotNull(parentId);
         Preconditions.checkArgument(!parentId.isVirtual(), "virtual ids cannot have children");
-        // TODO(b/121197119): we need to add the session id to the AutofillId to make them unique
-        // per session
-        return new AutofillId(parentId, virtualChildId);
+        return new AutofillId(parentId, virtualChildId, getIdAsInt());
     }
 
     /**
@@ -333,8 +346,7 @@
     @NonNull
     public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
             int virtualId) {
-        // TODO(b/121197119): use the constructor that takes a session id / assert on unit test.
-        return new ViewNode.ViewStructureImpl(parentId, virtualId);
+        return new ViewNode.ViewStructureImpl(parentId, virtualId, getIdAsInt());
     }
 
     boolean isContentCaptureEnabled() {
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index b7a486a..ddfecb0 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -672,9 +672,9 @@
         }
 
         @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
-        public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId) {
+        public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId, int sessionId) {
             mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
-            mNode.mAutofillId = new AutofillId(parentId, virtualId);
+            mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId);
         }
 
         @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index e8ac223..5945958 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -213,6 +213,7 @@
         }
         mService = null;
         mServiceDied = true;
+        cancelScheduledUnbind();
         @SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning
         final S castService = (S) this;
         mVultureCallback.onServiceDied(castService);
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index a319d83..b529bbe 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -30,6 +30,7 @@
     public static final String CONTROL_PRIVAPP_PERMISSIONS =
             SystemProperties.get("ro.control_privapp_permissions");
 
+    // ------ ro.hdmi.* -------- //
     /**
      * Property to indicate if a CEC audio device should forward volume keys when system audio
      * mode is off.
@@ -38,6 +39,14 @@
             SystemProperties.getBoolean(
                     "ro.hdmi.cec_audio_device_forward_volume_keys_system_audio_mode_off", false);
 
+    /**
+     * Property to indicate if the current device is a cec switch device.
+     *
+     * <p> Default is false.
+     */
+    public static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH =
+            "ro.hdmi.property_is_device_hdmi_cec_switch";
+
     // ------ ro.config.* -------- //
     public static final boolean CONFIG_AVOID_GFX_ACCEL =
             SystemProperties.getBoolean("ro.config.avoid_gfx_accel", false);
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
new file mode 100644
index 0000000..7619af2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.view.autofill;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.os.Parcel;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AutofillIdTest {
+
+    @Test
+    public void testNonVirtual() {
+        final AutofillId id = new AutofillId(42);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isFalse();
+        assertThat(id.getVirtualChildId()).isEqualTo(View.NO_ID);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isFalse();
+        assertThat(clone.getVirtualChildId()).isEqualTo(View.NO_ID);
+    }
+
+    @Test
+    public void testVirtual() {
+        final AutofillId id = new AutofillId(42, 108);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+    }
+
+    @Test
+    public void testVirtual_parentObjectConstructor() {
+        assertThrows(NullPointerException.class, () -> new AutofillId(null, 108));
+
+        final AutofillId id = new AutofillId(new AutofillId(42), 108);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+    }
+
+    @Test
+    public void testVirtual_withSession() {
+        final AutofillId id = new AutofillId(new AutofillId(42), 108, 666);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+        assertThat(id.getSessionId()).isEqualTo(666);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+        assertThat(clone.getSessionId()).isEqualTo(666);
+    }
+
+    @Test
+    public void testEqualsHashCode() {
+        final AutofillId realId = new AutofillId(42);
+        final AutofillId realIdSame = new AutofillId(42);
+        assertThat(realId).isEqualTo(realIdSame);
+        assertThat(realIdSame).isEqualTo(realId);
+        assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode());
+
+        final AutofillId realIdDifferent = new AutofillId(108);
+        assertThat(realId).isNotEqualTo(realIdDifferent);
+        assertThat(realIdDifferent).isNotEqualTo(realId);
+
+        final AutofillId virtualId = new AutofillId(42, 1);
+        final AutofillId virtualIdSame = new AutofillId(42, 1);
+        assertThat(virtualId).isEqualTo(virtualIdSame);
+        assertThat(virtualIdSame).isEqualTo(virtualId);
+        assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode());
+        assertThat(virtualId).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualId);
+
+        final AutofillId virtualIdDifferentChild = new AutofillId(42, 2);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualIdDifferentChild);
+
+        final AutofillId virtualIdDifferentParent = new AutofillId(108, 1);
+        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent);
+        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent);
+
+        final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+        assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession);
+        assertThat(virtualIdDifferentSession).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualIdDifferentSession);
+
+        final AutofillId sameVirtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+        assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession);
+        assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession);
+        assertThat(sameVirtualIdDifferentSession.hashCode())
+                .isEqualTo(virtualIdDifferentSession.hashCode());
+    }
+
+    private AutofillId cloneThroughParcel(AutofillId id) {
+        Parcel parcel = Parcel.obtain();
+
+        try {
+            // Write to parcel
+            parcel.setDataPosition(0); // Sanity / paranoid check
+            id.writeToParcel(parcel, 0);
+
+            // Read from parcel
+            parcel.setDataPosition(0);
+            AutofillId clone = AutofillId.CREATOR.createFromParcel(parcel);
+            assertThat(clone).isNotNull();
+            return clone;
+        } finally {
+            parcel.recycle();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 59f3a4c..73cceae 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -19,11 +19,14 @@
 
 import static org.testng.Assert.assertThrows;
 
+import android.view.View;
+import android.view.ViewStructure;
 import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Spy;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
 /**
@@ -35,34 +38,104 @@
 @RunWith(MockitoJUnitRunner.class)
 public class ContentCaptureSessionTest {
 
-    /**
-     * Uses a spy as ContentCaptureSession is abstract but (so far) we're testing its concrete
-     * methods.
-     */
-    @Spy
-    private ContentCaptureSession mMockSession;
+    private ContentCaptureSession mSession1 = new MyContentCaptureSession("111");
+
+    private ContentCaptureSession mSession2 = new MyContentCaptureSession("2222");
+
+    @Mock
+    private View mMockView;
 
     @Test
     public void testNewAutofillId_invalid() {
-        assertThrows(NullPointerException.class, () -> mMockSession.newAutofillId(null, 42));
+        assertThrows(NullPointerException.class, () -> mSession1.newAutofillId(null, 42));
         assertThrows(IllegalArgumentException.class,
-                () -> mMockSession.newAutofillId(new AutofillId(42, 42), 42));
+                () -> mSession1.newAutofillId(new AutofillId(42, 42), 42));
     }
 
     @Test
     public void testNewAutofillId_valid() {
         final AutofillId parentId = new AutofillId(42);
-        final AutofillId childId = mMockSession.newAutofillId(parentId, 108);
+        final AutofillId childId = mSession1.newAutofillId(parentId, 108);
         assertThat(childId.getViewId()).isEqualTo(42);
         assertThat(childId.getVirtualChildId()).isEqualTo(108);
-        // TODO(b/121197119): assert session id
+        assertThat(childId.getSessionId()).isEqualTo(mSession1.getIdAsInt());
+    }
+
+    @Test
+    public void testNewAutofillId_differentSessions() {
+        assertThat(mSession1.getIdAsInt()).isNotSameAs(mSession2.getIdAsInt()); //sanity check
+        final AutofillId parentId = new AutofillId(42);
+        final AutofillId childId1 = mSession1.newAutofillId(parentId, 108);
+        final AutofillId childId2 = mSession2.newAutofillId(parentId, 108);
+        assertThat(childId1).isNotEqualTo(childId2);
+        assertThat(childId2).isNotEqualTo(childId1);
     }
 
     @Test
     public void testNotifyXXX_null() {
-        assertThrows(NullPointerException.class, () -> mMockSession.notifyViewAppeared(null));
-        assertThrows(NullPointerException.class, () -> mMockSession.notifyViewDisappeared(null));
+        assertThrows(NullPointerException.class, () -> mSession1.notifyViewAppeared(null));
+        assertThrows(NullPointerException.class, () -> mSession1.notifyViewDisappeared(null));
         assertThrows(NullPointerException.class,
-                () -> mMockSession.notifyViewTextChanged(null, "whatever", 0));
+                () -> mSession1.notifyViewTextChanged(null, "whatever", 0));
+    }
+
+    @Test
+    public void testNewViewStructure() {
+        assertThat(mMockView.getAutofillId()).isNotNull(); // sanity check
+        final ViewStructure structure = mSession1.newViewStructure(mMockView);
+        assertThat(structure).isNotNull();
+        assertThat(structure.getAutofillId()).isEqualTo(mMockView.getAutofillId());
+    }
+
+    @Test
+    public void testNewVirtualViewStructure() {
+        final AutofillId parentId = new AutofillId(42);
+        final ViewStructure structure = mSession1.newVirtualViewStructure(parentId, 108);
+        assertThat(structure).isNotNull();
+        final AutofillId childId = mSession1.newAutofillId(parentId, 108);
+        assertThat(structure.getAutofillId()).isEqualTo(childId);
+    }
+
+    // Cannot use @Spy because we need to pass the session id on constructor
+    private class MyContentCaptureSession extends ContentCaptureSession {
+
+        private MyContentCaptureSession(String id) {
+            super(id);
+        }
+
+        @Override
+        MainContentCaptureSession getMainCaptureSession() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        ContentCaptureSession newChild(ContentCaptureContext context) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void flush() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void onDestroy() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewAppeared(ViewStructureImpl node) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewDisappeared(AutofillId id) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewTextChanged(AutofillId id, CharSequence text, int flags) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
index 995946b..bbfe01c 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
@@ -110,10 +110,10 @@
     @Test
     public void testAutofillIdMethods_explicitIdsConstructor() {
         AutofillId initialParentId = new AutofillId(42);
-        ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108);
+        ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108, 666);
         ViewNode node = structure.getNode();
 
-        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108));
+        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108, 666));
         assertThat(node.getParentAutofillId()).isEqualTo(initialParentId);
 
         AutofillId newChildId = new AutofillId(108);
diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk
index 680ebeb..5630749 100644
--- a/core/tests/overlaytests/device/Android.mk
+++ b/core/tests/overlaytests/device/Android.mk
@@ -19,7 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_PACKAGE_NAME := OverlayDeviceTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_TARGET_REQUIRED_MODULES := \
     OverlayDeviceTests_AppOverlayOne \
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index d14fdf5..4881636 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -23,7 +23,7 @@
         <uses-library android:name="android.test.runner"/>
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.overlaytest"
         android:label="Runtime resource overlay tests" />
 </manifest>
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 5a86885..91fcdbb 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -25,10 +25,11 @@
 import android.content.res.XmlResourceParser;
 import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
 import android.util.AttributeSet;
 import android.util.Xml;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index f35e511..cd3ed9d 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index 037449f..c0d4281 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index f657b5c..33c7b25 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 25dabad..0bffa38 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -39,6 +39,9 @@
     name: "privapp-permissions-platform.xml",
     sub_dir: "permissions",
     src: "privapp-permissions-platform.xml",
+    required: [
+        "privapp_whitelist_com.android.settings.intelligence",
+    ]
 }
 
 prebuilt_etc {
@@ -50,6 +53,13 @@
 }
 
 prebuilt_etc {
+    name: "privapp_whitelist_com.android.settings.intelligence",
+    sub_dir: "permissions",
+    src: "com.android.settings.intelligence.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "privapp_whitelist_com.android.systemui",
     product_specific: true,
     sub_dir: "permissions",
diff --git a/data/etc/com.android.settings.intelligence.xml b/data/etc/com.android.settings.intelligence.xml
new file mode 100644
index 0000000..6ca30c1
--- /dev/null
+++ b/data/etc/com.android.settings.intelligence.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.settings.intelligence">
+        <permission name="android.permission.MANAGE_FINGERPRINT"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+        <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index fbe613d..393a2a6 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -280,13 +280,6 @@
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.settings.intelligence">
-        <permission name="android.permission.MANAGE_FINGERPRINT"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
-        <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.sharedstoragebackup">
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 562a3b2..1661905 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -23,6 +23,7 @@
 #include "FileBlobCache.h"
 #include "Properties.h"
 #include "utils/TraceUtils.h"
+#include <GrContext.h>
 
 namespace android {
 namespace uirenderer {
@@ -168,6 +169,24 @@
     const void* value = data.data();
 
     BlobCache* bc = getBlobCacheLocked();
+    if (mInStoreVkPipelineInProgress) {
+        if (mOldPipelineCacheSize == -1) {
+            // Record the initial pipeline cache size stored in the file.
+            mOldPipelineCacheSize = bc->get(key.data(), keySize, nullptr, 0);
+        }
+        if (mNewPipelineCacheSize != -1 && mNewPipelineCacheSize == valueSize) {
+            // There has not been change in pipeline cache size. Stop trying to save.
+            mTryToStorePipelineCache = false;
+            return;
+        }
+        mNewPipelineCacheSize = valueSize;
+    } else {
+        mCacheDirty = true;
+        // If there are new shaders compiled, we probably have new pipeline state too.
+        // Store pipeline cache on the next flush.
+        mNewPipelineCacheSize = -1;
+        mTryToStorePipelineCache = true;
+    }
     bc->set(key.data(), keySize, value, valueSize);
 
     if (!mSavePending && mDeferredSaveDelay > 0) {
@@ -175,12 +194,31 @@
         std::thread deferredSaveThread([this]() {
             sleep(mDeferredSaveDelay);
             std::lock_guard<std::mutex> lock(mMutex);
-            saveToDiskLocked();
+            // Store file on disk if there a new shader or Vulkan pipeline cache size changed.
+            if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
+                saveToDiskLocked();
+                mOldPipelineCacheSize = mNewPipelineCacheSize;
+                mTryToStorePipelineCache = false;
+                mCacheDirty = false;
+            }
         });
         deferredSaveThread.detach();
     }
 }
 
+void ShaderCache::onVkFrameFlushed(GrContext* context) {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+
+        if (!mInitialized || !mTryToStorePipelineCache) {
+            return;
+        }
+    }
+    mInStoreVkPipelineInProgress = true;
+    context->storeVkPipelineCacheData();
+    mInStoreVkPipelineInProgress = false;
+}
+
 } /* namespace skiapipeline */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index d41aadb..0898017 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -75,6 +75,13 @@
      */
     void store(const SkData& key, const SkData& data) override;
 
+    /**
+     * "onVkFrameFlushed" tries to store Vulkan pipeline cache state.
+     * Pipeline cache is saved on disk only if the size of the data has changed or there was
+     * a new shader compiled.
+     */
+    void onVkFrameFlushed(GrContext* context);
+
 private:
     // Creation and (the lack of) destruction is handled internally.
     ShaderCache();
@@ -167,6 +174,33 @@
     mutable std::mutex mMutex;
 
     /**
+     *  If set to "true", the next call to onVkFrameFlushed, will invoke
+     * GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk.
+     */
+    bool mTryToStorePipelineCache = true;
+
+    /**
+     * This flag is used by "ShaderCache::store" to distinguish between shader data and
+     * Vulkan pipeline data.
+     */
+    bool mInStoreVkPipelineInProgress = false;
+
+    /**
+     *  "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used
+     *  to prevent unnecessary disk writes, if the pipeline cache size has not changed.
+     */
+    size_t mNewPipelineCacheSize = -1;
+    /**
+     *  "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk.
+     */
+    size_t mOldPipelineCacheSize = -1;
+
+    /**
+     *  "mCacheDirty" is true when there is new shader cache data, which is not saved to disk.
+     */
+    bool mCacheDirty = false;
+
+    /**
      * "sCache" is the singleton ShaderCache object.
      */
     static ShaderCache sCache;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 1d3a244..b9aae98 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -23,6 +23,7 @@
 #include "VkInteropFunctorDrawable.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/Frame.h"
+#include "ShaderCache.h"
 
 #include <SkSurface.h>
 #include <SkTypes.h>
@@ -73,6 +74,7 @@
     }
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
     renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
+    ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext());
     layerUpdateQueue->clear();
 
     // Draw visual debugging features
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 838b88b..499d493 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -286,6 +286,9 @@
             if (!isForCurrentUser(sbn)) {
                 return;
             }
+
+            mAgingHelper.onNotificationRemoved(sbn.getKey());
+
             boolean updatedImpressions = false;
             String channelId = mLiveNotifications.remove(sbn.getKey()).getChannel().getId();
             String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 4f81daf..af5a24f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -332,9 +332,9 @@
         ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
         bssid = config.BSSID;
         security = getSecurity(config);
-        updateKey();
         networkId = config.networkId;
         mConfig = config;
+        updateKey();
     }
 
     /** Updates {@link #mKey} and should only called upon object creation/initialization. */
@@ -343,7 +343,9 @@
 
         StringBuilder builder = new StringBuilder();
 
-        if (TextUtils.isEmpty(getSsidStr())) {
+        if (isPasspoint()) {
+            builder.append(mConfig.FQDN);
+        } else if (TextUtils.isEmpty(getSsidStr())) {
             builder.append(getBssid());
         } else {
             builder.append(getSsidStr());
@@ -606,7 +608,9 @@
     public static String getKey(WifiConfiguration config) {
         StringBuilder builder = new StringBuilder();
 
-        if (TextUtils.isEmpty(config.SSID)) {
+        if (config.isPasspoint()) {
+            builder.append(config.FQDN);
+        } else if (TextUtils.isEmpty(config.SSID)) {
             builder.append(config.BSSID);
         } else {
             builder.append(removeDoubleQuotes(config.SSID));
@@ -621,9 +625,10 @@
     }
 
     public boolean matches(WifiConfiguration config) {
-        if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
-            return ssid.equals(removeDoubleQuotes(config.SSID)) && config.FQDN.equals(mConfig.FQDN);
+        if (config.isPasspoint()) {
+            return (isPasspoint() && config.FQDN.equals(mConfig.FQDN));
         } else {
+            // Normal non-Passpoint network
             return ssid.equals(removeDoubleQuotes(config.SSID))
                     && security == getSecurity(config)
                     && (mConfig == null || mConfig.shared == config.shared);
@@ -828,6 +833,17 @@
         return "";
     }
 
+    /**
+     * Returns the display title for the AccessPoint, such as for an AccessPointPreference's title.
+     */
+    public String getTitle() {
+        if (isPasspoint()) {
+            return mConfig.providerFriendlyName;
+        } else {
+            return getSsidStr();
+        }
+    }
+
     public String getSummary() {
         return getSettingsSummary(mConfig);
     }
@@ -1048,18 +1064,21 @@
      */
     void setScanResults(Collection<ScanResult> scanResults) {
 
-        // Validate scan results are for current AP only
-        String key = getKey();
-        for (ScanResult result : scanResults) {
-            String scanResultKey = AccessPoint.getKey(result);
-            if (!mKey.equals(scanResultKey)) {
-                throw new IllegalArgumentException(
-                        String.format("ScanResult %s\nkey of %s did not match current AP key %s",
-                                      result, scanResultKey, key));
+        // Validate scan results are for current AP only by matching SSID/BSSID
+        // Passpoint R1 networks are not bound to a specific SSID/BSSID, so skip this for passpoint.
+        if (!isPasspoint()) {
+            String key = getKey();
+            for (ScanResult result : scanResults) {
+                String scanResultKey = AccessPoint.getKey(result);
+                if (!mKey.equals(scanResultKey)) {
+                    throw new IllegalArgumentException(
+                            String.format(
+                                    "ScanResult %s\nkey of %s did not match current AP key %s",
+                                    result, scanResultKey, key));
+                }
             }
         }
 
-
         int oldLevel = getLevel();
         mScanResults.clear();
         mScanResults.addAll(scanResults);
@@ -1149,6 +1168,9 @@
 
     void update(@Nullable WifiConfiguration config) {
         mConfig = config;
+        if (mConfig != null) {
+            ssid = removeDoubleQuotes(mConfig.SSID);
+        }
         networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
         ThreadUtils.postOnMainThread(() -> {
             if (mAccessPointListener != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index db364a3..1fa7083 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -266,7 +266,7 @@
         if (savedNetworks) {
             preference.setTitle(ap.getConfigName());
         } else {
-            preference.setTitle(ap.getSsidStr());
+            preference.setTitle(ap.getTitle());
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index e47ca32..79a7240 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -45,6 +45,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.widget.Toast;
 
 import androidx.annotation.GuardedBy;
@@ -573,9 +574,52 @@
                 accessPoints.add(accessPoint);
             }
 
+            List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());
+
+            // Add a unique Passpoint R1 AccessPoint for each Passpoint profile's FQDN.
+            List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans =
+                    mWifiManager.getAllMatchingWifiConfigs(cachedScanResults);
+            Set<String> seenFQDNs = new ArraySet<>();
+            for (Pair<WifiConfiguration,
+                    Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) {
+                WifiConfiguration config = pairing.first;
+
+                // TODO: Prioritize home networks before roaming networks
+                List<ScanResult> scanResults = new ArrayList<>();
+
+                List<ScanResult> homeScans =
+                        pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK);
+                List<ScanResult> roamingScans =
+                        pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK);
+
+                if (homeScans == null) {
+                    homeScans = new ArrayList<>();
+                }
+                if (roamingScans == null) {
+                    roamingScans = new ArrayList<>();
+                }
+
+                scanResults.addAll(homeScans);
+                scanResults.addAll(roamingScans);
+
+                if (seenFQDNs.add(config.FQDN)) {
+                    int bestRssi = Integer.MIN_VALUE;
+                    for (ScanResult result : scanResults) {
+                        if (result.level >= bestRssi) {
+                            bestRssi = result.level;
+                            config.SSID = AccessPoint.convertToQuotedString(result.SSID);
+                        }
+                    }
+
+                    AccessPoint accessPoint = new AccessPoint(mContext, config);
+                    accessPoint.setScanResults(scanResults);
+                    accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
+                    accessPoints.add(accessPoint);
+                }
+            }
+
             // If there were no scan results, create an AP for the currently connected network (if
             // it exists).
-            // TODO(b/b/73076869): Add support for passpoint (ephemeral) networks
             if (accessPoints.isEmpty() && connectionConfig != null) {
                 AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
                 activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1539584..9a11b96 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -472,7 +472,8 @@
 
                 @Override
                 public void onActivityViewDestroyed(ActivityView view) {
-                    NotificationEntry entry = mExpandedBubble.getEntry();
+                    NotificationEntry entry = mExpandedBubble != null
+                            ? mExpandedBubble.getEntry() : null;
                     Log.d(TAG, "onActivityViewDestroyed(key="
                             + ((entry != null) ? entry.key : "(none)") + "): " + view);
                 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6cccd62..24fd7b9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
 import static android.util.DebugUtils.flagsToString;
+import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
@@ -101,8 +102,6 @@
 
     private static final Object sLock = AutofillManagerService.class;
 
-    private static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
-
     /**
      * IME supports Smart Suggestions.
      */
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 239a386..a8ff9b0 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -168,7 +168,7 @@
                 }
             };
 
-            // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service),
+            // TODO(b/122728762): set cancellation signal, timeout (from both mClient and service),
             // cache IAugmentedAutofillManagerClient reference, etc...
             try {
                 mClient.getAugmentedAutofillClient(receiver);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0348f2b..cf4963c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2581,7 +2581,10 @@
 
         final RemoteAugmentedAutofillService remoteService = mService
                 .getRemoteAugmentedAutofillServiceLocked();
-        if (remoteService == null) return null;
+        if (remoteService == null) {
+            if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user");
+            return null;
+        }
 
         // Define which mode will be used
         final int mode;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 12db4f3..ff378b3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -149,6 +149,9 @@
 
         if (userBackupManagerService != null) {
             userBackupManagerService.tearDownService();
+
+            KeyValueBackupJob.cancel(userId, mContext);
+            FullBackupJob.cancel(userId, mContext);
         }
     }
 
@@ -577,9 +580,9 @@
      * @return Whether ongoing work will continue. The return value here will be passed along as the
      *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
      */
-    public boolean beginFullBackup(FullBackupJob scheduledJob) {
+    public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
         UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
+                getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
 
         return userBackupManagerService != null
                 && userBackupManagerService.beginFullBackup(scheduledJob);
@@ -589,9 +592,9 @@
      * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
      * longer met for running the full backup job.
      */
-    public void endFullBackup() {
+    public void endFullBackup(@UserIdInt int userId) {
         UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
+                getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
 
         if (userBackupManagerService != null) {
             userBackupManagerService.endFullBackup();
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 5708b1c..33d21cb0 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -22,18 +22,30 @@
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 public class FullBackupJob extends JobService {
+    private static final String USER_ID_EXTRA_KEY = "userId";
+
+    @VisibleForTesting
+    static final int MIN_JOB_ID = 52418896;
+    @VisibleForTesting
+    static final int MAX_JOB_ID = 52419896;
+
     private static ComponentName sIdleService =
             new ComponentName("android", FullBackupJob.class.getName());
 
-    private static final int JOB_ID = 0x5038;
+    @GuardedBy("mParamsForUser")
+    private final SparseArray<JobParameters> mParamsForUser = new SparseArray<>();
 
-    private JobParameters mParams;
-
-    public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
+    public static void schedule(int userId, Context ctx, long minDelay,
+            BackupManagerConstants constants) {
         JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService);
+        JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId), sIdleService);
         synchronized (constants) {
             builder.setRequiresDeviceIdle(true)
                     .setRequiredNetworkType(constants.getFullBackupRequiredNetworkType())
@@ -42,14 +54,28 @@
         if (minDelay > 0) {
             builder.setMinimumLatency(minDelay);
         }
+
+        Bundle extraInfo = new Bundle();
+        extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+        builder.setTransientExtras(extraInfo);
+
         js.schedule(builder.build());
     }
 
+    public static void cancel(int userId, Context ctx) {
+        JobScheduler js = (JobScheduler) ctx.getSystemService(
+                Context.JOB_SCHEDULER_SERVICE);
+        js.cancel(getJobIdForUserId(userId));
+    }
+
     // callback from the Backup Manager Service: it's finished its work for this pass
-    public void finishBackupPass() {
-        if (mParams != null) {
-            jobFinished(mParams, false);
-            mParams = null;
+    public void finishBackupPass(int userId) {
+        synchronized (mParamsForUser) {
+            JobParameters jobParameters = mParamsForUser.get(userId);
+            if (jobParameters != null) {
+                jobFinished(jobParameters, false);
+                mParamsForUser.remove(userId);
+            }
         }
     }
 
@@ -57,19 +83,33 @@
 
     @Override
     public boolean onStartJob(JobParameters params) {
-        mParams = params;
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+        synchronized (mParamsForUser) {
+            mParamsForUser.put(userId, params);
+        }
+
         Trampoline service = BackupManagerService.getInstance();
-        return service.beginFullBackup(this);
+        return service.beginFullBackup(userId, this);
     }
 
     @Override
     public boolean onStopJob(JobParameters params) {
-        if (mParams != null) {
-            mParams = null;
-            Trampoline service = BackupManagerService.getInstance();
-            service.endFullBackup();
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+        synchronized (mParamsForUser) {
+            if (mParamsForUser.removeReturnOld(userId) == null) {
+                return false;
+            }
         }
+
+        Trampoline service = BackupManagerService.getInstance();
+        service.endFullBackup(userId);
+
         return false;
     }
 
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/JobIdManager.java b/services/backup/java/com/android/server/backup/JobIdManager.java
new file mode 100644
index 0000000..2e834dbf
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/JobIdManager.java
@@ -0,0 +1,30 @@
+/*
+ * 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.backup;
+
+/**
+ * Allocates job IDs for {@link FullBackupJob} and {@link KeyValueBackupJob}
+ */
+public class JobIdManager {
+    public static int getJobIdForUserId(int minJobId, int maxJobId, int userId) {
+        if (minJobId + userId > maxJobId) {
+            throw new RuntimeException("No job IDs available in the given range");
+        }
+
+        return minJobId + userId;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index f2e7435..d43859e 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -25,8 +25,14 @@
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Random;
 
@@ -38,7 +44,8 @@
     private static final String TAG = "KeyValueBackupJob";
     private static ComponentName sKeyValueJobService =
             new ComponentName("android", KeyValueBackupJob.class.getName());
-    private static final int JOB_ID = 0x5039;
+
+    private static final String USER_ID_EXTRA_KEY = "userId";
 
     // Once someone asks for a backup, this is how long we hold off until we find
     // an on-charging opportunity.  If we hit this max latency we will run the operation
@@ -46,16 +53,22 @@
     // BackupManager.backupNow().
     private static final long MAX_DEFERRAL = AlarmManager.INTERVAL_DAY;
 
-    private static boolean sScheduled = false;
-    private static long sNextScheduled = 0;
+    @GuardedBy("KeyValueBackupJob.class")
+    private static final SparseBooleanArray sScheduledForUserId = new SparseBooleanArray();
+    @GuardedBy("KeyValueBackupJob.class")
+    private static final SparseLongArray sNextScheduledForUserId = new SparseLongArray();
 
-    public static void schedule(Context ctx, BackupManagerConstants constants) {
-        schedule(ctx, 0, constants);
+    private static final int MIN_JOB_ID = 52417896;
+    private static final int MAX_JOB_ID = 52418896;
+
+    public static void schedule(int userId, Context ctx, BackupManagerConstants constants) {
+        schedule(userId, ctx, 0, constants);
     }
 
-    public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+    public static void schedule(int userId, Context ctx, long delay,
+            BackupManagerConstants constants) {
         synchronized (KeyValueBackupJob.class) {
-            if (sScheduled) {
+            if (sScheduledForUserId.get(userId)) {
                 return;
             }
 
@@ -76,51 +89,61 @@
             if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
             }
-            JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
+
+            JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId),
+                    sKeyValueJobService)
                     .setMinimumLatency(delay)
                     .setRequiredNetworkType(networkType)
                     .setRequiresCharging(needsCharging)
                     .setOverrideDeadline(MAX_DEFERRAL);
+
+            Bundle extraInfo = new Bundle();
+            extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+            builder.setTransientExtras(extraInfo);
+
             JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
             js.schedule(builder.build());
 
-            sNextScheduled = System.currentTimeMillis() + delay;
-            sScheduled = true;
+            sScheduledForUserId.put(userId, true);
+            sNextScheduledForUserId.put(userId, System.currentTimeMillis() + delay);
         }
     }
 
-    public static void cancel(Context ctx) {
+    public static void cancel(int userId, Context ctx) {
         synchronized (KeyValueBackupJob.class) {
-            JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-            js.cancel(JOB_ID);
-            sNextScheduled = 0;
-            sScheduled = false;
+            JobScheduler js = (JobScheduler) ctx.getSystemService(
+                    Context.JOB_SCHEDULER_SERVICE);
+            js.cancel(getJobIdForUserId(userId));
+
+            clearScheduledForUserId(userId);
         }
     }
 
-    public static long nextScheduled() {
+    public static long nextScheduled(int userId) {
         synchronized (KeyValueBackupJob.class) {
-            return sNextScheduled;
+            return sNextScheduledForUserId.get(userId);
         }
     }
 
-    public static boolean isScheduled() {
+    @VisibleForTesting
+    public static boolean isScheduled(int userId) {
         synchronized (KeyValueBackupJob.class) {
-            return sScheduled;
+            return sScheduledForUserId.get(userId);
         }
     }
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
         synchronized (KeyValueBackupJob.class) {
-            sNextScheduled = 0;
-            sScheduled = false;
+            clearScheduledForUserId(userId);
         }
 
         // Time to run a key/value backup!
         Trampoline service = BackupManagerService.getInstance();
         try {
-            service.backupNow();
+            service.backupNowForUser(userId);
         } catch (RemoteException e) {}
 
         // This was just a trigger; ongoing wakelock management is done by the
@@ -134,4 +157,13 @@
         return false;
     }
 
+    @GuardedBy("KeyValueBackupJob.class")
+    private static void clearScheduledForUserId(int userId) {
+        sScheduledForUserId.delete(userId);
+        sNextScheduledForUserId.delete(userId);
+    }
+
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 79d4a2c..4ca2545 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -698,15 +698,15 @@
 
     // Full backup/restore entry points - non-Binder; called directly
     // by the full-backup scheduled job
-    /* package */ boolean beginFullBackup(FullBackupJob scheduledJob) {
+    /* package */ boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.beginFullBackup(scheduledJob) : false;
+        return (svc != null) ? svc.beginFullBackup(userId, scheduledJob) : false;
     }
 
-    /* package */ void endFullBackup() {
+    /* package */ void endFullBackup(@UserIdInt int userId) {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.endFullBackup();
+            svc.endFullBackup(userId);
         }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index e075785..ed6a46c 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -1674,8 +1674,8 @@
             }
             // We don't want the backup jobs to kick in any time soon.
             // Reschedules them to run in the distant future.
-            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
-            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            KeyValueBackupJob.schedule(mUserId, mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            FullBackupJob.schedule(mUserId, mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
         } finally {
             Binder.restoreCallingIdentity(oldToken);
         }
@@ -1910,7 +1910,7 @@
                 Runnable r = new Runnable() {
                     @Override
                     public void run() {
-                        FullBackupJob.schedule(mContext, latency, mConstants);
+                        FullBackupJob.schedule(mUserId, mContext, latency, mConstants);
                     }
                 };
                 mBackupHandler.postDelayed(r, 2500);
@@ -2033,7 +2033,7 @@
                 mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
         if (result.batterySaverEnabled) {
             if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
-            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+            FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval, mConstants);
             return false;
         }
 
@@ -2147,7 +2147,7 @@
                 mBackupHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        FullBackupJob.schedule(mContext, deferTime, mConstants);
+                        FullBackupJob.schedule(mUserId, mContext, deferTime, mConstants);
                     }
                 });
                 return false;
@@ -2251,7 +2251,7 @@
         }
 
         // ...and schedule a backup pass if necessary
-        KeyValueBackupJob.schedule(mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
     }
 
     // Note: packageName is currently unused, but may be in the future
@@ -2401,7 +2401,8 @@
                     mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
             if (result.batterySaverEnabled) {
                 if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
-                KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
+                // Try again in several hours.
+                KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
             } else {
                 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
                 synchronized (mQueueLock) {
@@ -2414,7 +2415,7 @@
                     }
 
                     // ...and cancel any pending scheduled job, because we've just superseded it
-                    KeyValueBackupJob.cancel(mContext);
+                    KeyValueBackupJob.cancel(mUserId, mContext);
                 }
             }
         } finally {
@@ -2737,13 +2738,13 @@
             synchronized (mQueueLock) {
                 if (enable && !wasEnabled && mSetupComplete) {
                     // if we've just been enabled, start scheduling backup passes
-                    KeyValueBackupJob.schedule(mContext, mConstants);
+                    KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
                     scheduleNextFullBackupJob(0);
                 } else if (!enable) {
                     // No longer enabled, so stop running backups
                     if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
 
-                    KeyValueBackupJob.cancel(mContext);
+                    KeyValueBackupJob.cancel(mUserId, mContext);
 
                     // This also constitutes an opt-out, so we wipe any data for
                     // this device from the backend.  We start that process with
@@ -3451,7 +3452,7 @@
             pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
             pw.println("Last backup pass started: " + mLastBackupPass
                     + " (now = " + System.currentTimeMillis() + ')');
-            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
+            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));
 
             pw.println("Transport whitelist:");
             for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2ee96d1..0fb4f93 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -633,7 +633,7 @@
             unregisterTask();
 
             if (mJob != null) {
-                mJob.finishBackupPass();
+                mJob.finishBackupPass(backupManagerService.getUserId());
             }
 
             synchronized (backupManagerService.getQueueLock()) {
diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
index 62ae3eb..c5e912e 100644
--- a/services/backup/java/com/android/server/backup/internal/SetupObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -77,7 +77,8 @@
                 if (MORE_DEBUG) {
                     Slog.d(TAG, "Setup complete so starting backups");
                 }
-                KeyValueBackupJob.schedule(mContext, mUserBackupManagerService.getConstants());
+                KeyValueBackupJob.schedule(mUserBackupManagerService.getUserId(), mContext,
+                        mUserBackupManagerService.getConstants());
                 mUserBackupManagerService.scheduleNextFullBackupJob(0);
             }
         }
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index f39d795..ef7ff92 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -1003,7 +1003,7 @@
             // Use the scheduler's default.
             delay = 0;
         }
-        KeyValueBackupJob.schedule(
+        KeyValueBackupJob.schedule(mBackupManagerService.getUserId(),
                 mBackupManagerService.getContext(), delay, mBackupManagerService.getConstants());
 
         for (String packageName : mOriginalQueue) {
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 6f5a196..ff029c1 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -343,14 +343,6 @@
     static final String PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES =
             "ro.hdmi.property_hdmi_cec_never_assign_logical_addresses";
 
-    /**
-     * Property to indicate if the current device is a cec switch device.
-     *
-     * <p> Default is false.
-     */
-    static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH =
-            "ro.hdmi.property_is_device_hdmi_cec_switch";
-
     // Set to false to allow playback device to go to suspend mode even
     // when it's an active source. True by default.
     static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index b75e75f..ba21b78 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -28,7 +28,7 @@
 
 /**
  * Feature action that handles device discovery sequences.
- * Device discovery is launched when TV device is woken from "Standby" state
+ * Device discovery is launched when device is woken from "Standby" state
  * or enabled "Control for Hdmi" from disabled state.
  *
  * <p>Device discovery goes through the following steps.
@@ -89,6 +89,7 @@
     private final DeviceDiscoveryCallback mCallback;
     private int mProcessedDeviceCount = 0;
     private int mTimeoutRetry = 0;
+    private boolean mIsTvDevice = source().mService.isTvDevice();
 
     /**
      * Constructor.
@@ -266,15 +267,19 @@
         current.mPortId = getPortId(current.mPhysicalAddress);
         current.mDeviceType = params[2] & 0xFF;
 
-        tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
+        // TODO(amyjojo): check if non-TV device needs to update cec switch info.
+        // This is to manager CEC device separately in case they don't have address.
+        if (mIsTvDevice) {
+            tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
                     current.mPhysicalAddress);
-
+        }
         increaseProcessedDeviceCount();
         checkAndProceedStage();
     }
 
     private int getPortId(int physicalAddress) {
-        return tv().getPortId(physicalAddress);
+        return mIsTvDevice ? tv().getPortId(physicalAddress)
+            : source().getPortId(physicalAddress);
     }
 
     private void handleSetOsdName(HdmiCecMessage cmd) {
@@ -345,7 +350,9 @@
         mCallback.onDeviceDiscoveryDone(result);
         finish();
         // Process any commands buffered while device discovery action was in progress.
-        tv().processAllDelayedMessages();
+        if (mIsTvDevice) {
+            tv().processAllDelayedMessages();
+        }
     }
 
     private void checkAndProceedStage() {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 6e1b018..32dc0261 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -916,6 +916,11 @@
         setActivePath(mService.portIdToPath(portId));
     }
 
+    // Returns the id of the port that the target device is connected to.
+    int getPortId(int physicalAddress) {
+        return mService.pathToPortId(physicalAddress);
+    }
+
     @ServiceThreadOnly
     HdmiCecMessageCache getCecMessageCache() {
         assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 8cfe47f..0e4e334 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -23,19 +23,25 @@
 import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.tv.TvContract;
 import android.os.SystemProperties;
+import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.hdmi.Constants.AudioCodec;
+import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
+import java.io.UnsupportedEncodingException;
 import java.util.HashMap;
+import java.util.List;
 
 /**
  * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
@@ -71,6 +77,10 @@
     // processing.
     private final HashMap<Integer, String> mTvInputs = new HashMap<>();
 
+    // Map-like container of all cec devices.
+    // device id is used as key of container.
+    private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
+
     protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
         mSystemAudioControlFeatureEnabled = true;
@@ -86,6 +96,132 @@
                 "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7");
     }
 
+    /**
+     * Called when a device is newly added or a new device is detected or
+     * an existing device is updated.
+     *
+     * @param info device info of a new device.
+     */
+    @ServiceThreadOnly
+    final void addCecDevice(HdmiDeviceInfo info) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo old = addDeviceInfo(info);
+        if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
+            // The addition of the device itself should not be notified.
+            // Note that different logical address could still be the same local device.
+            return;
+        }
+        if (old == null) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        } else if (!old.equals(info)) {
+            invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        }
+    }
+
+    /**
+     * Called when a device is removed or removal of device is detected.
+     *
+     * @param address a logical address of a device to be removed
+     */
+    @ServiceThreadOnly
+    final void removeCecDevice(int address) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
+
+        mCecMessageCache.flushMessagesFrom(address);
+        invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+    }
+
+    /**
+     * Called when a device is updated.
+     *
+     * @param info device info of the updating device.
+     */
+    @ServiceThreadOnly
+    final void updateCecDevice(HdmiDeviceInfo info) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo old = addDeviceInfo(info);
+
+        if (old == null) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        } else if (!old.equals(info)) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+        }
+    }
+
+    /**
+    * Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
+     * logical address as new device info's.
+     *
+     * @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
+     * @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
+     *         that has the same logical address as new one has.
+     */
+    @ServiceThreadOnly
+    @VisibleForTesting
+    protected HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
+        if (oldDeviceInfo != null) {
+            removeDeviceInfo(deviceInfo.getId());
+        }
+        mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
+        return oldDeviceInfo;
+    }
+
+    /**
+     * Remove a device info corresponding to the given {@code logicalAddress}.
+     * It returns removed {@link HdmiDeviceInfo} if exists.
+     *
+     * @param id id of device to be removed
+     * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
+     */
+    @ServiceThreadOnly
+    private HdmiDeviceInfo removeDeviceInfo(int id) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
+        if (deviceInfo != null) {
+            mDeviceInfos.remove(id);
+        }
+        return deviceInfo;
+    }
+
+    /**
+     * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
+     *
+     * @param logicalAddress logical address of the device to be retrieved
+     * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
+     *         Returns null if no logical address matched
+     */
+    @ServiceThreadOnly
+    HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
+        assertRunOnServiceThread();
+        return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
+    }
+
+    private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
+        mService.invokeDeviceEventListeners(info, status);
+    }
+
+    @Override
+    @ServiceThreadOnly
+    void onHotplug(int portId, boolean connected) {
+        assertRunOnServiceThread();
+        if (connected) {
+            mService.wakeUp();
+        }
+        if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            mCecMessageCache.flushAll();
+        } else {
+            if (connected) {
+                launchDeviceDiscovery();
+            } else {
+                // TODO(amyjojo): remove device from mDeviceInfo
+            }
+        }
+    }
+
     @Override
     @ServiceThreadOnly
     protected void onStandby(boolean initiatedByCec, int standbyAction) {
@@ -116,6 +252,8 @@
         boolean lastSystemAudioControlStatus =
                 SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
         systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
+        clearDeviceInfoList();
+        launchDeviceDiscovery();
         startQueuedActions();
     }
 
@@ -152,6 +290,78 @@
 
     @Override
     @ServiceThreadOnly
+    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        int path = HdmiUtils.twoBytesToInt(message.getParams());
+        int address = message.getSource();
+        int type = message.getParams()[2];
+
+        // Ignore if [Device Discovery Action] is going on.
+        if (hasAction(DeviceDiscoveryAction.class)) {
+            Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
+            return true;
+        }
+
+        // Update the device info with TIF, note that the same device info could have added in
+        // device discovery and we do not want to override it with default OSD name. Therefore we
+        // need the following check to skip redundant device info updating.
+        HdmiDeviceInfo oldDevice = getCecDeviceInfo(address);
+        if (oldDevice == null || oldDevice.getPhysicalAddress() != path) {
+            addCecDevice(new HdmiDeviceInfo(
+                    address, path, mService.pathToPortId(path), type,
+                    Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address)));
+            // if we are adding a new device info, send out a give osd name command
+            // to update the name of the device in TIF
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
+            return true;
+        }
+
+        Slog.w(TAG, "Device info exists. Not updating on Physical Address.");
+        return true;
+    }
+
+    @Override
+    protected boolean handleReportPowerStatus(HdmiCecMessage command) {
+        int newStatus = command.getParams()[0] & 0xFF;
+        updateDevicePowerStatus(command.getSource(), newStatus);
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleSetOsdName(HdmiCecMessage message) {
+        int source = message.getSource();
+        String osdName;
+        HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
+        // If the device is not in device list, ignore it.
+        if (deviceInfo == null) {
+            Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
+            return true;
+        }
+        try {
+            osdName = new String(message.getParams(), "US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+            Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
+            return true;
+        }
+
+        if (deviceInfo.getDisplayName().equals(osdName)) {
+            Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
+            return true;
+        }
+
+        Slog.d(TAG, "Updating device OSD name from "
+                + deviceInfo.getDisplayName()
+                + " to " + osdName);
+        updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
+                deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
+                deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
     protected boolean handleReportAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // TODO(amyjojo): implement report audio status handler
@@ -407,8 +617,8 @@
         HdmiLogger.debug(
                 "System Audio Mode change[old:%b new:%b]",
                 mSystemAudioActivated, newSystemAudioMode);
-        // Wake up device if System Audio Control is turned on but device is still on standby
-        if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
+        // Wake up device if System Audio Control is turned on
+        if (newSystemAudioMode) {
             mService.wakeUp();
         }
         setSystemAudioMode(newSystemAudioMode);
@@ -494,19 +704,20 @@
             invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
             return;
         }
-        getActiveSource().invalidate();
         if (!mService.isControlEnabled()) {
+            setRoutingPort(portId);
             setLocalActivePort(portId);
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
-        int oldPath = getLocalActivePort() != Constants.CEC_SWITCH_HOME
-                ? getActivePathOnSwitchFromActivePortId(getLocalActivePort())
+        int oldPath = getRoutingPort() != Constants.CEC_SWITCH_HOME
+                ? mService.portIdToPath(getRoutingPort())
                 : getDeviceInfo().getPhysicalAddress();
-        int newPath = getActivePathOnSwitchFromActivePortId(portId);
+        int newPath = mService.portIdToPath(portId);
         if (oldPath == newPath) {
             return;
         }
+        setRoutingPort(portId);
         setLocalActivePort(portId);
         HdmiCecMessage routingChange =
                 HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
@@ -575,10 +786,8 @@
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
             return;
         }
-        // Wake up device if it is still on standby
-        if (mService.isPowerStandbyOrTransient()) {
-            mService.wakeUp();
-        }
+        // Wake up device
+        mService.wakeUp();
         // Check if TV supports System Audio Control.
         // Handle broadcasting setSystemAudioMode on or aborting message on callback.
         queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
@@ -635,9 +844,7 @@
             return;
         }
         // Wake up if the current device if ready to route.
-        if (mService.isPowerStandbyOrTransient()) {
-            mService.wakeUp();
-        }
+        mService.wakeUp();
         if (portId == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
             switchToHomeTvInput();
         } else if (portId == Constants.CEC_SWITCH_ARC) {
@@ -725,4 +932,50 @@
                 mAddress, routingInformationPath));
         routeToInputFromPortId(getRoutingPort());
     }
+
+    protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+        HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
+        if (info == null) {
+            Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
+            return;
+        }
+
+        if (info.getDevicePowerStatus() == newPowerStatus) {
+            return;
+        }
+
+        HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
+        // addDeviceInfo replaces old device info with new one if exists.
+        addDeviceInfo(newInfo);
+
+        invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+    }
+
+    @ServiceThreadOnly
+    private void launchDeviceDiscovery() {
+        assertRunOnServiceThread();
+        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
+                new DeviceDiscoveryCallback() {
+                    @Override
+                    public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
+                        for (HdmiDeviceInfo info : deviceInfos) {
+                            addCecDevice(info);
+                        }
+                    }
+                });
+        addAndStartAction(action);
+    }
+
+    // Clear all device info.
+    @ServiceThreadOnly
+    private void clearDeviceInfoList() {
+        assertRunOnServiceThread();
+        for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
+            if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
+                continue;
+            }
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+        }
+        mDeviceInfos.clear();
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 6532e16..a95f7f1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -16,7 +16,10 @@
 
 package com.android.server.hdmi;
 
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+
 import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.SystemProperties;
 import android.util.Slog;
@@ -42,7 +45,7 @@
     // Device has cec switch functionality or not.
     // Default is false.
     protected boolean mIsSwitchDevice = SystemProperties.getBoolean(
-            Constants.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
 
     // Routing port number used for Routing Control.
     // This records the default routing port or the previous valid routing port.
@@ -71,9 +74,11 @@
     @ServiceThreadOnly
     void onHotplug(int portId, boolean connected) {
         assertRunOnServiceThread();
-        mCecMessageCache.flushAll();
+        if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            mCecMessageCache.flushAll();
+        }
         // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
-        if (mService.isPowerStandbyOrTransient()) {
+        if (connected) {
             mService.wakeUp();
         }
     }
@@ -117,6 +122,7 @@
             setActiveSource(activeSource);
         }
         setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
+        updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
         switchInputOnReceivingNewActivePath(physicalAddress);
         return true;
     }
@@ -185,6 +191,13 @@
         // do nothing
     }
 
+    // Update the power status of the devices connected to the current device.
+    // This only works if the current device is a switch and keeps tracking the device info
+    // of the device connected to it.
+    protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+        // do nothing
+    }
+
     // Active source claiming needs to be handled in Service
     // since service can decide who will be the active source when the device supports
     // multiple device types in this method.
@@ -204,10 +217,8 @@
         if (!mIsActiveSource) {
             return;
         }
-        // Wake up the device if the power is in standby mode
-        if (mService.isPowerStandbyOrTransient()) {
-            mService.wakeUp();
-        }
+        // Wake up the device
+        mService.wakeUp();
         return;
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index b91d8c6..a8c4350 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -346,10 +346,6 @@
         }
     }
 
-    int getPortId(int physicalAddress) {
-        return mService.pathToPortId(physicalAddress);
-    }
-
     /**
      * Returns the previous port id kept to handle input switching on <Inactive Source>.
      */
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 833091d..2d6e762 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -19,6 +19,7 @@
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
 
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
 import static com.android.server.hdmi.Constants.DISABLED;
 import static com.android.server.hdmi.Constants.ENABLED;
 import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
@@ -754,6 +755,9 @@
         mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
         mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
 
+        if (mMhlController == null) {
+            return;
+        }
         HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
         ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
         for (HdmiPortInfo info : mhlPortInfo) {
@@ -808,13 +812,31 @@
     }
 
     /**
-     * Returns the id of HDMI port located at the top of the hierarchy of
-     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
-     * the port id to be returned is the ID associated with the port address
-     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
+     * Returns the id of HDMI port located at the current device that runs this method.
+     *
+     * For TV with physical address 0x0000, target device 0x1120, we want port physical address
+     * 0x1000 to get the correct port id from {@link #mPortIdMap}. For device with Physical Address
+     * 0x2000, target device 0x2420, we want port address 0x24000 to get the port id.
+     *
+     * <p>Return {@link Constants#INVALID_PORT_ID} if target device does not connect to.
+     *
+     * @param path the target device's physical address.
+     * @return the id of the port that the target device eventually connects to
+     * on the current device.
      */
     int pathToPortId(int path) {
-        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
+        int mask = 0xF000;
+        int finalMask = 0xF000;
+        int physicalAddress = getPhysicalAddress();
+        int maskedAddress = physicalAddress;
+
+        while (maskedAddress != 0) {
+            maskedAddress = physicalAddress & mask;
+            finalMask |= mask;
+            mask >>= 4;
+        }
+
+        int portAddress = path & finalMask;
         return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
     }
 
@@ -1007,8 +1029,9 @@
     void onHotplug(int portId, boolean connected) {
         assertRunOnServiceThread();
 
-        if (connected && !isTvDevice()) {
-            if (getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT && isSwitchDevice()) {
+        if (connected && !isTvDevice()
+                && getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            if (isSwitchDevice()) {
                 initPortInfo();
                 HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");
             }
@@ -2130,7 +2153,7 @@
 
     boolean isSwitchDevice() {
         return SystemProperties.getBoolean(
-            Constants.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
     }
 
     boolean isTvDeviceEnabled() {
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b1e9144..b145e1e 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -106,9 +106,7 @@
 
         synchronized (mProxyByGroupTmp) {
             // record individual ranking result and nominate proxies for each group
-            // Note: iteration is done backwards such that the index can be used as a sort key
-            // in a string compare below
-            for (int i = N - 1; i >= 0; i--) {
+            for (int i = 0; i < N; i++) {
                 final NotificationRecord record = notificationList.get(i);
                 record.setAuthoritativeRank(i);
                 final String groupKey = record.getGroupKey();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d0b20e8..bf4e272 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -225,6 +225,17 @@
                 Slog.w(TAG, "Deleting orphan icon " + icon);
                 icon.delete();
             }
+
+            // Invalid sessions might have been marked while parsing. Re-write the database with
+            // the updated information.
+            writeSessionsLocked();
+
+            for (int i = 0; i < mSessions.size(); i++) {
+                final PackageInstallerSession session = mSessions.valueAt(i);
+                if (session.isStaged()) {
+                    mStagingManager.restoreSession(session);
+                }
+            }
         }
     }
 
@@ -329,9 +340,6 @@
 
                         if (valid) {
                             mSessions.put(session.sessionId, session);
-                            if (session.isStaged()) {
-                                mStagingManager.restoreSession(session);
-                            }
                         } else {
                             // Since this is early during boot we don't send
                             // any observer events about the session, but we
@@ -1122,6 +1130,11 @@
             mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
         }
 
+        public void onStagedSessionChanged(PackageInstallerSession session) {
+            writeSessionsAsync();
+            mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
+        }
+
         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
             mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 516927f..12d335d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2009,6 +2009,7 @@
         mCallback.onSessionFinished(this, success);
     }
 
+    /** {@hide} */
     void setStagedSessionReady() {
         synchronized (mLock) {
             mStagedSessionReady = true;
@@ -2016,6 +2017,7 @@
             mStagedSessionFailed = false;
             mStagedSessionErrorCode = SessionInfo.NO_ERROR;
         }
+        mCallback.onStagedSessionChanged(this);
     }
 
     /** {@hide} */
@@ -2026,6 +2028,33 @@
             mStagedSessionFailed = true;
             mStagedSessionErrorCode = errorCode;
         }
+        mCallback.onStagedSessionChanged(this);
+    }
+
+    /** {@hide} */
+    void setStagedSessionApplied() {
+        synchronized (mLock) {
+            mStagedSessionReady = false;
+            mStagedSessionApplied = true;
+            mStagedSessionFailed = false;
+            mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+        }
+        mCallback.onStagedSessionChanged(this);
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionReady() {
+        return mStagedSessionReady;
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionApplied() {
+        return mStagedSessionApplied;
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionFailed() {
+        return mStagedSessionFailed;
     }
 
     private void destroyInternal() {
@@ -2221,6 +2250,16 @@
         return permissionsArray;
     }
 
+    // Sanity check to be performed when the session is restored from an external file. Only one
+    // of the session states should be true, or none of them.
+    private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
+                                                     boolean isFailed) {
+        return (!isReady && !isApplied && !isFailed)
+                || (isReady && !isApplied && !isFailed)
+                || (!isReady && isApplied && !isFailed)
+                || (!isReady && !isApplied && isFailed);
+    }
+
     /**
      * Read new session from a {@link XmlPullParser xml description} and create it.
      *
@@ -2287,6 +2326,10 @@
         final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED);
         final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE);
 
+        if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
+            throw new IllegalArgumentException("Can't restore staged session with invalid state.");
+        }
+
         return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 installerThread, stagingManager, sessionId, userId, installerPackageName,
                 installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed,
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index c297c62..ac05ee8 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -168,7 +168,6 @@
         } else {
             session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED);
         }
-        mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
     }
 
     void commitSession(@NonNull PackageInstallerSession session) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b100ecd..a207354 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -945,7 +945,7 @@
     /** Returns true if uid has a visible window or its process is in a top state. */
     private boolean isUidForeground(int uid) {
         return (mService.getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
-            || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
+            || mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
     }
 
     /** Returns true if uid is in a persistent state. */
@@ -968,18 +968,19 @@
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
             final int callingUidProcState = mService.getUidStateLocked(callingUid);
             final boolean callingUidHasAnyVisibleWindow =
-                    mService.mWindowManager.isAnyWindowVisibleForUid(callingUid);
+                    mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
             final int realCallingUidProcState = (callingUid == realCallingUid)
                     ? callingUidProcState
                     : mService.getUidStateLocked(realCallingUid);
             final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
                     ? callingUidHasAnyVisibleWindow
-                    : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
+                    : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(
+                            realCallingUid);
             final String targetPackage = (r != null) ? r.packageName : null;
             final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
             final int targetUidProcState = mService.getUidStateLocked(targetUid);
             final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
-                    ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
+                    ? mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(targetUid)
                     : false;
             final String targetWhitelistTag = (targetUid != -1)
                     ? mService.getPendingTempWhitelistTagForUidLocked(targetUid)
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4e70bbc..8fb7947 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -280,6 +281,15 @@
     }
 
     /**
+     * Returns true if the callingUid has any non-toast window currently visible to the user.
+     */
+    boolean isAnyNonToastWindowVisibleForUid(int callingUid) {
+        return forAllWindows(w -> {
+            return w.getOwningUid() == callingUid && w.isVisible() && w.mAttrs.type != TYPE_TOAST;
+        }, true /* traverseTopToBottom */);
+    }
+
+    /**
      * Returns the app window token for the input binder if it exist in the system.
      * NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since
      * AppWindowToken represents an activity which can only exist on one display.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9b82f5c..90506e7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5716,17 +5716,6 @@
     }
 
     /**
-     * Returns true if the callingUid has any window currently visible to the user.
-     */
-    public boolean isAnyWindowVisibleForUid(int callingUid) {
-        synchronized (mGlobalLock) {
-            return mRoot.forAllWindows(w -> {
-                return w.getOwningUid() == callingUid && w.isVisible();
-            }, true /* traverseTopToBottom */);
-        }
-    }
-
-    /**
      * Called when a task has been removed from the recent tasks list.
      * <p>
      * Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index 769a9d4..0d3b6b2 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -1075,7 +1075,7 @@
                 createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
         FullBackupJob job = new FullBackupJob();
 
-        backupManagerService.beginFullBackup(job);
+        backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
 
         verify(mUserOneService).beginFullBackup(job);
     }
@@ -1086,7 +1086,7 @@
         BackupManagerService backupManagerService = createService();
         FullBackupJob job = new FullBackupJob();
 
-        backupManagerService.beginFullBackup(job);
+        backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
 
         verify(mUserOneService, never()).beginFullBackup(job);
     }
@@ -1097,7 +1097,7 @@
         BackupManagerService backupManagerService =
                 createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
 
-        backupManagerService.endFullBackup();
+        backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
 
         verify(mUserOneService).endFullBackup();
     }
@@ -1107,7 +1107,7 @@
     public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
         BackupManagerService backupManagerService = createService();
 
-        backupManagerService.endFullBackup();
+        backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
 
         verify(mUserOneService, never()).endFullBackup();
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
new file mode 100644
index 0000000..9a78d0b3
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.app.job.JobScheduler;
+import android.content.Context;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowJobScheduler;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowJobScheduler.class})
+@Presubmit
+public class FullBackupJobTest {
+    private Context mContext;
+    private BackupManagerConstants mConstants;
+    private ShadowJobScheduler mShadowJobScheduler;
+
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = RuntimeEnvironment.application;
+        mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
+        mConstants.start();
+
+        mShadowJobScheduler = Shadows.shadowOf(mContext.getSystemService(JobScheduler.class));
+
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mConstants.stop();
+        FullBackupJob.cancel(mUserOneId, mContext);
+        FullBackupJob.cancel(mUserTwoId, mContext);
+    }
+
+    @Test
+    public void testSchedule_afterScheduling_jobExists() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+    }
+
+    @Test
+    public void testCancel_afterCancelling_jobDoesntExist() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+        FullBackupJob.cancel(mUserOneId, mContext);
+        FullBackupJob.cancel(mUserTwoId, mContext);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+    }
+//
+    @Test
+    public void testSchedule_onlySchedulesForRequestedUser() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+    }
+//
+    @Test
+    public void testCancel_onlyCancelsForRequestedUser() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+        FullBackupJob.cancel(mUserOneId, mContext);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+    }
+
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(FullBackupJob.MIN_JOB_ID, FullBackupJob.MAX_JOB_ID,
+                userId);
+    }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
new file mode 100644
index 0000000..f8bb1ee
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import static org.testng.Assert.expectThrows;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class JobIdManagerTest {
+    private static final int MIN_JOB_ID = 10;
+    private static final int MAX_JOB_ID = 20;
+
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
+    @Before
+    public void setUp() {
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
+    }
+
+    @Test
+    public void testGetJobIdForUserId_returnsDifferentJobIdsForDifferentUsers() {
+        int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+        int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserTwoId);
+
+        assertThat(jobIdOne).isNotEqualTo(jobIdTwo);
+    }
+
+    @Test
+    public void testGetJobIdForUserId_returnsSameJobIdForSameUser() {
+        int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+        int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+
+        assertThat(jobIdOne).isEqualTo(jobIdTwo);
+    }
+
+    @Test
+    public void testGetJobIdForUserId_throwsExceptionIfRangeIsExceeded() {
+        expectThrows(
+                RuntimeException.class,
+                () -> JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID,
+                        MAX_JOB_ID + 1));
+    }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
index 8e17209..8d9e44f 100644
--- a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
@@ -18,8 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
 import org.junit.After;
@@ -35,32 +37,67 @@
     private Context mContext;
     private BackupManagerConstants mConstants;
 
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
     @Before
     public void setUp() throws Exception {
         mContext = RuntimeEnvironment.application;
         mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
         mConstants.start();
+
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
     }
 
     @After
     public void tearDown() throws Exception {
         mConstants.stop();
-        KeyValueBackupJob.cancel(mContext);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+        KeyValueBackupJob.cancel(mUserTwoId, mContext);
     }
 
     @Test
     public void testIsScheduled_beforeScheduling_returnsFalse() {
-        boolean isScheduled = KeyValueBackupJob.isScheduled();
-
-        assertThat(isScheduled).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
     }
 
     @Test
     public void testIsScheduled_afterScheduling_returnsTrue() {
-        KeyValueBackupJob.schedule(mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
 
-        boolean isScheduled = KeyValueBackupJob.isScheduled();
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
+    }
 
-        assertThat(isScheduled).isTrue();
+    @Test
+    public void testIsScheduled_afterCancelling_returnsFalse() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+        KeyValueBackupJob.cancel(mUserTwoId, mContext);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+    }
+
+    @Test
+    public void testIsScheduled_afterScheduling_returnsTrueOnlyForScheduledUser() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+    }
+
+    @Test
+    public void testIsScheduled_afterCancelling_returnsFalseOnlyForCancelledUser() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
     }
 }
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 8d5c301..4968176 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -1137,8 +1137,9 @@
          * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
          * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
          */
-        public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
-            ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+        public static void schedule(int userId, Context ctx, long delay,
+                BackupManagerConstants constants) {
+            ShadowKeyValueBackupJob.schedule(userId, ctx, delay, constants);
             throw new IllegalArgumentException();
         }
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
index b754356..b08ae6d 100644
--- a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
@@ -125,7 +125,7 @@
 
         setupObserver.onChange(true);
 
-        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mUserBackupManagerService.getUserId())).isTrue();
         // Verifies that the full backup job is scheduled. The job is scheduled via a posted message
         // on the backup handler so we verify that a message exists.
         assertThat(mUserBackupManagerService.getBackupHandler().hasMessagesOrCallbacks()).isTrue();
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 7dac795..b923bb0 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -2727,7 +2727,7 @@
             throws RemoteException, IOException {
         verify(transportMock.transport).requestBackupTime();
         assertBackupPendingFor(packages);
-        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mBackupManagerService.getUserId())).isTrue();
     }
 
     private void assertBackupPendingFor(PackageData... packages) throws IOException {
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
index 23c44b0e..f90ea6a 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
@@ -34,7 +34,8 @@
     }
 
     @Implementation
-    protected static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+    protected static void schedule(int userId, Context ctx, long delay,
+            BackupManagerConstants constants) {
         callingUid = Binder.getCallingUid();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index db83505..6a9a121 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -1149,31 +1149,31 @@
 
     @Test
     public void beginFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.beginFullBackup(new FullBackupJob());
+        mTrampoline.beginFullBackup(mUserId, new FullBackupJob());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
     public void beginFullBackup_forwarded() throws RemoteException {
         FullBackupJob fullBackupJob = new FullBackupJob();
-        when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true);
+        when(mBackupManagerServiceMock.beginFullBackup(mUserId, fullBackupJob)).thenReturn(true);
 
         mTrampoline.initializeService();
-        assertTrue(mTrampoline.beginFullBackup(fullBackupJob));
-        verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob);
+        assertTrue(mTrampoline.beginFullBackup(mUserId, fullBackupJob));
+        verify(mBackupManagerServiceMock).beginFullBackup(mUserId, fullBackupJob);
     }
 
     @Test
     public void endFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.endFullBackup();
+        mTrampoline.endFullBackup(mUserId);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
     public void endFullBackup_forwarded() throws RemoteException {
         mTrampoline.initializeService();
-        mTrampoline.endFullBackup();
-        verify(mBackupManagerServiceMock).endFullBackup();
+        mTrampoline.endFullBackup(mUserId);
+        verify(mBackupManagerServiceMock).endFullBackup(mUserId);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 4255e37..8607ec6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -50,6 +50,7 @@
 
     private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
     private int mMyPhysicalAddress = 0;
+    private HdmiPortInfo[] mHdmiPortInfo = null;
 
     @Override
     public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
@@ -92,9 +93,11 @@
 
     @Override
     public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
-        HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[1];
-        hdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
-        return hdmiPortInfo;
+        if (mHdmiPortInfo == null) {
+            mHdmiPortInfo = new HdmiPortInfo[1];
+            mHdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
+        }
+        return mHdmiPortInfo;
     }
 
     @Override
@@ -131,4 +134,10 @@
     protected void setPhysicalAddress(int physicalAddress) {
         mMyPhysicalAddress = physicalAddress;
     }
+
+    @VisibleForTesting
+    protected void setPortInfo(HdmiPortInfo[] hdmiPortInfo) {
+        mHdmiPortInfo = new HdmiPortInfo[hdmiPortInfo.length];
+        System.arraycopy(hdmiPortInfo, 0, mHdmiPortInfo, 0, hdmiPortInfo.length);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index b47f269..3b51a2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.hdmi;
 
+import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
+import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE;
+
 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
@@ -25,6 +28,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.hardware.hdmi.HdmiDeviceInfo;
 import android.media.AudioManager;
 import android.os.Looper;
 import android.os.SystemProperties;
@@ -64,6 +68,8 @@
     private int mMusicMaxVolume;
     private boolean mMusicMute;
     private int mAvrPhysicalAddress;
+    private int mInvokeDeviceEventState;
+    private HdmiDeviceInfo mDeviceInfo;
 
     @Before
     public void setUp() {
@@ -127,6 +133,18 @@
 
                 @Override
                 void wakeUp() {}
+
+                @Override
+                void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
+                    mDeviceInfo = device;
+                    mInvokeDeviceEventState = status;
+                }
+
+                @Override
+                int pathToPortId(int path) {
+                    // port id is not useful for the test right now
+                    return 1;
+                }
             };
 
         mMyLooper = mTestLooper.getLooper();
@@ -157,6 +175,8 @@
         mNativeWrapper.setPhysicalAddress(mAvrPhysicalAddress);
         SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true");
         SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "true");
+        mInvokeDeviceEventState = 0;
+        mDeviceInfo = null;
     }
 
     @Test
@@ -611,4 +631,73 @@
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
+
+    @Test
+    public void updateCecDevice_deviceNotExists_addDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
+        HdmiDeviceInfo newDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(newDevice);
+        assertThat(mDeviceInfo).isEqualTo(newDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(newDevice.getLogicalAddress())).isEqualTo(newDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
+    }
+
+    @Test
+    public void updateCecDevice_deviceExists_doNothing() {
+        mInvokeDeviceEventState = 0;
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(oldDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(0);
+    }
+
+    @Test
+    public void updateCecDevice_deviceInfoDifferent_updateDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 4, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(differentDevice);
+        assertThat(mDeviceInfo).isEqualTo(differentDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
+    }
+
+    @Test
+    public void handleReportPhysicalAddress_differentPath_addDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
+                .buildReportPhysicalAddressCommand(
+                        ADDR_PLAYBACK_1, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK);
+        mHdmiCecLocalDeviceAudioSystem.handleReportPhysicalAddress(reportPhysicalAddress);
+
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+        mTestLooper.dispatchAll();
+        assertThat(mDeviceInfo).isEqualTo(differentDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 18c9a65..67ce13f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -20,13 +20,18 @@
 
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import android.hardware.hdmi.HdmiPortInfo;
 import android.os.Looper;
 import android.os.test.TestLooper;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -102,6 +107,7 @@
     private TestLooper mTestLooper = new TestLooper();
     private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
     private boolean mStandbyMessageReceived;
+    private HdmiPortInfo[] mHdmiPortInfo;
 
     @Before
     public void SetUp() {
@@ -131,6 +137,16 @@
 
         mLocalDevices.add(mMyAudioSystemDevice);
         mLocalDevices.add(mMyPlaybackDevice);
+        mHdmiPortInfo = new HdmiPortInfo[4];
+        mHdmiPortInfo[0] =
+            new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+        mHdmiPortInfo[1] =
+            new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
+        mHdmiPortInfo[2] =
+            new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+        mHdmiPortInfo[3] =
+            new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
+        mNativeWrapper.setPortInfo(mHdmiPortInfo);
         mHdmiControlService.initPortInfo();
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
 
@@ -159,4 +175,24 @@
         assertTrue(mMyPlaybackDevice.isDisabled());
         assertTrue(mMyAudioSystemDevice.isDisabled());
     }
+
+    @Test
+    public void pathToPort_pathExists_weAreNonTv() {
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(1);
+        assertThat(mHdmiControlService.pathToPortId(0x2234)).isEqualTo(2);
+    }
+
+    @Test
+    public void pathToPort_pathExists_weAreTv() {
+        mNativeWrapper.setPhysicalAddress(0x0000);
+        assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(3);
+        assertThat(mHdmiControlService.pathToPortId(0x3234)).isEqualTo(4);
+    }
+
+    @Test
+    public void pathToPort_pathInvalid() {
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 5638cb36..c79e1db0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,8 +15,11 @@
  */
 package com.android.server.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 
+import static junit.framework.TestCase.assertEquals;
+
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
@@ -153,7 +156,7 @@
                 .build();
         mRecordGroupGSortA = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiGroupGSortA, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiGroupGSortB = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("B")
@@ -163,7 +166,7 @@
                 .build();
         mRecordGroupGSortB = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiGroupGSortB, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroup = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("C")
@@ -171,7 +174,7 @@
                 .build();
         mRecordNoGroup = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroup, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroup2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("D")
@@ -179,7 +182,7 @@
                 .build();
         mRecordNoGroup2 = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroup2, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroupSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("E")
@@ -188,7 +191,7 @@
                 .build();
         mRecordNoGroupSortA = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroupSortA, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mAudioAttributes = new AudioAttributes.Builder()
                 .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -197,11 +200,16 @@
                 .build();
     }
 
-    private NotificationChannel getDefaultChannel() {
+    private NotificationChannel getLowChannel() {
         return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
                 IMPORTANCE_LOW);
     }
 
+    private NotificationChannel getDefaultChannel() {
+        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+                IMPORTANCE_DEFAULT);
+    }
+
     @Test
     public void testSortShouldRespectCritical() throws Exception {
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(7);
@@ -285,4 +293,40 @@
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
         mHelper.sort(notificationList);
     }
+    
+    @Test
+    public void testGroupNotifications_highestIsProxy() {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<>();
+        // this should be the last in the list, except it's in a group with a high child
+        Notification lowSummaryN = new Notification.Builder(mContext, "")
+                .setGroup("group")
+                .setGroupSummary(true)
+                .build();
+        NotificationRecord lowSummary = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "summary", 0, 0, lowSummaryN, USER,
+                null, System.currentTimeMillis()), getLowChannel());
+        notificationList.add(lowSummary);
+
+        Notification lowN = new Notification.Builder(mContext, "").build();
+        NotificationRecord low = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "low", 0, 0, lowN, USER,
+                null, System.currentTimeMillis()), getLowChannel());
+        low.setContactAffinity(0.5f);
+        notificationList.add(low);
+
+        Notification highChildN = new Notification.Builder(mContext, "")
+                .setGroup("group")
+                .setGroupSummary(false)
+                .build();
+        NotificationRecord highChild = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "child", 0, 0, highChildN, USER,
+                null, System.currentTimeMillis()), getDefaultChannel());
+        notificationList.add(highChild);
+
+        mHelper.sort(notificationList);
+
+        assertEquals(lowSummary, notificationList.get(0));
+        assertEquals(highChild, notificationList.get(1));
+        assertEquals(low, notificationList.get(2));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 7a6b2b5..ec88718 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -643,10 +643,10 @@
             boolean hasForegroundActivities, boolean callerIsRecents,
             boolean callerIsTempWhitelisted) {
         // window visibility
-        doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
-                callingUid);
-        doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager)
-                .isAnyWindowVisibleForUid(realCallingUid);
+        doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
+                .isAnyNonToastWindowVisibleForUid(callingUid);
+        doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
+                .isAnyNonToastWindowVisibleForUid(realCallingUid);
         // process importance
         doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid);
         doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 0000000..45fe5d2
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,75 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for RootWindowContainer.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+public class RootWindowContainerTests extends WindowTestsBase {
+
+    private static final int FAKE_CALLING_UID = 667;
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_oneToastOneNonToastBothVisible() {
+        final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+        toastyToast.mHasSurface = true;
+        app.mHasSurface = true;
+
+        assertTrue(toastyToast.isVisible());
+        assertTrue(app.isVisible());
+        assertTrue(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_onlyToastVisible() {
+        final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
+        toastyToast.mHasSurface = true;
+
+        assertTrue(toastyToast.isVisible());
+        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_aFewNonToastButNoneVisible() {
+        final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar", FAKE_CALLING_UID);
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+
+        assertFalse(topBar.isVisible());
+        assertFalse(app.isVisible());
+        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+}
+
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 638cb03..cdc0a47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -257,6 +257,14 @@
         }
     }
 
+    WindowState createWindow(WindowState parent, int type, String name, int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            return (parent == null)
+                    ? createWindow(parent, type, mDisplayContent, name, ownerId)
+                    : createWindow(parent, type, parent.mToken, name, ownerId);
+        }
+    }
+
     WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType,
             int type, DisplayContent dc, String name) {
         synchronized (mWm.mGlobalLock) {
@@ -277,7 +285,16 @@
         synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(
                     dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
-            return createWindow(parent, type, token, name);
+            return createWindow(parent, type, token, name, 0 /* ownerId */);
+        }
+    }
+
+    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
+            int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            final WindowToken token = createWindowToken(
+                    dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
+            return createWindow(parent, type, token, name, ownerId);
         }
     }
 
@@ -299,6 +316,14 @@
     }
 
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
+            int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            return createWindow(parent, type, token, name, ownerId,
+                    false /* ownerCanAddInternalSystemWindow */);
+        }
+    }
+
+    WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
             int ownerId, boolean ownerCanAddInternalSystemWindow) {
         return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
                 mWm, mMockSession, mIWindow);
diff --git a/telephony/java/android/telephony/CallAttributes.aidl b/telephony/java/android/telephony/CallAttributes.aidl
new file mode 100644
index 0000000..69127df
--- /dev/null
+++ b/telephony/java/android/telephony/CallAttributes.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+parcelable CallAttributes;
+
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
new file mode 100644
index 0000000..2b99ce1
--- /dev/null
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -0,0 +1,151 @@
+/*
+ * 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.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.TelephonyManager.NetworkType;
+
+import java.util.Objects;
+
+/**
+ * Contains information about a call's attributes as passed up from the HAL. If there are multiple
+ * ongoing calls, the CallAttributes will pertain to the call in the foreground.
+ * @hide
+ */
+@SystemApi
+public class CallAttributes implements Parcelable {
+    private PreciseCallState mPreciseCallState;
+    @NetworkType
+    private int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints
+    private CallQuality mCallQuality;
+
+
+    public CallAttributes(PreciseCallState state, @NetworkType int networkType,
+            CallQuality callQuality) {
+        this.mPreciseCallState = state;
+        this.mNetworkType = networkType;
+        this.mCallQuality = callQuality;
+    }
+
+    @Override
+    public String toString() {
+        return "mPreciseCallState=" + mPreciseCallState + " mNetworkType=" + mNetworkType
+                + " mCallQuality=" + mCallQuality;
+    }
+
+    private CallAttributes(Parcel in) {
+        mPreciseCallState = (PreciseCallState) in.readValue(mPreciseCallState.getClass()
+                .getClassLoader());
+        mNetworkType = in.readInt();
+        mCallQuality = (CallQuality) in.readValue(mCallQuality.getClass().getClassLoader());
+    }
+
+    // getters
+    /**
+     * Returns the {@link PreciseCallState} of the call.
+     */
+    public PreciseCallState getPreciseCallState() {
+        return mPreciseCallState;
+    }
+
+    /**
+     * Returns the {@link TelephonyManager#NetworkType} of the call.
+     *
+     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+     * @see TelephonyManager#NETWORK_TYPE_GPRS
+     * @see TelephonyManager#NETWORK_TYPE_EDGE
+     * @see TelephonyManager#NETWORK_TYPE_UMTS
+     * @see TelephonyManager#NETWORK_TYPE_CDMA
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+     * @see TelephonyManager#NETWORK_TYPE_1xRTT
+     * @see TelephonyManager#NETWORK_TYPE_HSDPA
+     * @see TelephonyManager#NETWORK_TYPE_HSUPA
+     * @see TelephonyManager#NETWORK_TYPE_HSPA
+     * @see TelephonyManager#NETWORK_TYPE_IDEN
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+     * @see TelephonyManager#NETWORK_TYPE_LTE
+     * @see TelephonyManager#NETWORK_TYPE_EHRPD
+     * @see TelephonyManager#NETWORK_TYPE_HSPAP
+     * @see TelephonyManager#NETWORK_TYPE_GSM
+     * @see TelephonyManager#NETWORK_TYPE_TD_SCDMA
+     * @see TelephonyManager#NETWORK_TYPE_IWLAN
+     * @see TelephonyManager#NETWORK_TYPE_LTE_CA
+     * @see TelephonyManager#NETWORK_TYPE_NR
+     */
+    @NetworkType
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Returns the {#link CallQuality} of the call.
+     */
+    public CallQuality getCallQuality() {
+        return mCallQuality;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPreciseCallState, mNetworkType, mCallQuality);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof CallAttributes) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallAttributes s = (CallAttributes) o;
+
+        return (mPreciseCallState == s.mPreciseCallState
+                && mNetworkType == s.mNetworkType
+                && mCallQuality == s.mCallQuality);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public @Parcelable.ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+        mPreciseCallState.writeToParcel(dest, flags);
+        dest.writeInt(mNetworkType);
+        mCallQuality.writeToParcel(dest, flags);
+    }
+
+    public static final Parcelable.Creator<CallAttributes> CREATOR = new Parcelable.Creator() {
+        public CallAttributes createFromParcel(Parcel in) {
+            return new CallAttributes(in);
+        }
+
+        public CallAttributes[] newArray(int size) {
+            return new CallAttributes[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/CallQuality.aidl b/telephony/java/android/telephony/CallQuality.aidl
new file mode 100644
index 0000000..f54355f
--- /dev/null
+++ b/telephony/java/android/telephony/CallQuality.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+parcelable CallQuality;
+
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
new file mode 100644
index 0000000..b27f6b4
--- /dev/null
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -0,0 +1,341 @@
+/*
+ * 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.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Parcelable object to handle call quality.
+ * <p>
+ * Currently this supports IMS calls.
+ * <p>
+ * It provides the call quality level, duration, and additional information related to RTP packets,
+ * jitter and delay.
+ * <p>
+ * If there are multiple active calls, the CallQuality will pertain to the call in the foreground.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CallQuality implements Parcelable {
+
+    // Constants representing the call quality level (see #CallQuality);
+    public static final int CALL_QUALITY_EXCELLENT = 0;
+    public static final int CALL_QUALITY_GOOD = 1;
+    public static final int CALL_QUALITY_FAIR = 2;
+    public static final int CALL_QUALITY_POOR = 3;
+    public static final int CALL_QUALITY_BAD = 4;
+    public static final int CALL_QUALITY_NOT_AVAILABLE = 5;
+
+    /**
+     * Call quality
+     * @hide
+     */
+    @IntDef(prefix = { "CALL_QUALITY_" }, value = {
+            CALL_QUALITY_EXCELLENT,
+            CALL_QUALITY_GOOD,
+            CALL_QUALITY_FAIR,
+            CALL_QUALITY_POOR,
+            CALL_QUALITY_BAD,
+            CALL_QUALITY_NOT_AVAILABLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallQualityLevel {}
+
+    @CallQualityLevel
+    private int mDownlinkCallQualityLevel;
+    @CallQualityLevel
+    private int mUplinkCallQualityLevel;
+    private int mCallDuration;
+    private int mNumRtpPacketsTransmitted;
+    private int mNumRtpPacketsReceived;
+    private int mNumRtpPacketsTransmittedLost;
+    private int mNumRtpPacketsNotReceived;
+    private int mAverageRelativeJitter;
+    private int mMaxRelativeJitter;
+    private int mAverageRoundTripTime;
+    private int mCodecType;
+
+    /** @hide **/
+    public CallQuality(Parcel in) {
+        mDownlinkCallQualityLevel = in.readInt();
+        mUplinkCallQualityLevel = in.readInt();
+        mCallDuration = in.readInt();
+        mNumRtpPacketsTransmitted = in.readInt();
+        mNumRtpPacketsReceived = in.readInt();
+        mNumRtpPacketsTransmittedLost = in.readInt();
+        mNumRtpPacketsNotReceived = in.readInt();
+        mAverageRelativeJitter = in.readInt();
+        mMaxRelativeJitter = in.readInt();
+        mAverageRoundTripTime = in.readInt();
+        mCodecType = in.readInt();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param callQualityLevel the call quality level (see #CallQualityLevel)
+     * @param callDuration the call duration in milliseconds
+     * @param numRtpPacketsTransmitted RTP packets sent to network
+     * @param numRtpPacketsReceived RTP packets received from network
+     * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+     * transmitted
+     * @param numRtpPacketsNotReceived RTP packets which were lost in network and never recieved
+     * @param averageRelativeJitter average relative jitter in milliseconds
+     * @param maxRelativeJitter maximum relative jitter in milliseconds
+     * @param averageRoundTripTime average round trip delay in milliseconds
+     * @param codecType the codec type
+     */
+    public CallQuality(
+            @CallQualityLevel int downlinkCallQualityLevel,
+            @CallQualityLevel int uplinkCallQualityLevel,
+            int callDuration,
+            int numRtpPacketsTransmitted,
+            int numRtpPacketsReceived,
+            int numRtpPacketsTransmittedLost,
+            int numRtpPacketsNotReceived,
+            int averageRelativeJitter,
+            int maxRelativeJitter,
+            int averageRoundTripTime,
+            int codecType) {
+        this.mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+        this.mUplinkCallQualityLevel = uplinkCallQualityLevel;
+        this.mCallDuration = callDuration;
+        this.mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+        this.mNumRtpPacketsReceived = numRtpPacketsReceived;
+        this.mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+        this.mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+        this.mAverageRelativeJitter = averageRelativeJitter;
+        this.mMaxRelativeJitter = maxRelativeJitter;
+        this.mAverageRoundTripTime = averageRoundTripTime;
+        this.mCodecType = codecType;
+    }
+
+    // getters
+    /**
+     * Returns the downlink CallQualityLevel for a given ongoing call.
+     */
+    @CallQualityLevel
+    public int getDownlinkCallQualityLevel() {
+        return mDownlinkCallQualityLevel;
+    }
+
+    /**
+     * Returns the uplink CallQualityLevel for a given ongoing call.
+     */
+    @CallQualityLevel
+    public int getUplinkCallQualityLevel() {
+        return mUplinkCallQualityLevel;
+    }
+
+    /**
+     * Returns the duration of the call, in milliseconds.
+     */
+    public int getCallDuration() {
+        return mCallDuration;
+    }
+
+    /**
+     * Returns the total number of RTP packets transmitted by this device for a given ongoing call.
+     */
+    public int getNumRtpPacketsTransmitted() {
+        return mNumRtpPacketsTransmitted;
+    }
+
+    /**
+     * Returns the total number of RTP packets received by this device for a given ongoing call.
+     */
+    public int getNumRtpPacketsReceived() {
+        return mNumRtpPacketsReceived;
+    }
+
+    /**
+     * Returns the number of RTP packets which were sent by this device but were lost in the
+     * network before reaching the other party.
+     */
+    public int getNumRtpPacketsTransmittedLost() {
+        return mNumRtpPacketsTransmittedLost;
+    }
+
+    /**
+     * Returns the number of RTP packets which were sent by the other party but were lost in the
+     * network before reaching this device.
+     */
+    public int getNumRtpPacketsNotReceived() {
+        return mNumRtpPacketsNotReceived;
+    }
+
+    /**
+     * Returns the average relative jitter in milliseconds. Jitter represents the amount of variance
+     * in interarrival time of packets, for example, if two packets are sent 2 milliseconds apart
+     * but received 3 milliseconds apart, the relative jitter between those packets is 1
+     * millisecond.
+     *
+     * <p>See RFC 3550 for more information on jitter calculations.
+     */
+    public int getAverageRelativeJitter() {
+        return mAverageRelativeJitter;
+    }
+
+    /**
+     * Returns the maximum relative jitter for a given ongoing call. Jitter represents the amount of
+     * variance in interarrival time of packets, for example, if two packets are sent 2 milliseconds
+     * apart but received 3 milliseconds apart, the relative jitter between those packets is 1
+     * millisecond.
+     *
+     * <p>See RFC 3550 for more information on jitter calculations.
+     */
+    public int getMaxRelativeJitter() {
+        return mMaxRelativeJitter;
+    }
+
+    /**
+     * Returns the average round trip time in milliseconds.
+     */
+    public int getAverageRoundTripTime() {
+        return mAverageRoundTripTime;
+    }
+
+    /**
+     * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
+     * {@link ImsStreamMediaProfile}.
+     *
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_NONE
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_QCELP13K
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_B
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_NW
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_EFR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_FR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_HR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711U
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G723
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711A
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G722
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711AB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G729
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_NB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_SWB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_FB
+     */
+    public int getCodecType() {
+        return mCodecType;
+    }
+
+    // Parcelable things
+    @Override
+    public String toString() {
+        return "CallQuality: {downlinkCallQualityLevel=" + mDownlinkCallQualityLevel
+                + " uplinkCallQualityLevel=" + mUplinkCallQualityLevel
+                + " callDuration=" + mCallDuration
+                + " numRtpPacketsTransmitted=" + mNumRtpPacketsTransmitted
+                + " numRtpPacketsReceived=" + mNumRtpPacketsReceived
+                + " numRtpPacketsTransmittedLost=" + mNumRtpPacketsTransmittedLost
+                + " numRtpPacketsNotReceived=" + mNumRtpPacketsNotReceived
+                + " averageRelativeJitter=" + mAverageRelativeJitter
+                + " maxRelativeJitter=" + mMaxRelativeJitter
+                + " averageRoundTripTime=" + mAverageRoundTripTime
+                + " codecType=" + mCodecType
+                + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mDownlinkCallQualityLevel,
+                mUplinkCallQualityLevel,
+                mCallDuration,
+                mNumRtpPacketsTransmitted,
+                mNumRtpPacketsReceived,
+                mNumRtpPacketsTransmittedLost,
+                mNumRtpPacketsNotReceived,
+                mAverageRelativeJitter,
+                mMaxRelativeJitter,
+                mAverageRoundTripTime,
+                mCodecType);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof CallQuality) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallQuality s = (CallQuality) o;
+
+        return (mDownlinkCallQualityLevel == s.mDownlinkCallQualityLevel
+                && mUplinkCallQualityLevel == s.mUplinkCallQualityLevel
+                && mCallDuration == s.mCallDuration
+                && mNumRtpPacketsTransmitted == s.mNumRtpPacketsTransmitted
+                && mNumRtpPacketsReceived == s.mNumRtpPacketsReceived
+                && mNumRtpPacketsTransmittedLost == s.mNumRtpPacketsTransmittedLost
+                && mNumRtpPacketsNotReceived == s.mNumRtpPacketsNotReceived
+                && mAverageRelativeJitter == s.mAverageRelativeJitter
+                && mMaxRelativeJitter == s.mMaxRelativeJitter
+                && mAverageRoundTripTime == s.mAverageRoundTripTime
+                && mCodecType == s.mCodecType);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public @Parcelable.ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+        dest.writeInt(mDownlinkCallQualityLevel);
+        dest.writeInt(mUplinkCallQualityLevel);
+        dest.writeInt(mCallDuration);
+        dest.writeInt(mNumRtpPacketsTransmitted);
+        dest.writeInt(mNumRtpPacketsReceived);
+        dest.writeInt(mNumRtpPacketsTransmittedLost);
+        dest.writeInt(mNumRtpPacketsNotReceived);
+        dest.writeInt(mAverageRelativeJitter);
+        dest.writeInt(mMaxRelativeJitter);
+        dest.writeInt(mAverageRoundTripTime);
+        dest.writeInt(mCodecType);
+    }
+
+    public static final Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
+        public CallQuality createFromParcel(Parcel in) {
+            return new CallQuality(in);
+        }
+
+        public CallQuality[] newArray(int size) {
+            return new CallQuality[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/ICellInfoCallback.aidl b/telephony/java/android/telephony/ICellInfoCallback.aidl
index 7fb62682..ee3c1b1 100644
--- a/telephony/java/android/telephony/ICellInfoCallback.aidl
+++ b/telephony/java/android/telephony/ICellInfoCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.os.ParcelableException;
 import android.telephony.CellInfo;
 
 import java.util.List;
@@ -27,4 +28,5 @@
 oneway interface ICellInfoCallback
 {
     void onCellInfo(in List<CellInfo> state);
+    void onError(in int errorCode, in ParcelableException detail);
 }
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index e27b385..a16d7eb 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -335,6 +335,18 @@
     @SystemApi
     public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
 
+    /**
+     * Listen for changes to the call attributes of a currently active call.
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE}
+     *
+     * @see #onCallAttributesChanged
+     * @hide
+     */
+    @SystemApi
+    public static final int LISTEN_CALL_ATTRIBUTES_CHANGED                 = 0x04000000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -683,6 +695,17 @@
     }
 
     /**
+     * Callback invoked when the call attributes changes. Requires
+     * the READ_PRIVILEGED_PHONE_STATE permission.
+     * @param callAttributes the call attributes
+     * @hide
+     */
+    @SystemApi
+    public void onCallAttributesChanged(CallAttributes callAttributes) {
+        // default implementation empty
+    }
+
+    /**
      * Callback invoked when modem radio power state changes. Requires
      * the READ_PRIVILEGED_PHONE_STATE permission.
      * @param state the modem radio power state
@@ -941,6 +964,14 @@
                     () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
         }
 
+        public void onCallAttributesChanged(CallAttributes callAttributes) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes)));
+        }
+
         public void onPreferredDataSubIdChanged(int subId) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 2271069..e77042d 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -24,6 +24,8 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -178,6 +180,35 @@
         return mLte;
     }
 
+    /**
+     * Returns a List of CellSignalStrength Components of this SignalStrength Report.
+     *
+     * Use this API to access underlying
+     * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+     * granular information about the SignalStrength report. Only valid (non-empty)
+     * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
+     * and the list may contain more than one instance of a CellSignalStrength type.
+     *
+     * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
+     *
+     * @see android.telephony#CellSignalStrength
+     * @see android.telephony#CellSignalStrengthNr
+     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony#CellSignalStrengthTdscdma
+     * @see android.telephony#CellSignalStrengthWcdma
+     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony#CellSignalStrengthGsm
+     */
+    public @NonNull List<CellSignalStrength> getCellSignalStrengths() {
+        List<CellSignalStrength> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
+        if (mLte.isValid()) cssList.add(mLte);
+        if (mCdma.isValid()) cssList.add(mCdma);
+        if (mTdscdma.isValid()) cssList.add(mTdscdma);
+        if (mWcdma.isValid()) cssList.add(mWcdma);
+        if (mGsm.isValid()) cssList.add(mGsm);
+        return cssList;
+    }
+
     /** @hide */
     public void updateLevel(PersistableBundle cc, ServiceState ss) {
         mLteRsrpBoost = ss.getLteEarfcnRsrpBoost();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b48a1ce..7f7d5e7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4898,19 +4898,53 @@
     /** Callback for providing asynchronous {@link CellInfo} on request */
     public abstract static class CellInfoCallback {
         /**
-         * Response to
+         * Success response to
          * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
          *
-         * <p>Invoked when there is a response to
+         * Invoked when there is a response to
          * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}
          * to provide a list of {@link CellInfo}. If no {@link CellInfo} is available then an empty
-         * list will be provided. If an error occurs, null will be provided.
+         * list will be provided. If an error occurs, null will be provided unless the onError
+         * callback is overridden.
          *
          * @param cellInfo a list of {@link CellInfo}, an empty list, or null.
          *
          * {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
          */
-        public abstract void onCellInfo(List<CellInfo> cellInfo);
+        public abstract void onCellInfo(@NonNull List<CellInfo> cellInfo);
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"ERROR_"}, value = {ERROR_TIMEOUT, ERROR_MODEM_ERROR})
+        public @interface CellInfoCallbackError {}
+
+        /**
+         * The system timed out waiting for a response from the Radio.
+         */
+        public static final int ERROR_TIMEOUT = 1;
+
+        /**
+         * The modem returned a failure.
+         */
+        public static final int ERROR_MODEM_ERROR = 2;
+
+        /**
+         * Error response to
+         * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
+         *
+         * Invoked when an error condition prevents updated {@link CellInfo} from being fetched
+         * and returned from the modem. Callers of requestCellInfoUpdate() should override this
+         * function to receive detailed status information in the event of an error. By default,
+         * this function will invoke onCellInfo() with null.
+         *
+         * @param errorCode an error code indicating the type of failure.
+         * @param detail a Throwable object with additional detail regarding the failure if
+         *     available, otherwise null.
+         */
+        public void onError(@CellInfoCallbackError int errorCode, @Nullable Throwable detail) {
+            // By default, simply invoke the success callback with an empty list.
+            onCellInfo(new ArrayList<CellInfo>());
+        }
     };
 
     /**
@@ -4937,6 +4971,12 @@
                             Binder.withCleanCallingIdentity(() ->
                                     executor.execute(() -> callback.onCellInfo(cellInfo)));
                         }
+
+                        public void onError(int errorCode, android.os.ParcelableException detail) {
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> callback.onError(
+                                            errorCode, detail.getCause())));
+                        }
                     }, getOpPackageName());
 
         } catch (RemoteException ex) {
@@ -4971,6 +5011,12 @@
                             Binder.withCleanCallingIdentity(() ->
                                     executor.execute(() -> callback.onCellInfo(cellInfo)));
                         }
+
+                        public void onError(int errorCode, android.os.ParcelableException detail) {
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> callback.onError(
+                                            errorCode, detail.getCause())));
+                        }
                     }, getOpPackageName(), workSource);
         } catch (RemoteException ex) {
         }
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 00cf9c3..3dbebe8 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -17,14 +17,15 @@
 package com.android.internal.telephony;
 
 import android.os.Bundle;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
+import android.telephony.CallAttributes;
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.emergency.EmergencyNumber;
 
 oneway interface IPhoneStateListener {
@@ -54,6 +55,7 @@
     void onPhoneCapabilityChanged(in PhoneCapability capability);
     void onPreferredDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
+    void onCallAttributesChanged(in CallAttributes callAttributes);
     void onEmergencyNumberListChanged(in Map emergencyNumberList);
     void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
 }
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 7689fc3..59a7290 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -572,7 +572,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(mCertType, mCertSha256Fingerprint);
+            return Objects.hash(mCertType, Arrays.hashCode(mCertSha256Fingerprint));
         }
 
         @Override
@@ -842,24 +842,50 @@
     }
 
     /**
-     * CA (Certificate Authority) X509 certificate.
+     * CA (Certificate Authority) X509 certificates.
      */
-    private X509Certificate mCaCertificate = null;
+    private X509Certificate[] mCaCertificates = null;
+
     /**
      * Set the CA (Certification Authority) certificate associated with this credential.
      *
      * @param caCertificate The CA certificate to set to
      */
     public void setCaCertificate(X509Certificate caCertificate) {
-        mCaCertificate = caCertificate;
+        mCaCertificates = null;
+        if (caCertificate != null) {
+            mCaCertificates = new X509Certificate[] {caCertificate};
+        }
     }
+
+    /**
+     * Set the CA (Certification Authority) certificates associated with this credential.
+     *
+     * @param caCertificates The list of CA certificates to set to
+     * @hide
+     */
+    public void setCaCertificates(X509Certificate[] caCertificates) {
+        mCaCertificates = caCertificates;
+    }
+
     /**
      * Get the CA (Certification Authority) certificate associated with this credential.
      *
-     * @return CA certificate associated with this credential
+     * @return CA certificate associated with this credential, {@code null} if certificate is not
+     * set or certificate is more than one.
      */
     public X509Certificate getCaCertificate() {
-        return mCaCertificate;
+        return mCaCertificates == null || mCaCertificates.length > 1 ? null : mCaCertificates[0];
+    }
+
+    /**
+     * Get the CA (Certification Authority) certificates associated with this credential.
+     *
+     * @return The list of CA certificates associated with this credential
+     * @hide
+     */
+    public X509Certificate[] getCaCertificates() {
+        return mCaCertificates;
     }
 
     /**
@@ -933,7 +959,11 @@
                 mClientCertificateChain = Arrays.copyOf(source.mClientCertificateChain,
                                                         source.mClientCertificateChain.length);
             }
-            mCaCertificate = source.mCaCertificate;
+            if (source.mCaCertificates != null) {
+                mCaCertificates = Arrays.copyOf(source.mCaCertificates,
+                        source.mCaCertificates.length);
+            }
+
             mClientPrivateKey = source.mClientPrivateKey;
         }
     }
@@ -952,7 +982,7 @@
         dest.writeParcelable(mUserCredential, flags);
         dest.writeParcelable(mCertCredential, flags);
         dest.writeParcelable(mSimCredential, flags);
-        ParcelUtil.writeCertificate(dest, mCaCertificate);
+        ParcelUtil.writeCertificates(dest, mCaCertificates);
         ParcelUtil.writeCertificates(dest, mClientCertificateChain);
         ParcelUtil.writePrivateKey(dest, mClientPrivateKey);
     }
@@ -977,16 +1007,17 @@
                     : mCertCredential.equals(that.mCertCredential))
                 && (mSimCredential == null ? that.mSimCredential == null
                     : mSimCredential.equals(that.mSimCredential))
-                && isX509CertificateEquals(mCaCertificate, that.mCaCertificate)
+                && isX509CertificatesEquals(mCaCertificates, that.mCaCertificates)
                 && isX509CertificatesEquals(mClientCertificateChain, that.mClientCertificateChain)
                 && isPrivateKeyEquals(mClientPrivateKey, that.mClientPrivateKey);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mRealm, mCreationTimeInMillis, mExpirationTimeInMillis,
+        return Objects.hash(mCreationTimeInMillis, mExpirationTimeInMillis, mRealm,
                 mCheckAaaServerCertStatus, mUserCredential, mCertCredential, mSimCredential,
-                mCaCertificate, mClientCertificateChain, mClientPrivateKey);
+                mClientPrivateKey, Arrays.hashCode(mCaCertificates),
+                Arrays.hashCode(mClientCertificateChain));
     }
 
     @Override
@@ -1067,7 +1098,7 @@
                 credential.setUserCredential(in.readParcelable(null));
                 credential.setCertCredential(in.readParcelable(null));
                 credential.setSimCredential(in.readParcelable(null));
-                credential.setCaCertificate(ParcelUtil.readCertificate(in));
+                credential.setCaCertificates(ParcelUtil.readCertificates(in));
                 credential.setClientCertificateChain(ParcelUtil.readCertificates(in));
                 credential.setClientPrivateKey(ParcelUtil.readPrivateKey(in));
                 return credential;
@@ -1100,7 +1131,7 @@
 
         // CA certificate is required for R1 Passpoint profile.
         // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
-        if (isR1 && mCaCertificate == null) {
+        if (isR1 && mCaCertificates == null) {
             Log.d(TAG, "Missing CA Certificate for user credential");
             return false;
         }
@@ -1131,7 +1162,7 @@
         // Verify required key and certificates for certificate credential.
         // CA certificate is required for R1 Passpoint profile.
         // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
-        if (isR1 && mCaCertificate == null) {
+        if (isR1 && mCaCertificates == null) {
             Log.d(TAG, "Missing CA Certificate for certificate credential");
             return false;
         }
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index a9d4b8f..1ecc3fe 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.hotspot2.pps;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -44,17 +45,16 @@
      * @param userCred Instance of UserCredential
      * @param certCred Instance of CertificateCredential
      * @param simCred Instance of SimCredential
-     * @param caCert CA certificate
      * @param clientCertificateChain Chain of client certificates
      * @param clientPrivateKey Client private key
+     * @param caCerts CA certificates
      * @return {@link Credential}
      */
     private static Credential createCredential(Credential.UserCredential userCred,
-                                               Credential.CertificateCredential certCred,
-                                               Credential.SimCredential simCred,
-                                               X509Certificate caCert,
-                                               X509Certificate[] clientCertificateChain,
-                                               PrivateKey clientPrivateKey) {
+            Credential.CertificateCredential certCred,
+            Credential.SimCredential simCred,
+            X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey,
+            X509Certificate... caCerts) {
         Credential cred = new Credential();
         cred.setCreationTimeInMillis(123455L);
         cred.setExpirationTimeInMillis(2310093L);
@@ -63,7 +63,11 @@
         cred.setUserCredential(userCred);
         cred.setCertCredential(certCred);
         cred.setSimCredential(simCred);
-        cred.setCaCertificate(caCert);
+        if (caCerts != null && caCerts.length == 1) {
+            cred.setCaCertificate(caCerts[0]);
+        } else {
+            cred.setCaCertificates(caCerts);
+        }
         cred.setClientCertificateChain(clientCertificateChain);
         cred.setClientPrivateKey(clientPrivateKey);
         return cred;
@@ -80,8 +84,8 @@
         certCred.setCertType("x509v3");
         certCred.setCertSha256Fingerprint(
                 MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
-        return createCredential(null, certCred, null, FakeKeys.CA_CERT0,
-                new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+        return createCredential(null, certCred, null, new X509Certificate[] {FakeKeys.CLIENT_CERT},
+                FakeKeys.RSA_KEY1, FakeKeys.CA_CERT0, FakeKeys.CA_CERT1);
     }
 
     /**
@@ -93,7 +97,7 @@
         Credential.SimCredential simCred = new Credential.SimCredential();
         simCred.setImsi("1234*");
         simCred.setEapType(EAPConstants.EAP_SIM);
-        return createCredential(null, null, simCred, null, null, null);
+        return createCredential(null, null, simCred, null, null, (X509Certificate[]) null);
     }
 
     /**
@@ -110,7 +114,7 @@
         userCred.setSoftTokenApp("TestApp");
         userCred.setEapType(EAPConstants.EAP_TTLS);
         userCred.setNonEapInnerMethod("MS-CHAP");
-        return createCredential(userCred, null, null, FakeKeys.CA_CERT0, null, null);
+        return createCredential(userCred, null, null, null, null, FakeKeys.CA_CERT0);
     }
 
     private static void verifyParcel(Credential writeCred) {
@@ -120,6 +124,7 @@
         parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
         Credential readCred = Credential.CREATOR.createFromParcel(parcel);
         assertTrue(readCred.equals(writeCred));
+        assertEquals(writeCred.hashCode(), readCred.hashCode());
     }
 
     /**