Merge "Clear identity when asking for other users." into pi-dev
diff --git a/api/current.txt b/api/current.txt
index c5fd77c..5e7fa591 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15727,6 +15727,8 @@
     method public abstract void abortCaptures() throws android.hardware.camera2.CameraAccessException;
     method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public int captureBurstRequests(java.util.List<android.hardware.camera2.CaptureRequest>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException;
+    method public int captureSingleRequest(android.hardware.camera2.CaptureRequest, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close();
     method public abstract void finalizeOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CameraDevice getDevice();
@@ -15734,7 +15736,9 @@
     method public abstract boolean isReprocessable();
     method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public int setRepeatingBurstRequests(java.util.List<android.hardware.camera2.CaptureRequest>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public int setSingleRepeatingRequest(android.hardware.camera2.CaptureRequest, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
     method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
   }
@@ -15902,8 +15906,11 @@
     method public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(java.lang.String) throws android.hardware.camera2.CameraAccessException;
     method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
     method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public void openCamera(java.lang.String, java.util.concurrent.Executor, android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
     method public void registerAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback, android.os.Handler);
+    method public void registerAvailabilityCallback(java.util.concurrent.Executor, android.hardware.camera2.CameraManager.AvailabilityCallback);
     method public void registerTorchCallback(android.hardware.camera2.CameraManager.TorchCallback, android.os.Handler);
+    method public void registerTorchCallback(java.util.concurrent.Executor, android.hardware.camera2.CameraManager.TorchCallback);
     method public void setTorchMode(java.lang.String, boolean) throws android.hardware.camera2.CameraAccessException;
     method public void unregisterAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback);
     method public void unregisterTorchCallback(android.hardware.camera2.CameraManager.TorchCallback);
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 9cba926..8a9c738 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -167,10 +167,13 @@
 Landroid/app/ApplicationPackageManager;->mPM:Landroid/content/pm/IPackageManager;
 Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I
 Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List;
 Landroid/app/AppOpsManager;->mService:Lcom/android/internal/app/IAppOpsService;
 Landroid/app/AppOpsManager;->noteOp(I)I
 Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I
 Landroid/app/AppOpsManager;->OP_COARSE_LOCATION:I
+Landroid/app/AppOpsManager$OpEntry;->getDuration()I
+Landroid/app/AppOpsManager$OpEntry;->getRejectTime()J
 Landroid/app/AppOpsManager;->OP_FINE_LOCATION:I
 Landroid/app/AppOpsManager;->OP_GET_USAGE_STATS:I
 Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I
@@ -327,6 +330,8 @@
 Landroid/app/TaskStackListener;-><init>()V
 Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
 Landroid/app/trust/ITrustManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/app/usage/StorageStatsManager;->getFreeBytes(Ljava/lang/String;)J
+Landroid/app/usage/StorageStatsManager;->getTotalBytes(Ljava/lang/String;)J
 Landroid/app/usage/UsageStatsManager;->mService:Landroid/app/usage/IUsageStatsManager;
 Landroid/app/usage/UsageStats;->mLastEvent:I
 Landroid/app/usage/UsageStats;->mTotalTimeInForeground:J
@@ -357,6 +362,7 @@
 Landroid/bluetooth/BluetoothAdapter;->setScanMode(II)Z
 Landroid/bluetooth/BluetoothAdapter;->setScanMode(I)Z
 Landroid/bluetooth/BluetoothDevice;->createBond(I)Z
+Landroid/bluetooth/BluetoothDevice;->getAlias()Ljava/lang/String;
 Landroid/bluetooth/BluetoothDevice;->getAliasName()Ljava/lang/String;
 Landroid/bluetooth/BluetoothGattCharacteristic;->mInstance:I
 Landroid/bluetooth/BluetoothGattCharacteristic;->mService:Landroid/bluetooth/BluetoothGattService;
@@ -614,6 +620,7 @@
 Landroid/graphics/drawable/BitmapDrawable;->getOpticalInsets()Landroid/graphics/Insets;
 Landroid/graphics/drawable/BitmapDrawable;->getTint()Landroid/content/res/ColorStateList;
 Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode;
+Landroid/graphics/drawable/BitmapDrawable;->mTargetDensity:I
 Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V
 Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect;
 Landroid/graphics/drawable/DrawableContainer;->getOpticalInsets()Landroid/graphics/Insets;
@@ -677,6 +684,7 @@
 Landroid/graphics/NinePatch$InsetStruct;-><init>(IIIIIIIIFIF)V
 Landroid/graphics/NinePatch;->mBitmap:Landroid/graphics/Bitmap;
 Landroid/graphics/Picture;->mNativePicture:J
+Landroid/graphics/PixelXorXfermode;-><init>(I)V
 Landroid/graphics/PorterDuffColorFilter;->setColor(I)V
 Landroid/graphics/PorterDuffColorFilter;->setMode(Landroid/graphics/PorterDuff$Mode;)V
 Landroid/graphics/Region;-><init>(JI)V
@@ -692,14 +700,64 @@
 Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface;
 Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V
 Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
+Landroid/hardware/camera2/CameraCharacteristics;->CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->CONTROL_MAX_REGIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
 Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
 Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
+Landroid/hardware/camera2/CameraCharacteristics;->LED_AVAILABLE_LEDS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->LENS_INFO_SHADING_MAP_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->LOGICAL_MULTI_CAMERA_PHYSICAL_IDS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->QUIRKS_USE_PARTIAL_RESULT:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_CHARACTERISTICS_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_RESULT_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_SESSION_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_MAX_NUM_OUTPUT_STREAMS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_FORMATS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_JPEG_MIN_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_JPEG_SIZES:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_PROCESSED_SIZES:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
+Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_COORDINATES:Landroid/hardware/camera2/CaptureRequest$Key;
+Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_PROCESSING_METHOD:Landroid/hardware/camera2/CaptureRequest$Key;
+Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_TIMESTAMP:Landroid/hardware/camera2/CaptureRequest$Key;
 Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
+Landroid/hardware/camera2/CaptureRequest;->LED_TRANSMIT:Landroid/hardware/camera2/CaptureRequest$Key;
+Landroid/hardware/camera2/CaptureRequest;->REQUEST_ID:Landroid/hardware/camera2/CaptureRequest$Key;
+Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_BLUE:Landroid/hardware/camera2/CaptureRequest$Key;
+Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_GREEN:Landroid/hardware/camera2/CaptureRequest$Key;
+Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_RED:Landroid/hardware/camera2/CaptureRequest$Key;
+Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_COORDINATES:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_PROCESSING_METHOD:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_TIMESTAMP:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
 Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
+Landroid/hardware/camera2/CaptureResult;->LED_TRANSMIT:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->QUIRKS_PARTIAL_RESULT:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->REQUEST_FRAME_COUNT:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->REQUEST_ID:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_IDS:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_LANDMARKS:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_RECTANGLES:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_SCORES:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_LENS_SHADING_MAP:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_TIMESTAMPS:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_X_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_Y_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_PREDICTED_COLOR_GAINS:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_PREDICTED_COLOR_TRANSFORM:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->SYNC_FRAME_NUMBER:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_BLUE:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_GREEN:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_RED:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/impl/CameraMetadataNative;->mMetadataPtr:J
 Landroid/hardware/Camera;->addCallbackBuffer([BI)V
 Landroid/hardware/Camera;->mNativeContext:J
@@ -922,6 +980,7 @@
 Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J
 Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V
 Landroid/media/MediaCodec$CodecException;-><init>(IILjava/lang/String;)V
+Landroid/media/MediaCodec;->getBuffers(Z)[Ljava/nio/ByteBuffer;
 Landroid/media/MediaCodec;->releaseOutputBuffer(IZZJ)V
 Landroid/media/MediaFile;->FIRST_AUDIO_FILE_TYPE:I
 Landroid/media/MediaFile;->getFileType(Ljava/lang/String;)Landroid/media/MediaFile$MediaFileType;
@@ -1298,6 +1357,7 @@
 Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
 Landroid/os/storage/VolumeInfo;->getState()I
 Landroid/os/storage/VolumeInfo;->getType()I
+Landroid/os/storage/VolumeInfo;->isMountedReadable()Z
 Landroid/os/storage/VolumeInfo;->isPrimary()Z
 Landroid/os/storage/VolumeInfo;->isVisible()Z
 Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V
@@ -1849,6 +1909,8 @@
 Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZIIILandroid/text/TextUtils$TruncateAt;I)V
 Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
 Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V
+Landroid/text/Layout$Alignment;->ALIGN_LEFT:Landroid/text/Layout$Alignment;
+Landroid/text/Layout$Alignment;->ALIGN_RIGHT:Landroid/text/Layout$Alignment;
 Landroid/text/Layout;->DIRS_ALL_LEFT_TO_RIGHT:Landroid/text/Layout$Directions;
 Landroid/text/Layout;->getPrimaryHorizontal(IZ)F
 Landroid/text/method/LinkMovementMethod;->sInstance:Landroid/text/method/LinkMovementMethod;
@@ -1932,6 +1994,7 @@
 Landroid/util/Rational;->mNumerator:I
 Landroid/util/Rational;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/util/Singleton;->mInstance:Ljava/lang/Object;
+Landroid/util/Slog;->d(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/util/SparseIntArray;->mKeys:[I
 Landroid/util/SparseIntArray;->mSize:I
 Landroid/util/SparseIntArray;->mValues:[I
@@ -2095,6 +2158,7 @@
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextClassification;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextSelection;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionStarted(I)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/TextClassificationManager;->getTextClassifier(I)Landroid/view/textclassifier/TextClassifier;
 Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
 Landroid/view/TextureView;->destroyHardwareLayer()V
 Landroid/view/TextureView;->mLayer:Landroid/view/TextureLayer;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index fb8ded1..f38c80c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2855,4 +2855,13 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    @Override
+    public boolean isPackageStateProtected(String packageName, int userId) {
+        try {
+            return mPM.isPackageStateProtected(packageName, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 9a19133..d43d80f 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -674,4 +674,6 @@
     boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
 
     String getSystemTextClassifierPackageName();
+
+    boolean isPackageStateProtected(String packageName, int userId);
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4d8773c..114c485 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6140,4 +6140,16 @@
         throw new UnsupportedOperationException(
                 "getSystemTextClassifierPackageName not implemented in subclass");
     }
+
+    /**
+     * @return whether a given package's state is protected, e.g. package cannot be disabled,
+     *         suspended, hidden or force stopped.
+     *
+     * @hide
+     */
+    public boolean isPackageStateProtected(String packageName, int userId) {
+        throw new UnsupportedOperationException(
+            "isPackageStateProtected not implemented in subclass");
+    }
+
 }
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index ff69bd8..eafe593 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -16,15 +16,16 @@
 
 package android.hardware.camera2;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.os.Handler;
 import android.view.Surface;
 
+import java.util.concurrent.Executor;
 import java.util.List;
 
-
 /**
  * A configured capture session for a {@link CameraDevice}, used for capturing images from the
  * camera or reprocessing images captured from the camera in the same session previously.
@@ -354,6 +355,50 @@
             throws CameraAccessException;
 
     /**
+     * <p>Submit a request for an image to be captured by the camera device.</p>
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #capture(CaptureRequest, CaptureCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param request the settings for this capture
+     * @param executor the executor which will be used for invoking the listener.
+     * @param listener The callback object to notify once this request has been
+     * processed.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
+     *                                  configured as outputs for this session; or the request
+     *                                  targets a set of Surfaces that cannot be submitted
+     *                                  simultaneously in a reprocessable capture session; or a
+     *                                  reprocess capture request is submitted in a
+     *                                  non-reprocessable capture session; or the reprocess capture
+     *                                  request was created with a {@link TotalCaptureResult} from
+     *                                  a different session; or the capture targets a Surface in
+     *                                  the middle of being {@link #prepare prepared}; or the
+     *                                  executor is null, or the listener is not null.
+     *
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     * @see #abortCaptures
+     * @see CameraDevice#createReprocessableCaptureSession
+     */
+    public int captureSingleRequest(@NonNull CaptureRequest request,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * Submit a list of requests to be captured in sequence as a burst. The
      * burst will be captured in the minimum amount of time possible, and will
      * not be interleaved with requests submitted by other capture or repeat
@@ -416,6 +461,53 @@
             throws CameraAccessException;
 
     /**
+     * Submit a list of requests to be captured in sequence as a burst. The
+     * burst will be captured in the minimum amount of time possible, and will
+     * not be interleaved with requests submitted by other capture or repeat
+     * calls.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #captureBurst(List, CaptureCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param requests the list of settings for this burst capture
+     * @param executor the executor which will be used for invoking the listener.
+     * @param listener The callback object to notify each time one of the
+     * requests in the burst has been processed.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target
+     *                                  Surfaces not currently configured as outputs; or one of the
+     *                                  requests targets a set of Surfaces that cannot be submitted
+     *                                  simultaneously in a reprocessable capture session; or a
+     *                                  reprocess capture request is submitted in a
+     *                                  non-reprocessable capture session; or one of the reprocess
+     *                                  capture requests was created with a
+     *                                  {@link TotalCaptureResult} from a different session; or one
+     *                                  of the captures targets a Surface in the middle of being
+     *                                  {@link #prepare prepared}; or if the executor is null; or if
+     *                                  the listener is null.
+     *
+     * @see #capture
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     * @see #abortCaptures
+     */
+    public int captureBurstRequests(@NonNull List<CaptureRequest> requests,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * Request endlessly repeating capture of images by this capture session.
      *
      * <p>With this method, the camera device will continually capture images
@@ -483,6 +575,45 @@
             throws CameraAccessException;
 
     /**
+     * Request endlessly repeating capture of images by this capture session.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #setRepeatingRequest(CaptureRequest, CaptureCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param request the request to repeat indefinitely
+     * @param executor the executor which will be used for invoking the listener.
+     * @param listener The callback object to notify every time the
+     * request finishes processing.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException If the request references no Surfaces or references Surfaces
+     *                                  that are not currently configured as outputs; or the request
+     *                                  is a reprocess capture request; or the capture targets a
+     *                                  Surface in the middle of being {@link #prepare prepared}; or
+     *                                  the executor is null; or the listener is null.
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingBurst
+     * @see #stopRepeating
+     * @see #abortCaptures
+     */
+    public int setSingleRepeatingRequest(@NonNull CaptureRequest request,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * <p>Request endlessly repeating capture of a sequence of images by this
      * capture session.</p>
      *
@@ -555,6 +686,47 @@
             throws CameraAccessException;
 
     /**
+     * <p>Request endlessly repeating capture of a sequence of images by this
+     * capture session.</p>
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #setRepeatingBurst(List, CaptureCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param requests the list of requests to cycle through indefinitely
+     * @param executor the executor which will be used for invoking the listener.
+     * @param listener The callback object to notify each time one of the
+     * requests in the repeating bursts has finished processing.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests reference no Surfaces or reference Surfaces
+     *                                  not currently configured as outputs; or one of the requests
+     *                                  is a reprocess capture request; or one of the captures
+     *                                  targets a Surface in the middle of being
+     *                                  {@link #prepare prepared}; or the executor is null; or the
+     *                                  listener is null.
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #stopRepeating
+     * @see #abortCaptures
+     */
+    public int setRepeatingBurstRequests(@NonNull List<CaptureRequest> requests,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * <p>Cancel any ongoing repeating capture set by either
      * {@link #setRepeatingRequest setRepeatingRequest} or
      * {@link #setRepeatingBurst}. Has no effect on requests submitted through
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 4d64295..4124536 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -134,6 +135,27 @@
     }
 
     /**
+     * Register a callback to be notified about camera device availability.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param executor The executor which will be used to invoke the callback.
+     * @param callback the new callback to send camera availability notices to
+     *
+     * @throws IllegalArgumentException if the executor is {@code null}.
+     */
+    public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull AvailabilityCallback callback) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor was null");
+        }
+        CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor);
+    }
+
+    /**
      * Remove a previously-added callback; the callback will no longer receive connection and
      * disconnection callbacks.
      *
@@ -173,6 +195,27 @@
     }
 
     /**
+     * Register a callback to be notified about torch mode status.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #registerTorchCallback(TorchCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param executor The executor which will be used to invoke the callback
+     * @param callback The new callback to send torch mode status to
+     *
+     * @throws IllegalArgumentException if the executor is {@code null}.
+     */
+    public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull TorchCallback callback) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor was null");
+        }
+        CameraManagerGlobal.get().registerTorchCallback(callback, executor);
+    }
+
+    /**
      * Remove a previously-added callback; the callback will no longer receive torch mode status
      * callbacks.
      *
@@ -248,7 +291,7 @@
      *
      * @param cameraId The unique identifier of the camera device to open
      * @param callback The callback for the camera. Must not be null.
-     * @param handler  The handler to invoke the callback on. Must not be null.
+     * @param executor The executor to invoke the callback with. Must not be null.
      * @param uid      The UID of the application actually opening the camera.
      *                 Must be USE_CALLING_UID unless the caller is a service
      *                 that is trusted to open the device on behalf of an
@@ -267,7 +310,7 @@
      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
      */
     private CameraDevice openCameraDeviceUserAsync(String cameraId,
-            CameraDevice.StateCallback callback, Handler handler, final int uid)
+            CameraDevice.StateCallback callback, Executor executor, final int uid)
             throws CameraAccessException {
         CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
         CameraDevice device = null;
@@ -280,7 +323,7 @@
                     new android.hardware.camera2.impl.CameraDeviceImpl(
                         cameraId,
                         callback,
-                        handler,
+                        executor,
                         characteristics,
                         mContext.getApplicationInfo().targetSdkVersion);
 
@@ -421,7 +464,47 @@
             @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
             throws CameraAccessException {
 
-        openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
+        openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
+                USE_CALLING_UID);
+    }
+
+    /**
+     * Open a connection to a camera with the given ID.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #openCamera(String, StateCallback, Handler)}, except that it uses
+     * {@link java.util.concurrent.Executor} as an argument instead of
+     * {@link android.os.Handler}.</p>
+     *
+     * @param cameraId
+     *             The unique identifier of the camera device to open
+     * @param executor
+     *             The executor which will be used when invoking the callback.
+     * @param callback
+     *             The callback which is invoked once the camera is opened
+     *
+     * @throws CameraAccessException if the camera is disabled by device policy,
+     * has been disconnected, or is being used by a higher-priority camera API client.
+     *
+     * @throws IllegalArgumentException if cameraId, the callback or the executor was null,
+     * or the cameraId does not match any currently or previously available
+     * camera device.
+     *
+     * @throws SecurityException if the application does not have permission to
+     * access the camera
+     *
+     * @see #getCameraIdList
+     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
+     */
+    @RequiresPermission(android.Manifest.permission.CAMERA)
+    public void openCamera(@NonNull String cameraId,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull final CameraDevice.StateCallback callback)
+            throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor was null");
+        }
+        openCameraForUid(cameraId, callback, executor, USE_CALLING_UID);
     }
 
     /**
@@ -440,7 +523,7 @@
      * @hide
      */
     public void openCameraForUid(@NonNull String cameraId,
-            @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
+            @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
             int clientUid)
             throws CameraAccessException {
 
@@ -448,19 +531,12 @@
             throw new IllegalArgumentException("cameraId was null");
         } else if (callback == null) {
             throw new IllegalArgumentException("callback was null");
-        } else if (handler == null) {
-            if (Looper.myLooper() != null) {
-                handler = new Handler();
-            } else {
-                throw new IllegalArgumentException(
-                        "Handler argument is null, but no looper exists in the calling thread");
-            }
         }
         if (CameraManagerGlobal.sCameraServiceDisabled) {
             throw new IllegalArgumentException("No cameras available on device");
         }
 
-        openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
+        openCameraDeviceUserAsync(cameraId, callback, executor, clientUid);
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 9cac71c..a4640c1 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -158,14 +158,7 @@
     @Override
     public int capture(CaptureRequest request, CaptureCallback callback,
             Handler handler) throws CameraAccessException {
-        if (request == null) {
-            throw new IllegalArgumentException("request must not be null");
-        } else if (request.isReprocess() && !isReprocessable()) {
-            throw new IllegalArgumentException("this capture session cannot handle reprocess " +
-                    "requests");
-        } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
-            throw new IllegalArgumentException("capture request was created for another session");
-        }
+        checkCaptureRequest(request);
 
         synchronized (mDeviceImpl.mInterfaceLock) {
             checkNotClosed();
@@ -183,25 +176,45 @@
     }
 
     @Override
+    public int captureSingleRequest(CaptureRequest request, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        checkCaptureRequest(request);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
+                        " executor " + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.capture(request,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
+    private void checkCaptureRequest(CaptureRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("request must not be null");
+        } else if (request.isReprocess() && !isReprocessable()) {
+            throw new IllegalArgumentException("this capture session cannot handle reprocess " +
+                    "requests");
+        } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
+            throw new IllegalArgumentException("capture request was created for another session");
+        }
+    }
+
+    @Override
     public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
             Handler handler) throws CameraAccessException {
-        if (requests == null) {
-            throw new IllegalArgumentException("Requests must not be null");
-        } else if (requests.isEmpty()) {
-            throw new IllegalArgumentException("Requests must have at least one element");
-        }
-
-        for (CaptureRequest request : requests) {
-            if (request.isReprocess()) {
-                if (!isReprocessable()) {
-                    throw new IllegalArgumentException("This capture session cannot handle " +
-                            "reprocess requests");
-                } else if (request.getReprocessableSessionId() != mId) {
-                    throw new IllegalArgumentException("Capture request was created for another " +
-                            "session");
-                }
-            }
-        }
+        checkCaptureRequests(requests);
 
         synchronized (mDeviceImpl.mInterfaceLock) {
             checkNotClosed();
@@ -220,13 +233,56 @@
     }
 
     @Override
+    public int captureBurstRequests(List<CaptureRequest> requests, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        checkCaptureRequests(requests);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+                Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
+                        ", callback " + callback + " executor " + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.captureBurst(requests,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
+    private void checkCaptureRequests(List<CaptureRequest> requests) {
+        if (requests == null) {
+            throw new IllegalArgumentException("Requests must not be null");
+        } else if (requests.isEmpty()) {
+            throw new IllegalArgumentException("Requests must have at least one element");
+        }
+
+        for (CaptureRequest request : requests) {
+            if (request.isReprocess()) {
+                if (!isReprocessable()) {
+                    throw new IllegalArgumentException("This capture session cannot handle " +
+                            "reprocess requests");
+                } else if (request.getReprocessableSessionId() != mId) {
+                    throw new IllegalArgumentException("Capture request was created for another " +
+                            "session");
+                }
+            }
+        }
+
+    }
+
+    @Override
     public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
             Handler handler) throws CameraAccessException {
-        if (request == null) {
-            throw new IllegalArgumentException("request must not be null");
-        } else if (request.isReprocess()) {
-            throw new IllegalArgumentException("repeating reprocess requests are not supported");
-        }
+        checkRepeatingRequest(request);
 
         synchronized (mDeviceImpl.mInterfaceLock) {
             checkNotClosed();
@@ -244,20 +300,42 @@
     }
 
     @Override
+    public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        checkRepeatingRequest(request);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
+                        callback + " executor" + " " + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
+    private void checkRepeatingRequest(CaptureRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("request must not be null");
+        } else if (request.isReprocess()) {
+            throw new IllegalArgumentException("repeating reprocess requests are not supported");
+        }
+    }
+
+    @Override
     public int setRepeatingBurst(List<CaptureRequest> requests,
             CaptureCallback callback, Handler handler) throws CameraAccessException {
-        if (requests == null) {
-            throw new IllegalArgumentException("requests must not be null");
-        } else if (requests.isEmpty()) {
-            throw new IllegalArgumentException("requests must have at least one element");
-        }
-
-        for (CaptureRequest r : requests) {
-            if (r.isReprocess()) {
-                throw new IllegalArgumentException("repeating reprocess burst requests are not " +
-                        "supported");
-            }
-        }
+        checkRepeatingRequests(requests);
 
         synchronized (mDeviceImpl.mInterfaceLock) {
             checkNotClosed();
@@ -277,6 +355,48 @@
     }
 
     @Override
+    public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        checkRepeatingRequests(requests);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+                Log.v(TAG, mIdString + "setRepeatingBurst - requests " +
+                        Arrays.toString(requestArray) + ", callback " + callback +
+                        " executor" + "" + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
+    private void checkRepeatingRequests(List<CaptureRequest> requests) {
+        if (requests == null) {
+            throw new IllegalArgumentException("requests must not be null");
+        } else if (requests.isEmpty()) {
+            throw new IllegalArgumentException("requests must have at least one element");
+        }
+
+        for (CaptureRequest r : requests) {
+            if (r.isReprocess()) {
+                throw new IllegalArgumentException("repeating reprocess burst requests are not " +
+                        "supported");
+            }
+        }
+    }
+
+    @Override
     public void stopRepeating() throws CameraAccessException {
         synchronized (mDeviceImpl.mInterfaceLock) {
             checkNotClosed();
@@ -462,6 +582,11 @@
         final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
                 handler) : null;
 
+        return createCaptureCallbackProxyWithExecutor(executor, callback);
+    }
+
+    private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxyWithExecutor(
+            Executor executor, CaptureCallback callback) {
         return new CameraDeviceImpl.CaptureCallback() {
             @Override
             public void onCaptureStarted(CameraDevice camera,
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 89f6172..3494a7f 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -193,6 +193,13 @@
     }
 
     @Override
+    public int captureSingleRequest(CaptureRequest request, Executor executor,
+            CaptureCallback listener) throws CameraAccessException {
+        throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+                + " this method");
+    }
+
+    @Override
     public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
         if (!isConstrainedHighSpeedRequestList(requests)) {
@@ -204,6 +211,17 @@
     }
 
     @Override
+    public int captureBurstRequests(List<CaptureRequest> requests, Executor executor,
+            CaptureCallback listener) throws CameraAccessException {
+        if (!isConstrainedHighSpeedRequestList(requests)) {
+            throw new IllegalArgumentException(
+                "Only request lists created by createHighSpeedRequestList() can be submitted to " +
+                "a constrained high speed capture session");
+        }
+        return mSessionImpl.captureBurstRequests(requests, executor, listener);
+    }
+
+    @Override
     public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
         throw new UnsupportedOperationException("Constrained high speed session doesn't support"
@@ -211,6 +229,13 @@
     }
 
     @Override
+    public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+            CaptureCallback listener) throws CameraAccessException {
+        throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+                + " this method");
+    }
+
+    @Override
     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
         if (!isConstrainedHighSpeedRequestList(requests)) {
@@ -222,6 +247,17 @@
     }
 
     @Override
+    public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor,
+            CaptureCallback listener) throws CameraAccessException {
+        if (!isConstrainedHighSpeedRequestList(requests)) {
+            throw new IllegalArgumentException(
+                "Only request lists created by createHighSpeedRequestList() can be submitted to " +
+                "a constrained high speed capture session");
+        }
+        return mSessionImpl.setRepeatingBurstRequests(requests, executor, listener);
+    }
+
+    @Override
     public void stopRepeating() throws CameraAccessException {
         mSessionImpl.stopRepeating();
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1f35f31..d967fba 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -240,14 +240,14 @@
         }
     };
 
-    public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
+    public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
                         CameraCharacteristics characteristics, int appTargetSdkVersion) {
-        if (cameraId == null || callback == null || handler == null || characteristics == null) {
+        if (cameraId == null || callback == null || executor == null || characteristics == null) {
             throw new IllegalArgumentException("Null argument given");
         }
         mCameraId = cameraId;
         mDeviceCallback = callback;
-        mDeviceExecutor = checkAndWrapHandler(handler);
+        mDeviceExecutor = executor;
         mCharacteristics = characteristics;
         mAppTargetSdkVersion = appTargetSdkVersion;
 
@@ -2349,7 +2349,7 @@
      *
      * <p>If the callback isn't null, check the executor, otherwise pass it through.</p>
      */
-    static <T> Executor checkExecutor(Executor executor, T callback) {
+    public static <T> Executor checkExecutor(Executor executor, T callback) {
         return (callback != null) ? checkExecutor(executor) : executor;
     }
 
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index 1a15962..59dd8cb 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -357,28 +357,6 @@
     public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
 
     /**
-     * The metadata key for a {@link Float} typed value to retrieve the information about the
-     * radio frequency if this metadata represents radio content.
-     *
-     * @see Builder#putFloat(String, float)
-     * @see #getFloat(String)
-     */
-    public static final String METADATA_KEY_RADIO_FREQUENCY =
-            "android.media.metadata.RADIO_FREQUENCY";
-
-    /**
-     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
-     * information about the radio callsign if this metadata represents radio content.
-     *
-     * @see Builder#putText(String, CharSequence)
-     * @see Builder#putString(String, String)
-     * @see #getText(String)
-     * @see #getString(String)
-     */
-    public static final String METADATA_KEY_RADIO_CALLSIGN =
-            "android.media.metadata.RADIO_CALLSIGN";
-
-    /**
      * The metadata key for a {@link Long} typed value to retrieve the information about the
      * bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
      * AVRCP 1.5. It should be one of the following:
@@ -502,7 +480,7 @@
             METADATA_KEY_DATE, METADATA_KEY_GENRE, METADATA_KEY_ALBUM_ARTIST, METADATA_KEY_ART_URI,
             METADATA_KEY_ALBUM_ART_URI, METADATA_KEY_DISPLAY_TITLE, METADATA_KEY_DISPLAY_SUBTITLE,
             METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_KEY_DISPLAY_ICON_URI,
-            METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI, METADATA_KEY_RADIO_CALLSIGN})
+            METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI})
     @Retention(RetentionPolicy.SOURCE)
     public @interface TextKey {}
 
@@ -532,7 +510,7 @@
     /**
      * @hide
      */
-    @StringDef({METADATA_KEY_RADIO_FREQUENCY})
+    // TODO(jaewan): Add predefined float key.
     @Retention(RetentionPolicy.SOURCE)
     public @interface FloatKey {}
 
@@ -741,7 +719,6 @@
          * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
          * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
          * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
-         * <li>{@link #METADATA_KEY_RADIO_CALLSIGN}</li>
          * </ul>
          *
          * @param key The key for referencing this value
@@ -773,7 +750,6 @@
          * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
          * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
          * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
-         * <li>{@link #METADATA_KEY_RADIO_CALLSIGN}</li>
          * </ul>
          *
          * @param key The key for referencing this value
@@ -848,12 +824,7 @@
         }
 
         /**
-         * Put a float value into the metadata. Custom keys may be used, but if
-         * the METADATA_KEYs defined in this class are used they may only be one
-         * of the following:
-         * <ul>
-         * <li>{@link #METADATA_KEY_RADIO_FREQUENCY}</li>
-         * </ul>
+         * Put a float value into the metadata. Custom keys may be used.
          *
          * @param key The key for referencing this value
          * @param value The float value to store
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index 1fcf02b..5c08f19 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -337,6 +337,13 @@
          */
         public void onBufferingStateChanged(@NonNull MediaPlayerBase mpb,
                 @NonNull DataSourceDesc dsd, @BuffState int state) { }
+
+        /**
+         * Called to indicate that the playback speed has changed.
+         * @param mpb the player that is buffering
+         * @param speed the new playback speed.
+         */
+        public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
     }
 
 }
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 0f4b5da..2033c59 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -16,7 +16,7 @@
 
 package android.media;
 
-import static android.media.MediaPlayerBase.PLAYER_STATE_IDLE;
+import static android.media.MediaPlayerBase.BUFFERING_STATE_UNKNOWN;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.media.MediaPlayerBase.BuffState;
-import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaPlayerBase.PlayerState;
 import android.media.MediaPlaylistAgent.RepeatMode;
 import android.media.MediaPlaylistAgent.ShuffleMode;
@@ -46,6 +45,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -117,18 +117,20 @@
     /**
      * Command code for {@link MediaController2#skipToNextItem()}.
      * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the {@link SessionCallback#onCommandRequest(
+     * MediaSession2, ControllerInfo, Command)}.
      */
-    public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4;
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
 
     /**
      * Command code for {@link MediaController2#skipToPreviousItem()}.
      * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the {@link SessionCallback#onCommandRequest(
+     * MediaSession2, ControllerInfo, Command)}.
      */
-    public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5;
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
 
     /**
      * Command code for {@link MediaController2#prepare()}.
@@ -276,48 +278,60 @@
     /**
      * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
      */
-    public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 22;
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
 
     /**
      * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
      */
-    public static final int COMMAND_CODE_PLAY_FROM_URI = 23;
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
 
     /**
      * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
      */
-    public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 24;
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
 
     /**
      * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
      */
-    public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 25;
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
 
     /**
      * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
      */
-    public static final int COMMAND_CODE_PREPARE_FROM_URI = 26;
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
 
     /**
      * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
      */
-    public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 27;
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
 
     /**
      * Command code for {@link MediaController2#setRating(String, Rating2)}.
      * @hide
      */
-    // TODO(jaewan): Unhide
-    public static final int COMMAND_CODE_SET_RATING = 29;
+    public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
 
     /**
-     * Command code for {@link MediaBrowser2} specific functions that allows navigation and search
-     * from the {@link MediaLibraryService2}. This would be ignored for a {@link MediaSession2},
-     * not {@link android.media.MediaLibraryService2.MediaLibrarySession}.
+     * Command code for {@link android.media.MediaLibraryService2.MediaLibrarySession} specific
+     * functions. With or without this, a {@link MediaSession2} that isn't
+     * {@link android.media.MediaLibraryService2.MediaLibrarySession} would automatically reject
+     * the calls.
      *
+     * @see android.media.MediaLibraryService2.MediaLibrarySession
      * @see MediaBrowser2
+     * @hide
      */
-    public static final int COMMAND_CODE_BROWSER = 28;
+    // TODO(jaewan): Remove
+    public static final int COMMAND_CODE_BROWSER = 29;
+
+    // TODO(jaewan): Add javadoc
+    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
+    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
+    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
+    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
+    public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
+    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
+    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
 
     /**
      * @hide
@@ -520,6 +534,10 @@
             mProvider.addCommand_impl(command);
         }
 
+        public void addCommand(int commandCode) {
+            // TODO(jaewna): Implement
+        }
+
         public void addAllPredefinedCommands() {
             mProvider.addAllPredefinedCommands_impl();
         }
@@ -528,6 +546,10 @@
             mProvider.removeCommand_impl(command);
         }
 
+        public void removeCommand(int commandCode) {
+            // TODO(jaewan): Implement.
+        }
+
         public boolean hasCommand(@NonNull Command command) {
             return mProvider.hasCommand_impl(command);
         }
@@ -536,7 +558,7 @@
             return mProvider.hasCommand_impl(code);
         }
 
-        public @NonNull List<Command> getCommands() {
+        public @NonNull Set<Command> getCommands() {
             return mProvider.getCommands_impl();
         }
 
@@ -620,8 +642,8 @@
          * @see #COMMAND_CODE_PLAYBACK_PLAY
          * @see #COMMAND_CODE_PLAYBACK_PAUSE
          * @see #COMMAND_CODE_PLAYBACK_STOP
-         * @see #COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM
-         * @see #COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM
+         * @see #COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
+         * @see #COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
          * @see #COMMAND_CODE_PLAYBACK_PREPARE
          * @see #COMMAND_CODE_PLAYBACK_FAST_FORWARD
          * @see #COMMAND_CODE_PLAYBACK_REWIND
@@ -676,7 +698,7 @@
          * @param controller controller information
          * @param mediaId media id
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_PLAY_FROM_MEDIA_ID
+         * @see #COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
          */
         public void onPlayFromMediaId(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String mediaId,
@@ -693,7 +715,7 @@
          * @param controller controller information
          * @param query query string. Can be empty to indicate any suggested media
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_PLAY_FROM_SEARCH
+         * @see #COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
          */
         public void onPlayFromSearch(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String query,
@@ -707,7 +729,7 @@
          * @param controller controller information
          * @param uri uri
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_PLAY_FROM_URI
+         * @see #COMMAND_CODE_SESSION_PLAY_FROM_URI
          */
         public void onPlayFromUri(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Uri uri,
@@ -731,7 +753,7 @@
          * @param controller controller information
          * @param mediaId media id to prepare
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_PREPARE_FROM_MEDIA_ID
+         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
          */
         public void onPrepareFromMediaId(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String mediaId,
@@ -755,7 +777,7 @@
          * @param controller controller information
          * @param query query string. Can be empty to indicate any suggested media
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_PREPARE_FROM_SEARCH
+         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
          */
         public void onPrepareFromSearch(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String query,
@@ -779,7 +801,7 @@
          * @param controller controller information
          * @param uri uri
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_PREPARE_FROM_URI
+         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_URI
          */
         public void onPrepareFromUri(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Uri uri, @Nullable Bundle extras) { }
@@ -830,6 +852,15 @@
                 @NonNull MediaPlayerBase player, @NonNull MediaItem2 item, @BuffState int state) { }
 
         /**
+         * Called to indicate that the playback speed has changed.
+         * @param session the session for this event
+         * @param player the player for this event
+         * @param speed the new playback speed.
+         */
+        public void onPlaybackSpeedChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlayerBase player, float speed) { }
+
+        /**
          * Called when a playlist is changed from the {@link MediaPlaylistAgent}.
          * <p>
          * This is called when the underlying agent has called
@@ -1467,9 +1498,7 @@
      * Gets the current player state.
      *
      * @return the current player state
-     * @hide
      */
-    // TODO(jaewan): Unhide (b/74578458)
     public @PlayerState int getPlayerState() {
         return mProvider.getPlayerState_impl();
     }
@@ -1479,25 +1508,33 @@
      *
      * @return the current playback position in ms, or {@link MediaPlayerBase#UNKNOWN_TIME} if
      *         unknown.
-     * @hide
      */
-    // TODO(jaewan): Unhide (b/74578458)
-    public long getPosition() {
-        return mProvider.getPosition_impl();
+    public long getCurrentPosition() {
+        return mProvider.getCurrentPosition_impl();
     }
 
     /**
      * Gets the buffered position, or {@link MediaPlayerBase#UNKNOWN_TIME} if unknown.
      *
      * @return the buffered position in ms, or {@link MediaPlayerBase#UNKNOWN_TIME}.
-     * @hide
      */
-    // TODO(jaewan): Unhide (b/74578458)
     public long getBufferedPosition() {
         return mProvider.getBufferedPosition_impl();
     }
 
     /**
+     * Gets the current buffering state of the player.
+     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+     * buffered.
+     *
+     * @return the buffering state.
+     */
+    public @BuffState int getBufferingState() {
+        // TODO(jaewan): Implement this
+        return BUFFERING_STATE_UNKNOWN;
+    }
+
+    /**
      * Get the playback speed.
      *
      * @return speed
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index 0faed9d..5a1db3e 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -36,6 +36,7 @@
 import android.os.ResultReceiver;
 
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -66,7 +67,7 @@
     MediaItem2 getCurrentPlaylistItem_impl();
     void notifyError_impl(int errorCode, Bundle extras);
     int getPlayerState_impl();
-    long getPosition_impl();
+    long getCurrentPosition_impl();
     long getBufferedPosition_impl();
     void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
     void clearOnDataSourceMissingHelper_impl();
@@ -87,7 +88,7 @@
         void removeCommand_impl(Command command);
         boolean hasCommand_impl(Command command);
         boolean hasCommand_impl(int code);
-        List<Command> getCommands_impl();
+        Set<Command> getCommands_impl();
         Bundle toBundle_impl();
     }
 
diff --git a/packages/Osu2/Android.mk b/packages/Osu2/Android.mk
index 063ac7e..7de8908 100644
--- a/packages/Osu2/Android.mk
+++ b/packages/Osu2/Android.mk
@@ -12,6 +12,7 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
 LOCAL_PRIVILEGED_MODULE := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 include $(BUILD_PACKAGE)
 
diff --git a/packages/Osu2/tests/AndroidTest.xml b/packages/Osu2/tests/AndroidTest.xml
new file mode 100644
index 0000000..9514dab
--- /dev/null
+++ b/packages/Osu2/tests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!-- This test config file is auto-generated. -->
+<configuration description="Runs OSU App Tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="Osu2.apk" />
+        <option name="test-file-name" value="OsuTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.osu.tests" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
index 85a325a..e848901 100644
--- a/packages/SystemUI/res/layout/wireless_charging_layout.xml
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -42,7 +42,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
-            android:textSize="32sp"
+            android:textSize="24sp"
             android:textColor="?attr/wallpaperTextColor"/>
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4513f14..ff3f696 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -929,14 +929,14 @@
     <dimen name="wireless_charging_dots_radius_start">0dp</dimen>
     <dimen name="wireless_charging_dots_radius_end">4dp</dimen>
     <dimen name="wireless_charging_circle_radius_start">28dp</dimen>
-    <dimen name="wireless_charging_circle_radius_end">92dp</dimen>
+    <dimen name="wireless_charging_circle_radius_end">84dp</dimen>
     <integer name="wireless_charging_angle_offset">20</integer>
     <integer name="wireless_charging_scale_dots_duration">83</integer>
-    <integer name="wireless_charging_num_dots">20</integer>
-    <!-- Starting text size in dp of batteryLevel for wireless charging animation -->
-    <dimen name="wireless_charging_anim_battery_level_text_size_start">0dp</dimen>
-    <!-- Ending text size in dp of batteryLevel for wireless charging animation -->
-    <dimen name="wireless_charging_anim_battery_level_text_size_end">14dp</dimen>
+    <integer name="wireless_charging_num_dots">16</integer>
+    <!-- Starting text size in sp of batteryLevel for wireless charging animation -->
+    <item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen">0</item>
+    <!-- Ending text size in sp of batteryLevel for wireless charging animation -->
+    <item name="wireless_charging_anim_battery_level_text_size_end" format="float" type="dimen">24</item>
     <!-- time until battery info is at full opacity-->
     <integer name="wireless_charging_anim_opacity_offset">80</integer>
     <!-- duration batteryLevel opacity goes from 0 to 1 duration -->
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index afc9629..e11fe4e 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -55,9 +55,9 @@
      * @hide
      */
     public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int
-            batteryLevel, Callback callback) {
+            batteryLevel, Callback callback, boolean isDozing) {
         mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
-                batteryLevel, callback);
+                batteryLevel, callback, isDozing);
     }
 
     /**
@@ -65,8 +65,8 @@
      * @hide
      */
     public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
-            @Nullable Looper looper, int batteryLevel, Callback callback) {
-        return new WirelessChargingAnimation(context, looper, batteryLevel, callback);
+            @Nullable Looper looper, int batteryLevel, Callback callback, boolean isDozing) {
+        return new WirelessChargingAnimation(context, looper, batteryLevel, callback, isDozing);
     }
 
     /**
@@ -102,14 +102,14 @@
         private Callback mCallback;
 
         public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel,
-                Callback callback) {
+                Callback callback, boolean isDozing) {
             mCallback = callback;
-            mNextView = new WirelessChargingLayout(context, batteryLevel);
+            mNextView = new WirelessChargingLayout(context, batteryLevel, isDozing);
             mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
 
             final WindowManager.LayoutParams params = mParams;
             params.height = WindowManager.LayoutParams.WRAP_CONTENT;
-            params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+            params.width = WindowManager.LayoutParams.MATCH_PARENT;
             params.format = PixelFormat.TRANSLUCENT;
 
             params.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 9887533..c8e83de 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -20,6 +20,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Color;
 import android.util.AttributeSet;
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
@@ -38,24 +39,24 @@
 
     public WirelessChargingLayout(Context context) {
         super(context);
-        init(context, null);
+        init(context, null, false);
     }
 
-    public WirelessChargingLayout(Context context, int batterylLevel) {
+    public WirelessChargingLayout(Context context, int batteryLevel, boolean isDozing) {
         super(context);
-        init(context, null, batterylLevel);
+        init(context, null, batteryLevel, isDozing);
     }
 
     public WirelessChargingLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
-        init(context, attrs);
+        init(context, attrs, false);
     }
 
-    private void init(Context c, AttributeSet attrs) {
-        init(c, attrs, -1);
+    private void init(Context c, AttributeSet attrs, boolean isDozing) {
+        init(c, attrs, -1, false);
     }
 
-    private void init(Context context, AttributeSet attrs, int batteryLevel) {
+    private void init(Context context, AttributeSet attrs, int batteryLevel, boolean isDozing) {
         final int mBatteryLevel = batteryLevel;
         inflate(context, R.layout.wireless_charging_layout, this);
 
@@ -65,6 +66,11 @@
         // amount of battery:
         final TextView mPercentage = findViewById(R.id.wireless_charging_percentage);
 
+        if (isDozing) {
+            mChargingView.setPaintColor(Color.WHITE);
+            mPercentage.setTextColor(Color.WHITE);
+        }
+
         if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
             mPercentage.setText(NumberFormat.getPercentInstance().format(mBatteryLevel / 100f));
             mPercentage.setAlpha(0);
@@ -74,9 +80,9 @@
                 R.integer.wireless_charging_fade_offset);
         final long chargingAnimationFadeDuration = (long) context.getResources().getInteger(
                 R.integer.wireless_charging_fade_duration);
-        final int batteryLevelTextSizeStart = context.getResources().getDimensionPixelSize(
+        final float batteryLevelTextSizeStart = context.getResources().getFloat(
                 R.dimen.wireless_charging_anim_battery_level_text_size_start);
-        final int batteryLevelTextSizeEnd = context.getResources().getDimensionPixelSize(
+        final float batteryLevelTextSizeEnd = context.getResources().getFloat(
                 R.dimen.wireless_charging_anim_battery_level_text_size_end);
 
         // Animation Scale: battery percentage text scales from 0% to 100%
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
index 19c6dc1..9c411d6 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
@@ -98,6 +98,10 @@
         mPaint.setColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
     }
 
+    public void setPaintColor(int color) {
+        mPaint.setColor(color);
+    }
+
     @Override
     protected void onDraw(final Canvas canvas) {
         super.onDraw(canvas);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e6a9b46..8cd6295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2502,11 +2502,11 @@
                         public void onAnimationEnded() {
                             CrossFadeHelper.fadeIn(mNotificationPanel);
                         }
-                    }).show();
+                    }, mDozing).show();
         } else {
             // workspace
             WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
-                    batteryLevel, null).show();
+                    batteryLevel, null, false).show();
         }
     }
 
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 02b1380..2167941 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -632,23 +632,14 @@
             if (DBG) {
                 Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
             }
-            if (isBleAppPresent()) {
-                // Nothing to do, another app is here.
-                return;
-            }
-            if (DBG) {
-                Slog.d(TAG, "Disabling LE only mode after application crash");
-            }
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
-                    mEnable = false;
-                    mBluetooth.onBrEdrDown();
+
+            for (Map.Entry<IBinder, ClientDeathRecipient> entry : mBleApps.entrySet()) {
+                IBinder token = entry.getKey();
+                ClientDeathRecipient deathRec = entry.getValue();
+                if (deathRec.equals(this)) {
+                    updateBleAppCount(token, false, mPackageName);
+                    break;
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to call onBrEdrDown", e);
-            } finally {
-                mBluetoothLock.readLock().unlock();
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 27eeb93..59cd561 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1877,13 +1877,13 @@
         return newSuppressedVisualEffects;
     }
 
-    // TODO: log visual differences, not just audible ones
     @GuardedBy("mNotificationLock")
     protected void maybeRecordInterruptionLocked(NotificationRecord r) {
         if (r.isInterruptive()) {
             mAppUsageStats.reportInterruptiveNotification(r.sbn.getPackageName(),
                     r.getChannel().getId(),
                     getRealUserId(r.sbn.getUserId()));
+            logRecentLocked(r);
         }
     }
 
@@ -4344,10 +4344,6 @@
 
                     mNotificationsByKey.put(n.getKey(), r);
 
-                    if (!r.isUpdate) {
-                        logRecentLocked(r);
-                    }
-
                     // Ensure if this is a foreground service that the proper additional
                     // flags are set.
                     if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c965aec..950d8df 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import static android.Manifest.permission.DELETE_PACKAGES;
+import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
 import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
 import static android.Manifest.permission.INSTALL_PACKAGES;
 import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
@@ -115,6 +116,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
@@ -24315,6 +24317,23 @@
             return mSettings.getHarmfulAppWarningLPr(packageName, userId);
         }
     }
+
+    @Override
+    public boolean isPackageStateProtected(@NonNull String packageName, @UserIdInt int userId) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingAppId = UserHandle.getAppId(callingUid);
+
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
+                false /*requireFullPermission*/, true /*checkShell*/, "isPackageStateProtected");
+
+        if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID
+                && checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid) != PERMISSION_GRANTED) {
+            throw new SecurityException("Caller must have the "
+                    + MANAGE_DEVICE_ADMINS + " permission.");
+        }
+
+        return mProtectedPackages.isPackageStateProtected(userId, packageName);
+    }
 }
 
 interface PackageSender {