Merge "Show more logs when switching users and IMMS#DEBUG==true." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 3c878c8..481e651 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4384,6 +4384,7 @@
method public void setSharedElementReturnTransition(android.transition.Transition);
method public void setTargetFragment(android.app.Fragment, int);
method public void setUserVisibleHint(boolean);
+ method public boolean shouldShowRequestPermissionRationale(java.lang.String);
method public void startActivity(android.content.Intent);
method public void startActivity(android.content.Intent, android.os.Bundle);
method public void startActivityForResult(android.content.Intent, int);
@@ -13191,6 +13192,7 @@
method public abstract void close();
method public abstract android.hardware.camera2.CameraDevice getDevice();
method public abstract android.view.Surface getInputSurface();
+ method public abstract boolean isConstrainedHighSpeed();
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;
@@ -13311,6 +13313,8 @@
method public abstract void close();
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createConstrainedHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
@@ -13441,7 +13445,7 @@
field public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; // 0x1
field public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; // 0xc
field public static final int CONTROL_SCENE_MODE_HDR = 18; // 0x12
- field public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
+ field public static final deprecated int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
field public static final int CONTROL_SCENE_MODE_LANDSCAPE = 4; // 0x4
field public static final int CONTROL_SCENE_MODE_NIGHT = 5; // 0x5
field public static final int CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6; // 0x6
@@ -13488,6 +13492,7 @@
field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
@@ -32454,7 +32459,7 @@
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
method public android.text.StaticLayout.Builder setMaxLines(int);
method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
- method public android.text.StaticLayout.Builder setTextDir(android.text.TextDirectionHeuristic);
+ method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
}
public abstract interface TextDirectionHeuristic {
diff --git a/api/system-current.txt b/api/system-current.txt
index 65ca29a..32af01c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4480,6 +4480,7 @@
method public void setSharedElementReturnTransition(android.transition.Transition);
method public void setTargetFragment(android.app.Fragment, int);
method public void setUserVisibleHint(boolean);
+ method public boolean shouldShowRequestPermissionRationale(java.lang.String);
method public void startActivity(android.content.Intent);
method public void startActivity(android.content.Intent, android.os.Bundle);
method public void startActivityForResult(android.content.Intent, int);
@@ -13509,6 +13510,7 @@
method public abstract void close();
method public abstract android.hardware.camera2.CameraDevice getDevice();
method public abstract android.view.Surface getInputSurface();
+ method public abstract boolean isConstrainedHighSpeed();
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;
@@ -13629,6 +13631,8 @@
method public abstract void close();
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createConstrainedHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
@@ -13759,7 +13763,7 @@
field public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; // 0x1
field public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; // 0xc
field public static final int CONTROL_SCENE_MODE_HDR = 18; // 0x12
- field public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
+ field public static final deprecated int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
field public static final int CONTROL_SCENE_MODE_LANDSCAPE = 4; // 0x4
field public static final int CONTROL_SCENE_MODE_NIGHT = 5; // 0x5
field public static final int CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6; // 0x6
@@ -13806,6 +13810,7 @@
field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
@@ -34719,7 +34724,7 @@
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
method public android.text.StaticLayout.Builder setMaxLines(int);
method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
- method public android.text.StaticLayout.Builder setTextDir(android.text.TextDirectionHeuristic);
+ method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
}
public abstract interface TextDirectionHeuristic {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 40c5c64..26d4fd4 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1223,6 +1223,33 @@
}
/**
+ * Gets whether you should show UI with rationale for requesting a permission.
+ * You should do this only if you do not have the permission and the context in
+ * which the permission is requested does not clearly communicate to the user
+ * what would be the benefit from granting this permission.
+ * <p>
+ * For example, if you write a camera app, requesting the camera permission
+ * would be expected by the user and no rationale for why it is requested is
+ * needed. If however, the app needs location for tagging photos then a non-tech
+ * savvy user may wonder how location is related to taking photos. In this case
+ * you may choose to show UI with rationale of requesting this permission.
+ * </p>
+ *
+ * @param permission A permission your app wants to request.
+ * @return Whether you can show permission rationale UI.
+ *
+ * @see Context#checkSelfPermission(String)
+ * @see #requestPermissions(String[], int)
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ */
+ public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
+ if (mHost != null) {
+ mHost.getContext().getPackageManager().shouldShowRequestPermissionRationale(permission);
+ }
+ return false;
+ }
+
+ /**
* @hide Hack so that DialogFragment can make its Dialog before creating
* its views, and the view construction can use the dialog's context for
* inflation. Maybe this should become a public API. Note sure.
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index c22ee5f..82d40d3 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -471,6 +471,17 @@
public abstract boolean isReprocessable();
/**
+ * Return if this capture session is constrained high speed session that is created by
+ * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}.
+ *
+ * @return {@code true} if this session is constrained high speed capture session,
+ * {@code false} otherwise.
+ *
+ * @see CameraDevice#createConstrainedHighSpeedCaptureSession
+ */
+ public abstract boolean isConstrainedHighSpeed();
+
+ /**
* Get the input Surface associated with a reprocessable capture session.
*
* <p>Each reprocessable capture session has an input {@link Surface} where the reprocess
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d5867a9..85e8827 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -626,35 +626,54 @@
new Key<Integer>("android.control.maxRegionsAf", int.class);
/**
- * <p>List of available high speed video size and fps range configurations
- * supported by the camera device, in the format of (width, height, fps_min, fps_max).</p>
- * <p>When HIGH_SPEED_VIDEO is supported in {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}, this metadata
- * will list the supported high speed video size and fps range configurations. All the sizes
- * listed in this configuration will be a subset of the sizes reported by {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } for processed
- * non-stalling formats.</p>
- * <p>For the high speed video use case, where the application will set
- * {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the application must
+ * <p>List of available high speed video size, fps range and max batch size configurations
+ * supported by the camera device, in the format of (width, height, fps_min, fps_max, batch_size_max).</p>
+ * <p>When CONSTRAINED_HIGH_SPEED_VIDEO is supported in android.control.availableCapabilities,
+ * this metadata will list the supported high speed video size, fps range and max batch size
+ * configurations. All the sizes listed in this configuration will be a subset of the sizes
+ * reported by {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes }
+ * for processed non-stalling formats.</p>
+ * <p>For the high speed video use case, the application must
* select the video size and fps range from this metadata to configure the recording and
* preview streams and setup the recording requests. For example, if the application intends
* to do high speed recording, it can select the maximum size reported by this metadata to
* configure output streams. Once the size is selected, application can filter this metadata
* by selected size and get the supported fps ranges, and use these fps ranges to setup the
* recording requests. Note that for the use case of multiple output streams, application
- * must select one unique size from this metadata to use. Otherwise a request error might
- * occur.</p>
- * <p>For normal video recording use case, where some application will NOT set
- * {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the fps ranges
- * reported in this metadata must not be used to setup capture requests, or it will cause
- * request error.</p>
+ * must select one unique size from this metadata to use (e.g., preview and recording streams
+ * must have the same size). Otherwise, the high speed capture session creation will fail.</p>
+ * <p>The min and max fps will be multiple times of 30fps.</p>
+ * <p>High speed video streaming extends significant performance pressue to camera hardware,
+ * to achieve efficient high speed streaming, the camera device may have to aggregate
+ * multiple frames together and send to camera device for processing where the request
+ * controls are same for all the frames in this batch. Max batch size indicates
+ * the max possible number of frames the camera device will group together for this high
+ * speed stream configuration. This max batch size will be used to generate a high speed
+ * recording request list by
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }.
+ * The max batch size for each configuration will satisfy below conditions:</p>
+ * <ul>
+ * <li>Each max batch size will be a divisor of its corresponding fps_max / 30. For example,
+ * if max_fps is 300, max batch size will only be 1, 2, 5, or 10.</li>
+ * <li>The camera device may choose smaller internal batch size for each configuration, but
+ * the actual batch size will be a divisor of max batch size. For example, if the max batch
+ * size is 8, the actual batch size used by camera device will only be 1, 2, 4, or 8.</li>
+ * <li>The max batch size in each configuration entry must be no larger than 32.</li>
+ * </ul>
+ * <p>The camera device doesn't have to support batch mode to achieve high speed video recording,
+ * in such case, batch_size_max will be reported as 1 in each configuration entry.</p>
+ * <p>This fps ranges in this configuration list can only be used to create requests
+ * that are submitted to a high speed camera capture session created by
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }.
+ * The fps ranges reported in this metadata must not be used to setup capture requests for
+ * normal capture session, or it will cause request error.</p>
* <p><b>Range of valid values:</b><br></p>
- * <p>For each configuration, the fps_max >= 60fps.</p>
+ * <p>For each configuration, the fps_max >= 120fps.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
* Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
- * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES
- * @see CaptureRequest#CONTROL_SCENE_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @hide
*/
@@ -1308,7 +1327,8 @@
* the max pipeline depth.</p>
* <p>A pipeline depth of X stages is equivalent to a pipeline latency of
* X frame intervals.</p>
- * <p>This value will be 8 or less.</p>
+ * <p>This value will normally be 8 or less, however, for high speed capture session,
+ * the max pipeline depth will be up to 8 x size of high speed capture request list.</p>
* <p>This key is available on all devices.</p>
*
* @see CaptureResult#REQUEST_PIPELINE_DEPTH
@@ -1371,6 +1391,7 @@
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -1384,6 +1405,7 @@
* @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
* @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
*/
@PublicKey
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index d02f349..006030c 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -583,6 +583,147 @@
throws CameraAccessException;
/**
+ * <p>Create a new constrained high speed capture session.</p>
+ *
+ * <p>The application can use normal capture session (created via {@link #createCaptureSession})
+ * for high speed capture if the desired high speed FPS ranges are advertised by
+ * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}, in which case all API
+ * semantics associated with normal capture sessions applies.</p>
+ *
+ * <p>The method creates a specialized capture session that is only targeted at high speed
+ * video recording (>=120fps) use case if the camera device supports high speed video
+ * capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} contains
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}).
+ * Therefore, it has special characteristics compared with a normal capture session:</p>
+ *
+ * <ul>
+ *
+ * <li>In addition to the output target Surface requirements specified by the
+ * {@link #createCaptureSession} method, an active high speed capture session will support up
+ * to 2 output Surfaces, though the application might choose to configure just one Surface
+ * (e.g., preview only). All Surfaces must be either video encoder surfaces (acquired by
+ * {@link android.media.MediaRecorder#getSurface} or
+ * {@link android.media.MediaCodec#createInputSurface}) or preview surfaces (obtained from
+ * {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture} via
+ * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). The Surface sizes
+ * must be one of the sizes reported by {@link StreamConfigurationMap#getHighSpeedVideoSizes}.
+ * When multiple Surfaces are configured, their size must be same.</li>
+ *
+ * <li>An active high speed capture session only accepts request lists created via
+ * {@link #createConstrainedHighSpeedRequestList}, and the request list can only be submitted
+ * to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or
+ * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}.</li>
+ *
+ * <li>The FPS ranges being requested to this session must be selected from
+ * {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor}. The application can still use
+ * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE} to control the desired FPS range.
+ * Switching to an FPS range that has different
+ * {@link android.util.Range#getUpper() maximum FPS} may trigger some camera device
+ * reconfigurations, which may introduce extra latency. It is recommended that the
+ * application avoids unnecessary maximum target FPS changes as much as possible during high
+ * speed streaming.</li>
+ *
+ * <li>For the request lists submitted to this session, the camera device will override the
+ * {@link CaptureRequest#CONTROL_MODE control mode}, auto-exposure (AE), auto-white balance
+ * (AWB) and auto-focus (AF) to {@link CameraMetadata#CONTROL_MODE_AUTO},
+ * {@link CameraMetadata#CONTROL_AE_MODE_ON}, {@link CameraMetadata#CONTROL_AWB_MODE_AUTO}
+ * and {@link CameraMetadata#CONTROL_AF_MODE_CONTINUOUS_VIDEO}, respectively. All
+ * post-processing block mode controls will be overridden to be FAST. Therefore, no manual
+ * control of capture and post-processing parameters is possible. Beside these, only a subset
+ * of controls will work, see
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO} for
+ * more details.</li>
+ *
+ * </ul>
+ *
+ * @param outputs The new set of Surfaces that should be made available as
+ * targets for captured high speed image data.
+ * @param callback The callback to notify about the status of the new capture session.
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+ * the callback is null, or the handler is null but the current
+ * thread has no looper, or the camera device doesn't support
+ * high speed video capability.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #createCaptureSession
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see StreamConfigurationMap#getHighSpeedVideoSizes
+ * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+ * @see CameraCaptureSession#captureBurst
+ * @see CameraCaptureSession#setRepeatingBurst
+ * @see #createConstrainedHighSpeedRequestList
+ */
+ public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs,
+ @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
+ throws CameraAccessException;
+
+
+ /**
+ * <p>Create a unmodifiable list of requests that is suitable for constrained high speed capture
+ * session streaming.</p>
+ *
+ * <p>High speed video streaming creates significant performance pressue on the camera device,
+ * so to achieve efficient high speed streaming, the camera device may have to aggregate
+ * multiple frames together. This means requests must be sent in batched groups, with all
+ * requests sharing the same settings. This method takes the list of output target
+ * Surfaces (subject to the output Surface requirements specified by the contrained high speed
+ * session) and a {@link CaptureRequest request}, and generates a request list that has the same
+ * controls for each request. The input {@link CaptureRequest request} must contain the target
+ * output Surfaces and target high speed FPS range that is one of the
+ * {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor} for the Surface size.</p>
+ *
+ * <p>If both preview and recording Surfaces are specified in the {@code request}, the
+ * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE target FPS range} in the input
+ * {@link CaptureRequest request} must be a fixed framerate FPS range, where the
+ * {@link android.util.Range#getLower minimal FPS} ==
+ * {@link android.util.Range#getUpper() maximum FPS}. The created request list will contain
+ * a interleaved request pattern such that the preview output FPS is at least 30fps, the
+ * recording output FPS is {@link android.util.Range#getUpper() maximum FPS} of the requested
+ * FPS range. The application can submit this request list directly to an active high speed
+ * capture session to achieve high speed video recording. When only preview or recording
+ * Surface is specified, this method will return a list of request that have the same controls
+ * and output targets for all requests.</p>
+ *
+ * <p>Submitting a request list created by this method to a normal capture session will result
+ * in an {@link IllegalArgumentException} if the high speed
+ * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} is not supported by
+ * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}.</p>
+ *
+ * @param request The high speed capture request that will be used to generate the high speed
+ * request list.
+ * @return A unmodifiable CaptureRequest list that is suitable for constrained high speed
+ * capture.
+ *
+ * @throws IllegalArgumentException if the set of output Surfaces in the request do not meet the
+ * high speed video capability requirements, or the camera
+ * device doesn't support high speed video capability, or the
+ * request doesn't meet the high speed video capability
+ * requirements, or the request doesn't contain the required
+ * controls for high speed capture.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #createConstrainedHighSpeedCaptureSession
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see StreamConfigurationMap#getHighSpeedVideoSizes
+ * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+ */
+ @NonNull
+ public abstract List<CaptureRequest> createConstrainedHighSpeedRequestList(
+ @NonNull CaptureRequest request)throws CameraAccessException;
+
+ /**
* <p>Create a {@link CaptureRequest.Builder} for new capture requests,
* initialized with template for a target use case. The settings are chosen
* to be the best options for the specific camera device, so it is not
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ac29f80..f8db6d9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -648,6 +648,100 @@
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
+ /**
+ * <p>The device supports constrained high speed video recording (frame rate >=120fps)
+ * use case. The camera device will support high speed capture session created by
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }, which
+ * only accepts high speed request list created by
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }.</p>
+ * <p>A camera device can still support high speed video streaming by advertising the high speed
+ * FPS ranges in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}. For this case, all normal
+ * capture request per frame control and synchronization requirements will apply to
+ * the high speed fps ranges, the same as all other fps ranges. This capability describes
+ * the capability of a specialized operating mode with many limitations (see below), which
+ * is only targeted at high speed video recording.</p>
+ * <p>The supported high speed video sizes and fps ranges are specified in
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.
+ * To get desired output frame rates, the application is only allowed to select video size
+ * and FPS range combinations provided by
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.
+ * The fps range can be controlled via {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}.</p>
+ * <p>In this capability, the camera device will override aeMode, awbMode, and afMode to
+ * ON, ON, and CONTINUOUS_VIDEO, respectively. All post-processing block mode
+ * controls will be overridden to be FAST. Therefore, no manual control of capture
+ * and post-processing parameters is possible. All other controls operate the
+ * same as when {@link CaptureRequest#CONTROL_MODE android.control.mode} == AUTO. This means that all other
+ * android.control.* fields continue to work, such as</p>
+ * <ul>
+ * <li>{@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</li>
+ * <li>{@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</li>
+ * <li>{@link CaptureRequest#CONTROL_EFFECT_MODE android.control.effectMode}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}</li>
+ * </ul>
+ * <p>Outside of android.control.*, the following controls will work:</p>
+ * <ul>
+ * <li>{@link CaptureRequest#FLASH_MODE android.flash.mode} (TORCH mode only, automatic flash for still capture will not
+ * work since aeMode is ON)</li>
+ * <li>{@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode} (if it is supported)</li>
+ * <li>{@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}</li>
+ * <li>{@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} (if it is supported)</li>
+ * </ul>
+ * <p>For high speed recording use case, the actual maximum supported frame rate may
+ * be lower than what camera can output, depending on the destination Surfaces for
+ * the image data. For example, if the destination surface is from video encoder,
+ * the application need check if the video encoder is capable of supporting the
+ * high frame rate for a given video size, or it will end up with lower recording
+ * frame rate. If the destination surface is from preview window, the actual preview frame
+ * rate will be bounded by the screen refresh rate.</p>
+ * <p>The camera device will only support up to 2 high speed simultaneous output surfaces
+ * (preview and recording surfaces)
+ * in this mode. Above controls will be effective only if all of below conditions are true:</p>
+ * <ul>
+ * <li>The application creates a camera capture session with no more than 2 surfaces via
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }. The
+ * targeted surfaces must be preview surface (either from
+ * {@link android.view.SurfaceView } or {@link android.graphics.SurfaceTexture }) or
+ * recording surface(either from {@link android.media.MediaRecorder#getSurface } or
+ * {@link android.media.MediaCodec#createInputSurface }).</li>
+ * <li>The stream sizes are selected from the sizes reported by
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.</li>
+ * <li>The FPS ranges are selected from
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li>
+ * </ul>
+ * <p>When above conditions are NOT satistied, the
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
+ * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList } will fail.</p>
+ * <p>Switching to a FPS range that has different maximum FPS may trigger some camera device
+ * reconfigurations, which may introduce extra latency. It is recommended that
+ * the application avoids unnecessary maximum target FPS changes as much as possible
+ * during high speed streaming.</p>
+ *
+ * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
+ * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION
+ * @see CaptureRequest#CONTROL_AE_LOCK
+ * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CaptureRequest#CONTROL_AE_REGIONS
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see CaptureRequest#CONTROL_AF_REGIONS
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
+ * @see CaptureRequest#CONTROL_AWB_LOCK
+ * @see CaptureRequest#CONTROL_AWB_REGIONS
+ * @see CaptureRequest#CONTROL_EFFECT_MODE
+ * @see CaptureRequest#CONTROL_MODE
+ * @see CaptureRequest#FLASH_MODE
+ * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
@@ -1725,6 +1819,10 @@
public static final int CONTROL_SCENE_MODE_BARCODE = 16;
/**
+ * <p>This is deprecated, please use
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
+ * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }
+ * for high speed video recording.</p>
* <p>Optimized for high speed video recording (frame rate >=60fps) use case.</p>
* <p>The supported high speed video sizes and fps ranges are specified in
* android.control.availableHighSpeedVideoConfigurations. To get desired
@@ -1799,6 +1897,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @see CaptureRequest#CONTROL_SCENE_MODE
+ * @deprecated Please refer to this API documentation to find the alternatives
*/
public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17;
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 7a39dd5..ab0f607 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -721,4 +721,10 @@
}
}
+ @Override
+ public boolean isConstrainedHighSpeed() {
+ // TODO: to be implemented
+ return false;
+ }
+
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index e60e266..16701e5 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1906,4 +1906,18 @@
private CameraCharacteristics getCharacteristics() {
return mCharacteristics;
}
+
+ @Override
+ public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
+ android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
+ throws CameraAccessException {
+ // TODO: to be implemented
+ throw new UnsupportedOperationException("To be implemented!!!!");
+ }
+
+ @Override
+ public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("To be implemented!!!!");
+ }
}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
index c03144b..2449abe 100644
--- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
@@ -33,7 +33,7 @@
*/
public class MarshalQueryableHighSpeedVideoConfiguration
implements MarshalQueryable<HighSpeedVideoConfiguration> {
- private static final int SIZE = SIZEOF_INT32 * 4;
+ private static final int SIZE = SIZEOF_INT32 * 5;
private class MarshalerHighSpeedVideoConfiguration
extends Marshaler<HighSpeedVideoConfiguration> {
@@ -49,6 +49,7 @@
buffer.putInt(value.getHeight());
buffer.putInt(value.getFpsMin());
buffer.putInt(value.getFpsMax());
+ buffer.putInt(value.getBatchSizeMax());
}
@Override
@@ -57,8 +58,9 @@
int height = buffer.getInt();
int fpsMin = buffer.getInt();
int fpsMax = buffer.getInt();
+ int batchSizeMax = buffer.getInt();
- return new HighSpeedVideoConfiguration(width, height, fpsMin, fpsMax);
+ return new HighSpeedVideoConfiguration(width, height, fpsMin, fpsMax, batchSizeMax);
}
@Override
diff --git a/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java b/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
index 088049f..b469126 100644
--- a/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
+++ b/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
@@ -33,6 +33,7 @@
* @hide
*/
public final class HighSpeedVideoConfiguration {
+ static final private int HIGH_SPEED_MAX_MINIMAL_FPS = 120;
/**
* Create a new {@link HighSpeedVideoConfiguration}.
@@ -48,15 +49,18 @@
* @hide
*/
public HighSpeedVideoConfiguration(
- final int width, final int height, final int fpsMin, final int fpsMax) {
- if (fpsMax < 60) {
- throw new IllegalArgumentException("fpsMax must be at least 60");
+ final int width, final int height, final int fpsMin, final int fpsMax,
+ final int batchSizeMax) {
+ if (fpsMax < HIGH_SPEED_MAX_MINIMAL_FPS) {
+ throw new IllegalArgumentException("fpsMax must be at least " +
+ HIGH_SPEED_MAX_MINIMAL_FPS);
}
mFpsMax = fpsMax;
mWidth = checkArgumentPositive(width, "width must be positive");
mHeight = checkArgumentPositive(height, "height must be positive");
mFpsMin = checkArgumentPositive(fpsMin, "fpsMin must be positive");
mSize = new Size(mWidth, mHeight);
+ mBatchSizeMax = checkArgumentPositive(batchSizeMax, "batchSizeMax must be positive");
mFpsRange = new Range<Integer>(mFpsMin, mFpsMax);
}
@@ -106,9 +110,18 @@
}
/**
+ * Convenience method to return the max batch size of this high speed video configuration.
+ *
+ * @return the maximal batch size for this high speed video configuration
+ */
+ public int getBatchSizeMax() {
+ return mBatchSizeMax;
+ }
+
+ /**
* Convenience method to return the FPS range of this high speed video configuration.
*
- * @return a Range with high bound >= 60
+ * @return a Range with high bound >= {@value #HIGH_SPEED_MAX_MINIMAL_FPS}
*/
public Range<Integer> getFpsRange() {
return mFpsRange;
@@ -135,7 +148,8 @@
return mWidth == other.mWidth &&
mHeight == other.mHeight &&
mFpsMin == other.mFpsMin &&
- mFpsMax == other.mFpsMax;
+ mFpsMax == other.mFpsMax &&
+ mBatchSizeMax == other.mBatchSizeMax;
}
return false;
}
@@ -152,6 +166,7 @@
private final int mHeight;
private final int mFpsMin;
private final int mFpsMax;
+ private final int mBatchSizeMax;
private final Size mSize;
private final Range<Integer> mFpsRange;
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index e99a960..5b5cdd2 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -291,7 +291,7 @@
b.setText(text, where, where + after)
.setPaint(getPaint())
.setWidth(getWidth())
- .setTextDir(getTextDirectionHeuristic())
+ .setTextDirection(getTextDirectionHeuristic())
.setLineSpacing(getSpacingAdd(), getSpacingMultiplier())
.setEllipsizedWidth(mEllipsizedWidth)
.setEllipsize(mEllipsizeAt)
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index d6d046b..464710b 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -184,7 +184,7 @@
* @param textDir text direction heuristic for resolving BiDi behavior.
* @return this builder, useful for chaining
*/
- public Builder setTextDir(TextDirectionHeuristic textDir) {
+ public Builder setTextDirection(TextDirectionHeuristic textDir) {
mTextDir = textDir;
return this;
}
@@ -473,7 +473,7 @@
Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
.setAlignment(align)
- .setTextDir(textDir)
+ .setTextDirection(textDir)
.setLineSpacing(spacingadd, spacingmult)
.setIncludePad(includepad)
.setEllipsizedWidth(ellipsizedWidth)
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 6e2d110..664c02a 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -19,6 +19,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -158,7 +159,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -167,6 +170,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
@@ -244,7 +253,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -252,6 +263,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
@@ -354,7 +371,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -362,6 +381,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
@@ -468,7 +493,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(focused, interactiveRegion);
@@ -476,6 +503,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
@@ -545,7 +578,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(next, interactiveRegion);
@@ -553,6 +588,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 774307f..126540f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17115,6 +17115,7 @@
* Also calls {@link StateListAnimator#jumpToCurrentState()} if there is a StateListAnimator
* attached to this view.
*/
+ @CallSuper
public void jumpDrawablesToCurrentState() {
if (mBackground != null) {
mBackground.jumpToCurrentState();
@@ -17564,13 +17565,16 @@
* @see Drawable#setTintMode(PorterDuff.Mode)
*/
public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
- if (mBackgroundTint == null) {
- mBackgroundTint = new TintInfo();
+ if (mForegroundInfo == null) {
+ mForegroundInfo = new ForegroundInfo();
}
- mBackgroundTint.mTintMode = tintMode;
- mBackgroundTint.mHasTintMode = true;
+ if (mForegroundInfo.mTintInfo == null) {
+ mForegroundInfo.mTintInfo = new TintInfo();
+ }
+ mForegroundInfo.mTintInfo.mTintMode = tintMode;
+ mForegroundInfo.mTintInfo.mHasTintMode = true;
- applyBackgroundTint();
+ applyForegroundTint();
}
/**
@@ -17580,7 +17584,7 @@
* @return the blending mode used to apply the tint to the foreground
* drawable
* @attr ref android.R.styleable#View_foregroundTintMode
- * @see #setBackgroundTintMode(PorterDuff.Mode)
+ * @see #setForegroundTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getForegroundTintMode() {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index f7b6405..ca5f5ad 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1058,6 +1058,13 @@
public boolean isKeyguardSecure();
/**
+ * Return whether the keyguard is on.
+ *
+ * @return true if in keyguard is on.
+ */
+ public boolean isKeyguardShowingOrOccluded();
+
+ /**
* inKeyguardRestrictedKeyInputMode
*
* if keyguard screen is showing or in restricted key input mode (i.e. in
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a0d1930..7f85f5a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -5309,6 +5309,13 @@
}
@Override
+ void syncSelectedItem() {
+ if (mDataChanged) {
+ layoutChildren();
+ }
+ }
+
+ @Override
protected void handleDataChanged() {
int count = mItemCount;
int lastHandledItemCount = mLastHandledItemCount;
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 6962711..cfe02bd 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -551,37 +551,67 @@
}
/**
- * Return the position of the currently selected item within the adapter's data set
+ * Returns the position of the currently selected item within the adapter's
+ * data set, or {@link #INVALID_POSITION} if there is nothing selected.
+ * <p>
+ * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#MNC},
+ * calling this method between an adapter data set change and a subsequent
+ * layout pass could return invalid data.
*
- * @return int Position (starting at 0), or {@link #INVALID_POSITION} if there is nothing selected.
+ * @return the selected item's position (starting at 0), or
+ * {@link #INVALID_POSITION} if there is nothing selected
*/
@ViewDebug.CapturedViewProperty
public int getSelectedItemPosition() {
+ syncSelectedItem();
return mNextSelectedPosition;
}
/**
- * @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}
- * if nothing is selected.
+ * Returns the row ID corresponding to the currently selected item, or
+ * {@link #INVALID_ROW_ID} if nothing is selected.
+ * <p>
+ * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#MNC},
+ * calling this method between an adapter data set change and a subsequent
+ * layout pass could return invalid data.
+ *
+ * @return the selected item's row ID, or {@link #INVALID_ROW_ID} if
+ * nothing is selected
*/
@ViewDebug.CapturedViewProperty
public long getSelectedItemId() {
+ syncSelectedItem();
return mNextSelectedRowId;
}
/**
- * @return The view corresponding to the currently selected item, or null
- * if nothing is selected
+ * Returns the view corresponding to the currently selected item, or
+ * {@code null} if nothing is selected.
+ * <p>
+ * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#MNC},
+ * calling this method between an adapter data set change and a subsequent
+ * layout pass could return inconsistent data.
+ *
+ * @return the selected item's view, or {@code null} if nothing is selected
*/
+ @Nullable
public abstract View getSelectedView();
/**
- * @return The data corresponding to the currently selected item, or
- * null if there is nothing selected.
+ * Returns the data corresponding to the currently selected item, or
+ * {@code null} if nothing is selected.
+ * <p>
+ * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#MNC},
+ * calling this method between an adapter data set change and a subsequent
+ * layout pass could return inconsistent data.
+ *
+ * @return the data corresponding to the currently selected item, or
+ * {@code null} if there is nothing selected.
*/
+ @Nullable
public Object getSelectedItem() {
- T adapter = getAdapter();
- int selection = getSelectedItemPosition();
+ final T adapter = getAdapter();
+ final int selection = getSelectedItemPosition();
if (adapter != null && adapter.getCount() > 0 && selection >= 0) {
return adapter.getItem(selection);
} else {
@@ -590,6 +620,15 @@
}
/**
+ * Synchronizes the selected item's position and ID, if necessary.
+ */
+ void syncSelectedItem() {
+ if (mDataChanged) {
+ onLayout(false, mLeft, mTop, mRight, mBottom);
+ }
+ }
+
+ /**
* @return The number of items owned by the Adapter associated with this
* AdapterView. (This is the number of data items, which may be
* larger than the number of visible views.)
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 260854f..027f6d6 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -52,12 +52,6 @@
*/
public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
/**
- * Contains the list of objects that represent the data of this ArrayAdapter.
- * The content of this list is referred to as "the array" in the documentation.
- */
- private List<T> mObjects;
-
- /**
* Lock used to modify the content of {@link #mObjects}. Any write operation
* performed on the array should be synchronized on this lock. This lock is also
* used by the filter (see {@link #getFilter()} to make a synchronized copy of
@@ -65,6 +59,14 @@
*/
private final Object mLock = new Object();
+ private final LayoutInflater mInflater;
+
+ /**
+ * Contains the list of objects that represent the data of this ArrayAdapter.
+ * The content of this list is referred to as "the array" in the documentation.
+ */
+ private List<T> mObjects;
+
/**
* The resource indicating what views to inflate to display the content of this
* array adapter.
@@ -97,8 +99,6 @@
private ArrayList<T> mOriginalValues;
private ArrayFilter mFilter;
- private LayoutInflater mInflater;
-
/** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
private LayoutInflater mDropDownInflater;
@@ -442,9 +442,6 @@
return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
}
- /**
- * {@inheritDoc}
- */
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index d38a225..7e542c9 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -115,7 +115,8 @@
R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
// Set up and attach container.
- mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
+ mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator, false);
+ mDelegator.addView(mContainer);
// Set up header views.
final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
@@ -471,7 +472,11 @@
@Override
public void setEnabled(boolean enabled) {
- mContainer.setEnabled(false);
+ mContainer.setEnabled(enabled);
+ mDayPickerView.setEnabled(enabled);
+ mYearPickerView.setEnabled(enabled);
+ mHeaderYear.setEnabled(enabled);
+ mHeaderMonthDay.setEnabled(enabled);
}
@Override
@@ -481,8 +486,7 @@
@Override
public CalendarView getCalendarView() {
- throw new UnsupportedOperationException(
- "CalendarView does not exists for the new DatePicker");
+ throw new UnsupportedOperationException("Not supported by calendar-mode DatePicker");
}
@Override
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0cac529..56f9b5c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1004,14 +1004,14 @@
stopSelectionActionMode();
} else {
stopSelectionActionMode();
- startSelectionActionModeWithSelectionAndStartDrag();
+ selectCurrentWordAndStartDrag();
}
handled = true;
}
// Start a new selection
if (!handled) {
- handled = startSelectionActionModeWithSelectionAndStartDrag();
+ handled = selectCurrentWordAndStartDrag();
}
return handled;
@@ -1724,22 +1724,9 @@
}
/**
- * Starts a Selection Action Mode with the current selection and enters drag mode. This should
- * be used whenever the mode is started from a touch event.
- *
- * @return true if the selection mode was actually started.
- */
- private boolean startSelectionActionModeWithSelectionAndStartDrag() {
- boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
- if (selectionStarted) {
- getSelectionController().enterDrag();
- }
- return selectionStarted;
- }
-
- /**
* Starts a Selection Action Mode with the current selection and ensures the selection handles
- * are shown. This should be used when the mode is started from a non-touch event.
+ * are shown if there is a selection, otherwise the insertion handle is shown. This should be
+ * used when the mode is started from a non-touch event.
*
* @return true if the selection mode was actually started.
*/
@@ -1747,40 +1734,67 @@
boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
if (selectionStarted) {
getSelectionController().show();
+ } else if (getInsertionController() != null) {
+ getInsertionController().show();
}
return selectionStarted;
}
- private boolean startSelectionActionModeWithSelectionInternal() {
+ /**
+ * If the TextView allows text selection, selects the current word when no existing selection
+ * was available and starts a drag.
+ *
+ * @return true if the drag was started.
+ */
+ private boolean selectCurrentWordAndStartDrag() {
if (extractedTextModeWillBeStarted()) {
// Cancel the single tap delayed runnable.
if (mSelectionModeWithoutSelectionRunnable != null) {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
}
+ return false;
+ }
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.finish();
+ }
+ if (!checkFieldAndSelectCurrentWord()) {
+ return false;
+ }
+ getSelectionController().enterDrag();
+ return true;
+ }
+ /**
+ * Checks whether a selection can be performed on the current TextView and if so selects
+ * the current word.
+ *
+ * @return true if there already was a selection or if the current word was selected.
+ */
+ private boolean checkFieldAndSelectCurrentWord() {
+ if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
+ Log.w(TextView.LOG_TAG,
+ "TextView does not support text selection. Selection cancelled.");
return false;
}
+ if (!mTextView.hasSelection()) {
+ // There may already be a selection on device rotation
+ return selectCurrentWord();
+ }
+ return true;
+ }
+
+ private boolean startSelectionActionModeWithSelectionInternal() {
if (mSelectionActionMode != null) {
// Selection action mode is already started
mSelectionActionMode.invalidate();
return false;
}
- if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
- Log.w(TextView.LOG_TAG,
- "TextView does not support text selection. Action mode cancelled.");
+ if (!checkFieldAndSelectCurrentWord()) {
return false;
}
- if (!mTextView.hasSelection()) {
- // There may already be a selection on device rotation
- if (!selectCurrentWord()) {
- // No word found under cursor or text selection not permitted.
- return false;
- }
- }
-
boolean willExtract = extractedTextModeWillBeStarted();
// Do not start the action mode when extracted text will show up full screen, which would
@@ -1829,12 +1843,18 @@
}
if (selectionStart == selectionEnd) {
// Spans overlap the cursor.
- return true;
+ for (int i = 0; i < suggestionSpans.length; i++) {
+ if (suggestionSpans[i].getSuggestions().length > 0) {
+ return true;
+ }
+ }
+ return false;
}
int minSpanStart = mTextView.getText().length();
int maxSpanEnd = 0;
int unionOfSpansCoveringSelectionStartStart = mTextView.getText().length();
int unionOfSpansCoveringSelectionStartEnd = 0;
+ boolean hasValidSuggestions = false;
for (int i = 0; i < suggestionSpans.length; i++) {
final int spanStart = spannable.getSpanStart(suggestionSpans[i]);
final int spanEnd = spannable.getSpanEnd(suggestionSpans[i]);
@@ -1844,11 +1864,16 @@
// The span doesn't cover the current selection start point.
continue;
}
+ hasValidSuggestions =
+ hasValidSuggestions || suggestionSpans[i].getSuggestions().length > 0;
unionOfSpansCoveringSelectionStartStart =
Math.min(unionOfSpansCoveringSelectionStartStart, spanStart);
unionOfSpansCoveringSelectionStartEnd =
Math.max(unionOfSpansCoveringSelectionStartEnd, spanEnd);
}
+ if (!hasValidSuggestions) {
+ return false;
+ }
if (unionOfSpansCoveringSelectionStartStart >= unionOfSpansCoveringSelectionStartEnd) {
// No spans cover the selection start point.
return false;
@@ -4071,12 +4096,17 @@
offset = getNextCursorOffset(selectionEnd, false);
mTouchWordDelta = 0.0f;
}
- mInWord = !getWordIteratorWithText().isBoundary(offset);
positionAtCursorOffset(offset, false);
}
}
@Override
+ protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
+ super.positionAtCursorOffset(offset, parentScrolled);
+ mInWord = !getWordIteratorWithText().isBoundary(offset);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -4193,12 +4223,17 @@
offset = getNextCursorOffset(selectionStart, true);
mTouchWordDelta = 0.0f;
}
- mInWord = !getWordIteratorWithText().isBoundary(offset);
positionAtCursorOffset(offset, false);
}
}
@Override
+ protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
+ super.positionAtCursorOffset(offset, parentScrolled);
+ mInWord = !getWordIteratorWithText().isBoundary(offset);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -4377,7 +4412,7 @@
boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop;
if (stayedInArea && isPositionOnText(eventX, eventY)) {
- startSelectionActionModeWithSelectionAndStartDrag();
+ selectCurrentWordAndStartDrag();
mDiscardNextActionUp = true;
}
}
@@ -4480,6 +4515,7 @@
mEndHandle.showAtLocation(endOffset);
// No longer the first dragging motion, reset.
+ startSelectionActionModeWithSelection();
mDragAcceleratorActive = false;
mStartOffset = -1;
}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 16dc26d..e7d9226 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.Widget;
import android.content.Context;
@@ -608,7 +609,16 @@
mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
- mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
+ final Drawable selectionDivider = attributesArray.getDrawable(
+ R.styleable.NumberPicker_selectionDivider);
+ if (selectionDivider != null) {
+ selectionDivider.setCallback(this);
+ selectionDivider.setLayoutDirection(getLayoutDirection());
+ if (selectionDivider.isStateful()) {
+ selectionDivider.setState(getDrawableState());
+ }
+ }
+ mSelectionDivider = selectionDivider;
final int defSelectionDividerHeight = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
@@ -1499,6 +1509,38 @@
removeAllCallbacks();
}
+ @CallSuper
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+
+ final int[] state = getDrawableState();
+
+ if (mSelectionDivider != null && mSelectionDivider.isStateful()) {
+ mSelectionDivider.setState(state);
+ }
+ }
+
+ @CallSuper
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+
+ if (mSelectionDivider != null) {
+ mSelectionDivider.jumpToCurrentState();
+ }
+ }
+
+ /** @hide */
+ @Override
+ public void onResolveDrawables(@ResolvedLayoutDir int layoutDirection) {
+ super.onResolveDrawables(layoutDirection);
+
+ if (mSelectionDivider != null) {
+ mSelectionDivider.setLayoutDirection(layoutDirection);
+ }
+ }
+
@Override
protected void onDraw(Canvas canvas) {
if (!mHasSelectorWheel) {
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index e7760ee..3bf9485 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -52,6 +52,8 @@
* If no appropriate binding can be found, an {@link IllegalStateException} is thrown.
*/
public class SimpleAdapter extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
+ private final LayoutInflater mInflater;
+
private int[] mTo;
private String[] mFrom;
private ViewBinder mViewBinder;
@@ -60,7 +62,6 @@
private int mResource;
private int mDropDownResource;
- private LayoutInflater mInflater;
/** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
private LayoutInflater mDropDownInflater;
@@ -174,8 +175,8 @@
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
- return createViewFromResource(
- mDropDownInflater, position, convertView, parent, mDropDownResource);
+ final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
+ return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
}
private void bindView(int position, View view) {
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index c521f72..27fa3b9 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -188,32 +188,9 @@
mLocalActivityManager = activityGroup;
}
-
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- final ViewTreeObserver treeObserver = getViewTreeObserver();
- treeObserver.addOnTouchModeChangeListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- final ViewTreeObserver treeObserver = getViewTreeObserver();
- treeObserver.removeOnTouchModeChangeListener(this);
- }
-
- /**
- * {@inheritDoc}
- */
public void onTouchModeChanged(boolean isInTouchMode) {
- if (!isInTouchMode) {
- // leaving touch mode.. if nothing has focus, let's give it to
- // the indicator of the current tab
- if (mCurrentView != null && (!mCurrentView.hasFocus() || mCurrentView.isFocused())) {
- mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
- }
- }
+ // No longer used, but kept to maintain API compatibility.
}
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e14e39c..3a85476 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6680,7 +6680,7 @@
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
mHint.length(), mTextPaint, hintWidth)
.setAlignment(alignment)
- .setTextDir(mTextDir)
+ .setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy)
@@ -6771,7 +6771,7 @@
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
0, mTransformed.length(), mTextPaint, wantWidth)
.setAlignment(alignment)
- .setTextDir(mTextDir)
+ .setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy)
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index e02d706..c32ba85 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -37,6 +37,7 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -323,6 +324,7 @@
}
};
+ private final Rect mViewPort = new Rect();
private final Point mCoords = new Point();
private final Region mTouchableRegion = new Region();
@@ -394,7 +396,7 @@
mMainPanel = new FloatingToolbarMainPanel(mParent.getContext(), mOpenOverflow);
}
List<MenuItem> overflowMenuItems =
- mMainPanel.layoutMenuItems(menuItems, suggestedWidth);
+ mMainPanel.layoutMenuItems(menuItems, getToolbarWidth(suggestedWidth));
mMainPanel.setOnMenuItemClickListener(menuItemClickListener);
if (!overflowMenuItems.isEmpty()) {
if (mOverflowPanel == null) {
@@ -430,7 +432,8 @@
// The "show" animation will make this visible.
mContentContainer.setAlpha(0);
}
- updateOverflowHeight(contentRect.top - (mMarginVertical * 2));
+ refreshViewPort();
+ updateOverflowHeight(contentRect.top - (mMarginVertical * 2) - mViewPort.top);
refreshCoordinatesAndOverflowDirection(contentRect);
preparePopupContent();
mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoords.x, mCoords.y);
@@ -494,6 +497,7 @@
}
cancelOverflowAnimations();
+ refreshViewPort();
refreshCoordinatesAndOverflowDirection(contentRect);
preparePopupContent();
mPopupWindow.update(mCoords.x, mCoords.y, getWidth(), getHeight());
@@ -521,18 +525,24 @@
}
private void refreshCoordinatesAndOverflowDirection(Rect contentRect) {
+ // NOTE: Ensure that mViewPort has been refreshed before this.
+
int x = contentRect.centerX() - getWidth() / 2;
int y;
- if (contentRect.top > getHeight()) {
+ if (contentRect.top - getHeight() > mViewPort.top) {
y = contentRect.top - getHeight();
mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP;
- } else if (contentRect.top > getToolbarHeightWithVerticalMargin()) {
+ } else if (contentRect.top - getToolbarHeightWithVerticalMargin() > mViewPort.top) {
y = contentRect.top - getToolbarHeightWithVerticalMargin();
mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
} else {
y = contentRect.bottom;
mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
}
+
+ // Update x so that the toolbar isn't rendered behind the nav bar in landscape.
+ x = Math.max(0, Math.min(x, mViewPort.right - getWidth()));
+
mCoords.set(x, y);
if (mOverflowPanel != null) {
mOverflowPanel.setOverflowDirection(mOverflowDirection);
@@ -821,6 +831,29 @@
mPopupWindow.setHeight(height + mMarginVertical * 2);
}
+
+ private void refreshViewPort() {
+ mParent.getGlobalVisibleRect(mViewPort);
+ WindowInsets windowInsets = mParent.getRootWindowInsets();
+ mViewPort.set(
+ mViewPort.left + windowInsets.getStableInsetLeft(),
+ mViewPort.top + windowInsets.getStableInsetTop(),
+ mViewPort.right - windowInsets.getStableInsetRight(),
+ mViewPort.bottom - windowInsets.getStableInsetBottom());
+ }
+
+ private int getToolbarWidth(int suggestedWidth) {
+ int width = suggestedWidth;
+ refreshViewPort();
+ int maximumWidth = mViewPort.width() - 2 * mParent.getResources()
+ .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+ if (width <= 0) {
+ width = mParent.getResources()
+ .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
+ }
+ return Math.min(width, maximumWidth);
+ }
+
/**
* Sets the touchable region of this popup to be zero. This means that all touch events on
* this popup will go through to the surface behind it.
@@ -906,12 +939,11 @@
*
* @return The menu items that are not included in this main panel.
*/
- public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int suggestedWidth) {
+ public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int width) {
Preconditions.checkNotNull(menuItems);
- final int toolbarWidth = getAdjustedToolbarWidth(mContext, suggestedWidth)
- // Reserve space for the "open overflow" button.
- - getEstimatedOpenOverflowButtonWidth(mContext);
+ // Reserve space for the "open overflow" button.
+ final int toolbarWidth = width - getEstimatedOpenOverflowButtonWidth(mContext);
int availableWidth = toolbarWidth;
final LinkedList<MenuItem> remainingMenuItems = new LinkedList<MenuItem>(menuItems);
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 52fd111..3fd3b3c 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -111,19 +111,19 @@
"()J",
(void*)android_os_Trace_nativeGetEnabledTags },
{ "nativeTraceCounter",
- "(JLjava/lang/String;I)V",
+ "!(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeTraceCounter },
{ "nativeTraceBegin",
- "(JLjava/lang/String;)V",
+ "!(JLjava/lang/String;)V",
(void*)android_os_Trace_nativeTraceBegin },
{ "nativeTraceEnd",
- "(J)V",
+ "!(J)V",
(void*)android_os_Trace_nativeTraceEnd },
{ "nativeAsyncTraceBegin",
- "(JLjava/lang/String;I)V",
+ "!(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceBegin },
{ "nativeAsyncTraceEnd",
- "(JLjava/lang/String;I)V",
+ "!(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceEnd },
{ "nativeSetAppTracingAllowed",
"(Z)V",
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3fa92a8..abd2409 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -499,7 +499,7 @@
nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
UiFrameInfoBuilder(proxy->frameInfo())
.setVsync(vsync, vsync)
- .addFlag(FrameInfoFlags::kSurfaceCanvas);
+ .addFlag(FrameInfoFlags::SurfaceCanvas);
proxy->syncAndDrawFrame();
}
diff --git a/core/res/res/drawable/number_picker_divider_material.xml b/core/res/res/drawable/number_picker_divider_material.xml
new file mode 100644
index 0000000..2474be0
--- /dev/null
+++ b/core/res/res/drawable/number_picker_divider_material.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?attr/colorControlNormal"
+ android:shape="rectangle">
+ <solid android:color="@color/black" />
+</shape>
diff --git a/core/res/res/layout/number_picker_material.xml b/core/res/res/layout/number_picker_material.xml
new file mode 100644
index 0000000..47edec4
--- /dev/null
+++ b/core/res/res/layout/number_picker_material.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ class="android.widget.NumberPicker$CustomEditText"
+ android:id="@+id/numberpicker_input"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:singleLine="true"
+ android:background="@null"
+ android:textAppearance="@style/TextAppearance.Material.Caption" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index f7a42fa..7782ed7 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -630,13 +630,13 @@
</style>
<style name="Widget.Material.NumberPicker" parent="Widget.NumberPicker">
- <item name="internalLayout">@layout/number_picker_with_selector_wheel</item>
+ <item name="internalLayout">@layout/number_picker_material</item>
<item name="solidColor">@color/transparent</item>
- <item name="selectionDivider">@drawable/numberpicker_selection_divider</item>
- <item name="selectionDividerHeight">2dip</item>
- <item name="selectionDividersDistance">48dip</item>
- <item name="internalMinWidth">64dip</item>
- <item name="internalMaxHeight">180dip</item>
+ <item name="selectionDivider">@drawable/number_picker_divider_material</item>
+ <item name="selectionDividerHeight">2dp</item>
+ <item name="selectionDividersDistance">48dp</item>
+ <item name="internalMinWidth">64dp</item>
+ <item name="internalMaxHeight">180dp</item>
<item name="virtualButtonPressedDrawable">?attr/selectableItemBackground</item>
</style>
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index d6f8cca..7aa0aef 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -587,10 +587,29 @@
public static final int RAW12 = 0x26;
/**
- * Android dense depth image format.
+ * <p>Android dense depth image format.</p>
*
- * Each pixel is 16 bits, representing a depth ranging measurement from
- * a depth camera or similar sensor.
+ * <p>Each pixel is 16 bits, representing a depth ranging measurement from a depth camera or
+ * similar sensor. The 16-bit sample consists of a confidence value and the actual ranging
+ * measurement.</p>
+ *
+ * <p>The confidence value is an estimate of correctness for this sample. It is encoded in the
+ * 3 most significant bits of the sample, with a value of 0 representing 100% confidence, a
+ * value of 1 representing 0% confidence, a value of 2 representing 1/7, a value of 3
+ * representing 2/7, and so on.</p>
+ *
+ * <p>As an example, the following sample extracts the range and confidence from the first pixel
+ * of a DEPTH16-format {@link android.media.Image}, and converts the confidence to a
+ * floating-point value between 0 and 1.f inclusive, with 1.f representing maximum confidence:
+ *
+ * <pre>
+ * ShortBuffer shortDepthBuffer = img.getPlanes()[0].getBuffer().asShortBuffer();
+ * short depthSample = shortDepthBuffer.get()
+ * short depthRange = (short) (depthSample & 0x1FFF);
+ * short depthConfidence = (short) ((depthSample >> 13) & 0x7);
+ * float depthPercentage = depthConfidence == 0 ? 1.f : (depthConfidence - 1) / 7.f;
+ * </pre>
+ * </p>
*
* <p>This format assumes
* <ul>
@@ -602,19 +621,32 @@
*
* <pre> y_size = stride * height </pre>
*
- * When produced by a camera, the units are millimeters.
+ * When produced by a camera, the units for the range are millimeters.
*/
public static final int DEPTH16 = 0x44363159;
/**
* Android sparse depth point cloud format.
*
- * <p>A variable-length list of 3D points, with each point represented
- * by a triple of floats.</p>
+ * <p>A variable-length list of 3D points plus a confidence value, with each point represented
+ * by four floats; first the X, Y, Z position coordinates, and then the confidence value.</p>
*
- * <p>The number of points is {@code (size of the buffer in bytes) / 12}.
+ * <p>The number of points is {@code (size of the buffer in bytes) / 16}.
*
- * The coordinate system and units depend on the source of the point cloud data.
+ * <p>The coordinate system and units of the position values depend on the source of the point
+ * cloud data. The confidence value is between 0.f and 1.f, inclusive, with 0 representing 0%
+ * confidence and 1.f representing 100% confidence in the measured position values.</p>
+ *
+ * <p>As an example, the following code extracts the first depth point in a DEPTH_POINT_CLOUD
+ * format {@link android.media.Image}:
+ * <pre>
+ * FloatBuffer floatDepthBuffer = img.getPlanes()[0].getBuffer().asFloatBuffer();
+ * float x = floatDepthBuffer.get();
+ * float y = floatDepthBuffer.get();
+ * float z = floatDepthBuffer.get();
+ * float confidence = floatDepthBuffer.get();
+ * </pre>
+ *
*/
public static final int DEPTH_POINT_CLOUD = 0x101;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index 03be759..aa2b946 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -43,13 +43,17 @@
private static final String PACKAGE_NAME = "android.security.keystore";
private static final String KEYSTORE_SECRET_KEY_CLASS_NAME =
PACKAGE_NAME + ".AndroidKeyStoreSecretKey";
+ private static final String KEYSTORE_PRIVATE_KEY_CLASS_NAME =
+ PACKAGE_NAME + ".AndroidKeyStorePrivateKey";
+ private static final String KEYSTORE_PUBLIC_KEY_CLASS_NAME =
+ PACKAGE_NAME + ".AndroidKeyStorePublicKey";
AndroidKeyStoreBCWorkaroundProvider() {
super("AndroidKeyStoreBCWorkaround",
1.0,
"Android KeyStore security provider to work around Bouncy Castle");
- // javax.crypto.Mac
+ // --------------------- javax.crypto.Mac
putMacImpl("HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA1");
put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1");
put("Alg.Alias.Mac.HMAC-SHA1", "HmacSHA1");
@@ -75,7 +79,7 @@
put("Alg.Alias.Mac.HMAC-SHA512", "HmacSHA512");
put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512");
- // javax.crypto.Cipher
+ // --------------------- javax.crypto.Cipher
putSymmetricCipherImpl("AES/ECB/NoPadding",
PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$NoPadding");
putSymmetricCipherImpl("AES/ECB/PKCS7Padding",
@@ -88,6 +92,36 @@
putSymmetricCipherImpl("AES/CTR/NoPadding",
PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding");
+
+ putAsymmetricCipherImpl("RSA/ECB/NoPadding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$NoPadding");
+ put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
+ putAsymmetricCipherImpl("RSA/ECB/PKCS1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$PKCS1Padding");
+ put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPPadding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPPadding", "RSA/ECB/OAEPPadding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-1AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-1AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-224AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA224AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-224AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA256AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-256AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-384AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA384AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-384AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-384AndMGF1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-512AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA512AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
}
private void putMacImpl(String algorithm, String implClass) {
@@ -99,4 +133,10 @@
put("Cipher." + transformation, implClass);
put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
}
+
+ private void putAsymmetricCipherImpl(String transformation, String implClass) {
+ put("Cipher." + transformation, implClass);
+ put("Cipher." + transformation + " SupportedKeyClasses",
+ KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+ }
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 3ad3c9d..fd9bdb8 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -66,7 +66,7 @@
*/
private IBinder mOperationToken;
private long mOperationHandle;
- private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+ private KeyStoreCryptoOperationStreamer mMainDataStreamer;
/**
* Encountered exception which could not be immediately thrown because it was encountered inside
@@ -210,7 +210,6 @@
byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, getAdditionalEntropyAmountForBegin());
- KeymasterArguments keymasterOutputArgs = new KeymasterArguments();
OperationResult opResult = mKeyStore.begin(
mKey.getAlias(),
mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT,
@@ -247,9 +246,21 @@
}
loadAlgorithmSpecificParametersFromBeginResult(opResult.outParams);
- mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+ mMainDataStreamer = createMainDataStreamer(mKeyStore, opResult.token);
+ }
+
+ /**
+ * Creates a streamer which sends plaintext/ciphertext into the provided KeyStore and receives
+ * the corresponding ciphertext/plaintext from the KeyStore.
+ *
+ * <p>This implementation returns a working streamer.
+ */
+ @NonNull
+ protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+ KeyStore keyStore, IBinder operationToken) {
+ return new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- mKeyStore, opResult.token));
+ keyStore, operationToken));
}
@Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
index 6098e5c..1751aa5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
@@ -19,7 +19,7 @@
import java.security.Key;
/**
- * {@link Key} backed by AndroidKeyStore.
+ * {@link Key} backed by Android Keystore.
*
* @hide
*/
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
new file mode 100644
index 0000000..b586ad4
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.security.keystore;
+
+import java.security.PrivateKey;
+
+/**
+ * {@link PrivateKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStorePrivateKey extends AndroidKeyStoreKey implements PrivateKey {
+
+ public AndroidKeyStorePrivateKey(String alias, String algorithm) {
+ super(alias, algorithm);
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
new file mode 100644
index 0000000..8133d46
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.security.keystore;
+
+import java.security.PublicKey;
+
+/**
+ * {@link PublicKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey {
+
+ private final byte[] mEncoded;
+
+ public AndroidKeyStorePublicKey(String alias, String algorithm, byte[] x509EncodedForm) {
+ super(alias, algorithm);
+ mEncoded = ArrayUtils.cloneIfNotEmpty(x509EncodedForm);
+ }
+
+ @Override
+ public String getFormat() {
+ return "X.509";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return ArrayUtils.cloneIfNotEmpty(mEncoded);
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
new file mode 100644
index 0000000..f70c323
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2015 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.security.keystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import libcore.util.EmptyArray;
+
+import java.io.ByteArrayOutputStream;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+
+/**
+ * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase {
+
+ /**
+ * Raw RSA cipher without any padding.
+ */
+ public static final class NoPadding extends AndroidKeyStoreRSACipherSpi {
+ public NoPadding() {
+ super(KeymasterDefs.KM_PAD_NONE);
+ }
+
+ @Override
+ protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+ @Override
+ protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
+ throws InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unexpected parameters: " + params + ". No parameters supported");
+ }
+ }
+
+ @Override
+ protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+ throws InvalidAlgorithmParameterException {
+
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unexpected parameters: " + params + ". No parameters supported");
+ }
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForBegin() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+ KeyStore keyStore, IBinder operationToken) {
+ if (isEncrypting()) {
+ // KeyStore's RSA encryption without padding expects the input to be of the same
+ // length as the modulus. We thus have to buffer all input to pad it with leading
+ // zeros.
+ return new ZeroPaddingEncryptionStreamer(
+ super.createMainDataStreamer(keyStore, operationToken),
+ getModulusSizeBytes());
+ } else {
+ return super.createMainDataStreamer(keyStore, operationToken);
+ }
+ }
+
+ /**
+ * Streamer which buffers all plaintext input, then pads it with leading zeros to match
+ * modulus size, and then sends it into KeyStore to obtain ciphertext.
+ */
+ private static class ZeroPaddingEncryptionStreamer
+ implements KeyStoreCryptoOperationStreamer {
+
+ private final KeyStoreCryptoOperationStreamer mDelegate;
+ private final int mModulusSizeBytes;
+ private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
+
+ private ZeroPaddingEncryptionStreamer(
+ KeyStoreCryptoOperationStreamer delegate,
+ int modulusSizeBytes) {
+ mDelegate = delegate;
+ mModulusSizeBytes = modulusSizeBytes;
+ }
+
+ @Override
+ public byte[] update(byte[] input, int inputOffset, int inputLength)
+ throws KeyStoreException {
+ if (inputLength > 0) {
+ mInputBuffer.write(input, inputOffset, inputLength);
+ }
+ return EmptyArray.BYTE;
+ }
+
+ @Override
+ public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+ throws KeyStoreException {
+ if (inputLength > 0) {
+ mInputBuffer.write(input, inputOffset, inputLength);
+ }
+ byte[] bufferedInput = mInputBuffer.toByteArray();
+ mInputBuffer.reset();
+ byte[] paddedInput;
+ if (bufferedInput.length < mModulusSizeBytes) {
+ // Pad input with leading zeros
+ paddedInput = new byte[mModulusSizeBytes];
+ System.arraycopy(
+ bufferedInput, 0,
+ paddedInput,
+ paddedInput.length - bufferedInput.length,
+ bufferedInput.length);
+ } else {
+ // No need to pad input
+ paddedInput = bufferedInput;
+ }
+ return mDelegate.doFinal(paddedInput, 0, paddedInput.length);
+ }
+ }
+ }
+
+ /**
+ * RSA cipher with PKCS#1 v1.5 encryption padding.
+ */
+ public static final class PKCS1Padding extends AndroidKeyStoreRSACipherSpi {
+ public PKCS1Padding() {
+ super(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ }
+
+ @Override
+ protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+ @Override
+ protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
+ throws InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unexpected parameters: " + params + ". No parameters supported");
+ }
+ }
+
+ @Override
+ protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+ throws InvalidAlgorithmParameterException {
+
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unexpected parameters: " + params + ". No parameters supported");
+ }
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForBegin() {
+ return (isEncrypting()) ? getModulusSizeBytes() : 0;
+ }
+ }
+
+ /**
+ * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF.
+ */
+ abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
+
+ private static final String MGF_ALGORITGM_MGF1 = "MGF1";
+
+ private int mKeymasterDigest = -1;
+ private int mDigestOutputSizeBytes;
+
+ OAEPWithMGF1Padding(int keymasterDigest) {
+ super(KeymasterDefs.KM_PAD_RSA_OAEP);
+ mKeymasterDigest = keymasterDigest;
+ mDigestOutputSizeBytes =
+ (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
+ }
+
+ @Override
+ protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+ @Override
+ protected final void initAlgorithmSpecificParameters(
+ @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException {
+ if (params == null) {
+ return;
+ }
+
+ if (!(params instanceof OAEPParameterSpec)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported parameter spec: " + params
+ + ". Only OAEPParameterSpec supported");
+ }
+ OAEPParameterSpec spec = (OAEPParameterSpec) params;
+ if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported MGF: " + spec.getMGFAlgorithm()
+ + ". Only " + MGF_ALGORITGM_MGF1 + " supported");
+ }
+ String jcaDigest = spec.getDigestAlgorithm();
+ int keymasterDigest;
+ try {
+ keymasterDigest = KeyProperties.Digest.toKeymaster(jcaDigest);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported digest: " + jcaDigest, e);
+ }
+ switch (keymasterDigest) {
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ // Permitted.
+ break;
+ default:
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported digest: " + jcaDigest);
+ }
+ AlgorithmParameterSpec mgfParams = spec.getMGFParameters();
+ if (mgfParams == null) {
+ throw new InvalidAlgorithmParameterException("MGF parameters must be provided");
+ }
+ // Check whether MGF parameters match the OAEPParameterSpec
+ if (!(mgfParams instanceof MGF1ParameterSpec)) {
+ throw new InvalidAlgorithmParameterException("Unsupported MGF parameters"
+ + ": " + mgfParams + ". Only MGF1ParameterSpec supported");
+ }
+ MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams;
+ String mgf1JcaDigest = mgfSpec.getDigestAlgorithm();
+ if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported MGF1 digest: " + mgf1JcaDigest
+ + ". Only " + KeyProperties.DIGEST_SHA1 + " supported");
+ }
+ PSource pSource = spec.getPSource();
+ if (!(pSource instanceof PSource.PSpecified)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported source of encoding input P: " + pSource
+ + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
+ }
+ PSource.PSpecified pSourceSpecified = (PSource.PSpecified) pSource;
+ byte[] pSourceValue = pSourceSpecified.getValue();
+ if ((pSourceValue != null) && (pSourceValue.length > 0)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported source of encoding input P: " + pSource
+ + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
+ }
+ mKeymasterDigest = keymasterDigest;
+ mDigestOutputSizeBytes =
+ (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
+ }
+
+ @Override
+ protected final void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+ throws InvalidAlgorithmParameterException {
+ if (params == null) {
+ return;
+ }
+
+ OAEPParameterSpec spec;
+ try {
+ spec = params.getParameterSpec(OAEPParameterSpec.class);
+ } catch (InvalidParameterSpecException e) {
+ throw new InvalidAlgorithmParameterException("OAEP parameters required"
+ + ", but not found in parameters: " + params, e);
+ }
+ if (spec == null) {
+ throw new InvalidAlgorithmParameterException("OAEP parameters required"
+ + ", but not provided in parameters: " + params);
+ }
+ initAlgorithmSpecificParameters(spec);
+ }
+
+ @Override
+ protected final AlgorithmParameters engineGetParameters() {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec(
+ KeyProperties.Digest.fromKeymaster(mKeymasterDigest),
+ MGF_ALGORITGM_MGF1,
+ MGF1ParameterSpec.SHA1,
+ PSource.PSpecified.DEFAULT);
+ try {
+ AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP");
+ params.init(spec);
+ return params;
+ } catch (NoSuchAlgorithmException e) {
+ throw new ProviderException(
+ "Failed to obtain OAEP AlgorithmParameters", e);
+ } catch (InvalidParameterSpecException e) {
+ throw new ProviderException(
+ "Failed to initialize OAEP AlgorithmParameters with an IV",
+ e);
+ }
+ }
+
+ @Override
+ protected final void addAlgorithmSpecificParametersToBegin(
+ KeymasterArguments keymasterArgs) {
+ super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ }
+
+ @Override
+ protected final void loadAlgorithmSpecificParametersFromBeginResult(
+ @NonNull KeymasterArguments keymasterArgs) {
+ super.loadAlgorithmSpecificParametersFromBeginResult(keymasterArgs);
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForBegin() {
+ return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
+ }
+ }
+
+ public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA1AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA1);
+ }
+ }
+
+ public static class OAEPWithSHA224AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA224AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+ }
+ }
+
+ public static class OAEPWithSHA256AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA256AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+ }
+ }
+
+ public static class OAEPWithSHA384AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA384AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+ }
+ }
+
+ public static class OAEPWithSHA512AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA512AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+ }
+ }
+
+ private final int mKeymasterPadding;
+
+ private int mModulusSizeBytes = -1;
+
+ AndroidKeyStoreRSACipherSpi(int keymasterPadding) {
+ mKeymasterPadding = keymasterPadding;
+ }
+
+ @Override
+ protected final void initKey(int opmode, Key key) throws InvalidKeyException {
+ if (key == null) {
+ throw new InvalidKeyException("Unsupported key: null");
+ }
+ if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
+ throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+ + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+ }
+ AndroidKeyStoreKey keystoreKey;
+ if (key instanceof AndroidKeyStorePrivateKey) {
+ keystoreKey = (AndroidKeyStoreKey) key;
+ } else if (key instanceof AndroidKeyStorePublicKey) {
+ keystoreKey = (AndroidKeyStoreKey) key;
+ } else {
+ throw new InvalidKeyException("Unsupported key type: " + key);
+ }
+
+ if (keystoreKey instanceof PrivateKey) {
+ if ((opmode != Cipher.DECRYPT_MODE) && (opmode != Cipher.UNWRAP_MODE)) {
+ throw new InvalidKeyException("Private key cannot be used with opmode: " + opmode
+ + ". Only DECRYPT_MODE and UNWRAP_MODE supported");
+ }
+ } else {
+ if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.WRAP_MODE)) {
+ throw new InvalidKeyException("Public key cannot be used with opmode: " + opmode
+ + ". Only ENCRYPT_MODE and WRAP_MODE supported");
+ }
+ }
+
+ KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+ int errorCode = getKeyStore().getKeyCharacteristics(
+ keystoreKey.getAlias(), null, null, keyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw getKeyStore().getInvalidKeyException(keystoreKey.getAlias(), errorCode);
+ }
+ int keySizeBits = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+ if (keySizeBits == -1) {
+ throw new InvalidKeyException("Size of key not known");
+ }
+ mModulusSizeBytes = (keySizeBits + 7) / 8;
+
+ setKey(keystoreKey);
+ }
+
+ @Override
+ protected final void resetAll() {
+ mModulusSizeBytes = -1;
+ super.resetAll();
+ }
+
+ @Override
+ protected final void resetWhilePreservingInitState() {
+ super.resetWhilePreservingInitState();
+ }
+
+ @Override
+ protected void addAlgorithmSpecificParametersToBegin(
+ @NonNull KeymasterArguments keymasterArgs) {
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ }
+
+ @Override
+ protected void loadAlgorithmSpecificParametersFromBeginResult(
+ @NonNull KeymasterArguments keymasterArgs) {
+ }
+
+ @Override
+ protected final int engineGetBlockSize() {
+ // Not a block cipher, according to the RI
+ return 0;
+ }
+
+ @Override
+ protected final byte[] engineGetIV() {
+ // IV never used
+ return null;
+ }
+
+ @Override
+ protected final int engineGetOutputSize(int inputLen) {
+ return getModulusSizeBytes();
+ }
+
+ protected final int getModulusSizeBytes() {
+ if (mModulusSizeBytes == -1) {
+ throw new IllegalStateException("Not initialized");
+ }
+ return mModulusSizeBytes;
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
new file mode 100644
index 0000000..36bc997b
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.security.keystore;
+
+import java.math.BigInteger;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * {@link RSAPublicKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implements RSAPublicKey {
+ private final BigInteger mModulus;
+ private final BigInteger mPublicExponent;
+
+ public AndroidKeyStoreRSAPublicKey(String alias, byte[] x509EncodedForm, BigInteger modulus,
+ BigInteger publicExponent) {
+ super(alias, "RSA", x509EncodedForm);
+ mModulus = modulus;
+ mPublicExponent = publicExponent;
+ }
+
+ public AndroidKeyStoreRSAPublicKey(String alias, RSAPublicKey info) {
+ this(alias, info.getEncoded(), info.getModulus(), info.getPublicExponent());
+ if (!"X.509".equalsIgnoreCase(info.getFormat())) {
+ throw new IllegalArgumentException(
+ "Unsupported key export format: " + info.getFormat());
+ }
+ }
+
+ @Override
+ public BigInteger getModulus() {
+ return mModulus;
+ }
+
+ @Override
+ public BigInteger getPublicExponent() {
+ return mPublicExponent;
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
index f75516b..af354ab 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
@@ -19,7 +19,7 @@
import javax.crypto.SecretKey;
/**
- * {@link SecretKey} backed by keystore.
+ * {@link SecretKey} backed by Android Keystore.
*
* @hide
*/
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
index 7d57e5f..47b4996 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
@@ -44,12 +44,12 @@
*
* @hide
*/
-public class KeyStoreCryptoOperationChunkedStreamer {
+class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationStreamer {
/**
* Bidirectional chunked data stream over a KeyStore crypto operation.
*/
- public interface Stream {
+ interface Stream {
/**
* Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
* be reached.
@@ -66,12 +66,11 @@
// Binder buffer is about 1MB, but it's shared between all active transactions of the process.
// Thus, it's safer to use a much smaller upper bound.
private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
- private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private final Stream mKeyStoreStream;
private final int mMaxChunkSize;
- private byte[] mBuffered = EMPTY_BYTE_ARRAY;
+ private byte[] mBuffered = EmptyArray.BYTE;
private int mBufferedOffset;
private int mBufferedLength;
@@ -84,10 +83,11 @@
mMaxChunkSize = maxChunkSize;
}
+ @Override
public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
if (inputLength == 0) {
// No input provided
- return EMPTY_BYTE_ARRAY;
+ return EmptyArray.BYTE;
}
ByteArrayOutputStream bufferedOutput = null;
@@ -129,7 +129,7 @@
if (opResult.inputConsumed == chunk.length) {
// The whole chunk was consumed
- mBuffered = EMPTY_BYTE_ARRAY;
+ mBuffered = EmptyArray.BYTE;
mBufferedOffset = 0;
mBufferedLength = 0;
} else if (opResult.inputConsumed == 0) {
@@ -185,17 +185,18 @@
if (bufferedOutput == null) {
// No output produced
- return EMPTY_BYTE_ARRAY;
+ return EmptyArray.BYTE;
} else {
return bufferedOutput.toByteArray();
}
}
+ @Override
public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
throws KeyStoreException {
if (inputLength == 0) {
// No input provided -- simplify the rest of the code
- input = EMPTY_BYTE_ARRAY;
+ input = EmptyArray.BYTE;
inputOffset = 0;
}
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
new file mode 100644
index 0000000..2fb8f20
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.security.keystore;
+
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
+ * {@code update} and {@code finish} operations.
+ *
+ * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
+ * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
+ * operation may consume less data than provided, in which case the caller has to buffer the
+ * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
+ * various JCA crypto primitives.
+ *
+ * @hide
+ */
+interface KeyStoreCryptoOperationStreamer {
+ byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
+ byte[] doFinal(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
+}
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 38a6a21..6815254 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -30,35 +30,36 @@
#define UI_THREAD_FRAME_INFO_SIZE 9
enum class FrameInfoIndex {
- kFlags = 0,
- kIntendedVsync,
- kVsync,
- kOldestInputEvent,
- kNewestInputEvent,
- kHandleInputStart,
- kAnimationStart,
- kPerformTraversalsStart,
- kDrawStart,
+ Flags = 0,
+ IntendedVsync,
+ Vsync,
+ OldestInputEvent,
+ NewestInputEvent,
+ HandleInputStart,
+ AnimationStart,
+ PerformTraversalsStart,
+ DrawStart,
// End of UI frame info
- kSyncStart,
- kIssueDrawCommandsStart,
- kSwapBuffers,
- kFrameCompleted,
+ SyncStart,
+ IssueDrawCommandsStart,
+ SwapBuffers,
+ FrameCompleted,
// Must be the last value!
- kNumIndexes
+ NumIndexes
};
extern const std::string FrameInfoNames[];
-enum class FrameInfoFlags {
- kWindowLayoutChanged = 1 << 0,
- kRTAnimation = 1 << 1,
- kSurfaceCanvas = 1 << 2,
- kSkippedFrame = 1 << 3,
+namespace FrameInfoFlags {
+ enum {
+ WindowLayoutChanged = 1 << 0,
+ RTAnimation = 1 << 1,
+ SurfaceCanvas = 1 << 2,
+ SkippedFrame = 1 << 3,
+ };
};
-MAKE_FLAGS_ENUM(FrameInfoFlags)
class ANDROID_API UiFrameInfoBuilder {
public:
@@ -67,19 +68,19 @@
}
UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) {
- set(FrameInfoIndex::kVsync) = vsyncTime;
- set(FrameInfoIndex::kIntendedVsync) = intendedVsync;
+ set(FrameInfoIndex::Vsync) = vsyncTime;
+ set(FrameInfoIndex::IntendedVsync) = intendedVsync;
// Pretend the other fields are all at vsync, too, so that naive
// duration calculations end up being 0 instead of very large
- set(FrameInfoIndex::kHandleInputStart) = vsyncTime;
- set(FrameInfoIndex::kAnimationStart) = vsyncTime;
- set(FrameInfoIndex::kPerformTraversalsStart) = vsyncTime;
- set(FrameInfoIndex::kDrawStart) = vsyncTime;
+ set(FrameInfoIndex::HandleInputStart) = vsyncTime;
+ set(FrameInfoIndex::AnimationStart) = vsyncTime;
+ set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
+ set(FrameInfoIndex::DrawStart) = vsyncTime;
return *this;
}
- UiFrameInfoBuilder& addFlag(FrameInfoFlags flag) {
- set(FrameInfoIndex::kFlags) |= static_cast<uint64_t>(flag);
+ UiFrameInfoBuilder& addFlag(int frameInfoFlag) {
+ set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag);
return *this;
}
@@ -96,32 +97,32 @@
void importUiThreadInfo(int64_t* info);
void markSyncStart() {
- set(FrameInfoIndex::kSyncStart) = systemTime(CLOCK_MONOTONIC);
+ set(FrameInfoIndex::SyncStart) = systemTime(CLOCK_MONOTONIC);
}
void markIssueDrawCommandsStart() {
- set(FrameInfoIndex::kIssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC);
+ set(FrameInfoIndex::IssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC);
}
void markSwapBuffers() {
- set(FrameInfoIndex::kSwapBuffers) = systemTime(CLOCK_MONOTONIC);
+ set(FrameInfoIndex::SwapBuffers) = systemTime(CLOCK_MONOTONIC);
}
void markFrameCompleted() {
- set(FrameInfoIndex::kFrameCompleted) = systemTime(CLOCK_MONOTONIC);
+ set(FrameInfoIndex::FrameCompleted) = systemTime(CLOCK_MONOTONIC);
}
- void addFlag(FrameInfoFlags flag) {
- set(FrameInfoIndex::kFlags) |= static_cast<uint64_t>(flag);
+ void addFlag(int frameInfoFlag) {
+ set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag);
}
int64_t operator[](FrameInfoIndex index) const {
- if (index == FrameInfoIndex::kNumIndexes) return 0;
+ if (index == FrameInfoIndex::NumIndexes) return 0;
return mFrameInfo[static_cast<int>(index)];
}
int64_t operator[](int index) const {
- if (index < 0 || index >= static_cast<int>(FrameInfoIndex::kNumIndexes)) return 0;
+ if (index < 0 || index >= static_cast<int>(FrameInfoIndex::NumIndexes)) return 0;
return mFrameInfo[index];
}
@@ -130,7 +131,7 @@
return mFrameInfo[static_cast<int>(index)];
}
- int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::kNumIndexes)];
+ int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::NumIndexes)];
};
} /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 95b0995..9557cb0 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -46,15 +46,15 @@
};
static const std::array<BarSegment,9> Bar {{
- { FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync, 0x00695C },
- { FrameInfoIndex::kVsync, FrameInfoIndex::kHandleInputStart, 0x00796B },
- { FrameInfoIndex::kHandleInputStart, FrameInfoIndex::kAnimationStart, 0x00897B },
- { FrameInfoIndex::kAnimationStart, FrameInfoIndex::kPerformTraversalsStart, 0x009688 },
- { FrameInfoIndex::kPerformTraversalsStart, FrameInfoIndex::kDrawStart, 0x26A69A},
- { FrameInfoIndex::kDrawStart, FrameInfoIndex::kSyncStart, 0x2196F3},
- { FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart, 0x4FC3F7},
- { FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kSwapBuffers, 0xF44336},
- { FrameInfoIndex::kSwapBuffers, FrameInfoIndex::kFrameCompleted, 0xFF9800},
+ { FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync, 0x00695C },
+ { FrameInfoIndex::Vsync, FrameInfoIndex::HandleInputStart, 0x00796B },
+ { FrameInfoIndex::HandleInputStart, FrameInfoIndex::AnimationStart, 0x00897B },
+ { FrameInfoIndex::AnimationStart, FrameInfoIndex::PerformTraversalsStart, 0x009688 },
+ { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x26A69A},
+ { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3},
+ { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7},
+ { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336},
+ { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, 0xFF9800},
}};
static int dpToPx(int dp, float density) {
@@ -137,7 +137,7 @@
for (size_t fi = 0, ri = 0; fi < mFrameSource.size(); fi++, ri += 4) {
// TODO: Skipped frames will leave little holes in the graph, but this
// is better than bogus and freaky lines, so...
- if (mFrameSource[fi][FrameInfoIndex::kFlags] & FrameInfoFlags::kSkippedFrame) {
+ if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
continue;
}
@@ -166,7 +166,7 @@
size_t fi = mFrameSource.size() - 1;
size_t ri = fi * 4;
float top = baseline - (mVerticalUnit * duration(fi,
- FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kIssueDrawCommandsStart));
+ FrameInfoIndex::IntendedVsync, FrameInfoIndex::IssueDrawCommandsStart));
canvas->drawRect(mRects[ri], top, mRects[ri + 2], baseline, &paint);
}
@@ -214,15 +214,15 @@
fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
for (size_t i = 0; i < mFrameSource.size(); i++) {
- if (mFrameSource[i][FrameInfoIndex::kIntendedVsync] <= mLastFrameLogged) {
+ if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
continue;
}
- mLastFrameLogged = mFrameSource[i][FrameInfoIndex::kIntendedVsync];
+ mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
- duration(i, FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kSyncStart),
- duration(i, FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart),
- duration(i, FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kSwapBuffers),
- duration(i, FrameInfoIndex::kSwapBuffers, FrameInfoIndex::kFrameCompleted));
+ duration(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
+ duration(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
+ duration(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
+ duration(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
}
fflush(file);
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index cc5c403..5c0801e 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -40,11 +40,11 @@
};
static const Comparison COMPARISONS[] = {
- {FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync},
- {FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync},
- {FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart},
- {FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart},
- {FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted},
+ {FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync},
+ {FrameInfoIndex::OldestInputEvent, FrameInfoIndex::Vsync},
+ {FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
+ {FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
+ {FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
};
// If the event exceeds 10 seconds throw it away, this isn't a jank event
@@ -64,8 +64,8 @@
* time on the RenderThread, figure out how to attribute that as a jank-causer
*/
static const int64_t EXEMPT_FRAMES_FLAGS
- = FrameInfoFlags::kWindowLayoutChanged
- | FrameInfoFlags::kSurfaceCanvas;
+ = FrameInfoFlags::WindowLayoutChanged
+ | FrameInfoFlags::SurfaceCanvas;
// The bucketing algorithm controls so to speak
// If a frame is <= to this it goes in bucket 0
@@ -206,7 +206,7 @@
mData->totalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration =
- frame[FrameInfoIndex::kFrameCompleted] - frame[FrameInfoIndex::kIntendedVsync];
+ frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::IntendedVsync];
uint32_t framebucket = frameCountIndexForFrameTime(
totalDuration, mData->frameCounts.size());
// Keep the fast path as fast as possible.
@@ -215,7 +215,7 @@
return;
}
- if (frame[FrameInfoIndex::kFlags] & EXEMPT_FRAMES_FLAGS) {
+ if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
return;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index acf9c13..b88f30e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -157,7 +157,7 @@
}
static bool wasSkipped(FrameInfo* info) {
- return info && ((*info)[FrameInfoIndex::kFlags] & FrameInfoFlags::kSkippedFrame);
+ return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
}
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo) {
@@ -185,7 +185,7 @@
}
if (CC_UNLIKELY(!mNativeWindow.get())) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame);
+ mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
info.out.canDrawThisFrame = false;
return;
}
@@ -199,7 +199,7 @@
info.out.canDrawThisFrame = !runningBehind;
if (!info.out.canDrawThisFrame) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame);
+ mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
}
if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
@@ -228,7 +228,7 @@
mDamageAccumulator.finish(&dirty);
if (dirty.isEmpty() && Properties::skipEmptyFrames) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame);
+ mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
return;
}
@@ -288,7 +288,7 @@
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
UiFrameInfoBuilder(frameInfo)
- .addFlag(FrameInfoFlags::kRTAnimation)
+ .addFlag(FrameInfoFlags::RTAnimation)
.setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
mRenderThread.timeLord().latestVsync());
@@ -400,17 +400,17 @@
void CanvasContext::dumpFrames(int fd) {
FILE* file = fdopen(fd, "a");
fprintf(file, "\n\n---PROFILEDATA---\n");
- for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::kNumIndexes); i++) {
+ for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
fprintf(file, "%s", FrameInfoNames[i].c_str());
fprintf(file, ",");
}
for (size_t i = 0; i < mFrames.size(); i++) {
FrameInfo& frame = mFrames[i];
- if (frame[FrameInfoIndex::kSyncStart] == 0) {
+ if (frame[FrameInfoIndex::SyncStart] == 0) {
continue;
}
fprintf(file, "\n");
- for (int i = 0; i < static_cast<int>(FrameInfoIndex::kNumIndexes); i++) {
+ for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
fprintf(file, "%" PRId64 ",", frame[i]);
}
}
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 83af4ae..008e297 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -110,7 +110,7 @@
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
- int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::kVsync)];
+ int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
mRenderThread->timeLord().vsyncReceived(vsync);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 1b31059..5ca9083 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -35,30 +35,4 @@
static_assert(std::is_standard_layout<Type>::value, \
#Type " must have standard layout")
-#define MAKE_FLAGS_ENUM(enumType) \
- inline void operator|=(int& lhs, enumType rhs) { \
- lhs |= static_cast<int>(rhs); \
- } \
- inline int operator|(int lhs, enumType rhs) { \
- return lhs | static_cast<int>(rhs); \
- } \
- inline int operator|(enumType lhs, int rhs) { \
- return static_cast<int>(lhs) | rhs; \
- } \
- inline int operator|(enumType lhs, enumType rhs) { \
- return static_cast<int>(lhs) | static_cast<int>(rhs); \
- } \
- inline void operator&=(int& lhs, enumType rhs) { \
- lhs &= static_cast<int>(rhs); \
- } \
- inline int operator&(int lhs, enumType rhs) { \
- return lhs & static_cast<int>(rhs); \
- } \
- inline int operator&(enumType lhs, int rhs) { \
- return static_cast<int>(lhs) & rhs; \
- } \
- inline int operator&(enumType lhs, enumType rhs) { \
- return static_cast<int>(lhs) & static_cast<int>(rhs); \
- }
-
#endif /* MACROS_H */
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index a9accb02..d7968d2 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -301,17 +301,19 @@
status_t err = crypto->setMediaDrmSession(sessionId);
- String8 msg("setMediaDrmSession failed");
- if (err == ERROR_DRM_SESSION_NOT_OPENED) {
- msg += ": session not opened";
- } else if (err == ERROR_UNSUPPORTED) {
- msg += ": not supported by this crypto scheme";
- } else if (err == NO_INIT) {
- msg += ": crypto plugin not initialized";
- } else if (err != OK) {
- msg.appendFormat(": general failure (%d)", err);
+ if (err != OK) {
+ String8 msg("setMediaDrmSession failed");
+ if (err == ERROR_DRM_SESSION_NOT_OPENED) {
+ msg += ": session not opened";
+ } else if (err == ERROR_UNSUPPORTED) {
+ msg += ": not supported by this crypto scheme";
+ } else if (err == NO_INIT) {
+ msg += ": crypto plugin not initialized";
+ } else {
+ msg.appendFormat(": general failure (%d)", err);
+ }
+ jniThrowException(env, "android/media/MediaCryptoException", msg.string());
}
- jniThrowException(env, "android/media/MediaCryptoException", msg.string());
}
static JNINativeMethod gMethods[] = {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 29c3c75..cd0e587 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -731,27 +731,31 @@
@SmallTest
public void testReadWriteHighSpeedVideoConfiguration() {
- // int32 x 4 x 1
+ // int32 x 5 x 1
checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
new HighSpeedVideoConfiguration(
- /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200),
+ /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200,
+ /*batchSizeMax*/8),
/* width, height, fpsMin, fpsMax */
- toByteArray(1000, 255, 30, 200));
+ toByteArray(1000, 255, 30, 200, 8));
- // int32 x 4 x 3
+ // int32 x 5 x 3
checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
new HighSpeedVideoConfiguration[] {
new HighSpeedVideoConfiguration(
- /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120),
+ /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120,
+ /*batchSizeMax*/8),
new HighSpeedVideoConfiguration(
- /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200),
+ /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200,
+ /*batchSizeMax*/4),
new HighSpeedVideoConfiguration(
- /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60)
+ /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60,
+ /*batchSizeMax*/2)
},
toByteArray(
- 1280, 720, 60, 120,
- 123, 456, 1, 200,
- 4096, 2592, 30, 60
+ 1280, 720, 60, 120, 8,
+ 123, 456, 1, 200, 4,
+ 4096, 2592, 30, 60, 2
));
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index c702673..bbd3e60 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -35,6 +35,7 @@
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.util.MutableBoolean;
import android.util.Pair;
import android.view.Display;
import android.view.LayoutInflater;
@@ -57,7 +58,6 @@
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Annotation for a method that is only called from the primary user's SystemUI process and will be
@@ -362,7 +362,12 @@
// RecentsActivity)
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
sInstanceLoadPlan = loader.createLoadPlan(mContext);
- sInstanceLoadPlan.preloadRawTasks(true);
+
+ ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
+ if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
+ sInstanceLoadPlan.preloadRawTasks(isTopTaskHome.value);
+ }
}
@Override
@@ -546,7 +551,7 @@
// If Recents is the front most activity, then we should just communicate with it directly
// to launch the first task or dismiss itself
ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- AtomicBoolean isTopTaskHome = new AtomicBoolean(true);
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
// Notify recents to toggle itself
Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
@@ -555,7 +560,7 @@
return;
} else {
// Otherwise, start the recents activity
- startRecentsActivity(topTask, isTopTaskHome.get());
+ startRecentsActivity(topTask, isTopTaskHome.value);
}
}
@@ -563,9 +568,9 @@
void startRecentsActivity() {
// Check if the top task is in the home stack, and start the recents activity
ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- AtomicBoolean isTopTaskHome = new AtomicBoolean(true);
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
- startRecentsActivity(topTask, isTopTaskHome.get());
+ startRecentsActivity(topTask, isTopTaskHome.value);
}
}
@@ -654,6 +659,7 @@
if (task == null) {
// If no task is specified or we can not find the task just use the front most one
task = tasks.get(tasks.size() - 1);
+ runningTaskOut.copyFrom(task);
}
// Get the transform for the running task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index ca0f357..272d39a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -54,6 +54,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
+import android.util.MutableBoolean;
import android.util.Pair;
import android.util.SparseArray;
import android.view.Display;
@@ -67,12 +68,9 @@
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Acts as a shim around the real system services that we need to access data from, and provides
@@ -192,7 +190,7 @@
// Break early if we can't get a valid set of tasks
if (tasks == null) {
- return new ArrayList<ActivityManager.RecentTaskInfo>();
+ return new ArrayList<>();
}
boolean isFirstValidTask = true;
@@ -235,7 +233,7 @@
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
- AtomicBoolean isHomeTopMost) {
+ MutableBoolean isHomeTopMost) {
if (topTask != null) {
ComponentName topActivity = topTask.topActivity;
@@ -243,13 +241,13 @@
if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
topActivity.getClassName().equals(Recents.sRecentsActivity)) {
if (isHomeTopMost != null) {
- isHomeTopMost.set(false);
+ isHomeTopMost.value = false;
}
return true;
}
if (isHomeTopMost != null) {
- isHomeTopMost.set(isInHomeStack(topTask.id));
+ isHomeTopMost.value = isInHomeStack(topTask.id);
}
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 40cd211..f40c58d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -104,13 +104,9 @@
if (mRawTasks == null) {
preloadRawTasks(isTopTaskHome);
}
- int firstStackId = -1;
int taskCount = mRawTasks.size();
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
- if (firstStackId < 0) {
- firstStackId = t.stackId;
- }
// Compose the task key
Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
@@ -158,17 +154,17 @@
if (!mConfig.multiStackEnabled ||
Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
- firstStackId = 0;
+ int firstStackId = 0;
ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
if (stackTasks == null) {
- stackTasks = new ArrayList<Task>();
+ stackTasks = new ArrayList<>();
stacksTasks.put(firstStackId, stackTasks);
}
stackTasks.add(task);
} else {
ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
if (stackTasks == null) {
- stackTasks = new ArrayList<Task>();
+ stackTasks = new ArrayList<>();
stacksTasks.put(t.stackId, stackTasks);
}
stackTasks.add(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b3e6221..cec613c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -550,7 +550,7 @@
if (tv == null) {
launchRunnable.run();
} else {
- if (!task.group.isFrontMostTask(task)) {
+ if (task.group != null && !task.group.isFrontMostTask(task)) {
// For affiliated tasks that are behind other tasks, we must animate the front cards
// out of view before starting the task transition
stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d4c3194..7bcbcfb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -166,8 +166,6 @@
private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList =
new ArrayList<>();
- private final Region mTempRegion = new Region();
-
private final Rect mTempRect = new Rect();
private final Rect mTempRect1 = new Rect();
@@ -2241,7 +2239,7 @@
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2265,6 +2263,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2282,6 +2281,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
@@ -2293,7 +2296,7 @@
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2317,6 +2320,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2334,6 +2338,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
@@ -2345,7 +2353,7 @@
long interrogatingTid) throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2369,6 +2377,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2386,6 +2395,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
@@ -2397,7 +2410,7 @@
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2422,6 +2435,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2439,6 +2453,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
@@ -2450,7 +2468,7 @@
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2474,6 +2492,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2491,6 +2510,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e07e4e2..2e4b881 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3819,10 +3819,12 @@
// }
updateTcpBufferSizes(networkAgent);
- // TODO: deprecate and remove mDefaultDns when we can do so safely.
- // For now, use it only when the network has Internet access. http://b/18327075
- final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
- NET_CAPABILITY_INTERNET);
+ // TODO: deprecate and remove mDefaultDns when we can do so safely. See http://b/18327075
+ // In L, we used it only when the network had Internet access but provided no DNS servers.
+ // For now, just disable it, and if disabling it doesn't break things, remove it.
+ // final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
+ // NET_CAPABILITY_INTERNET);
+ final boolean useDefaultDns = false;
final boolean flushDns = updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId, flushDns, useDefaultDns);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 3315c89..21f96c9 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1785,7 +1785,13 @@
// Get the calling package. We will use it for the purpose of caching.
final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
- List<String> callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
+ List<String> callerOwnedPackageNames;
+ long ident = Binder.clearCallingIdentity();
+ try {
+ callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
String msg = String.format(
"Uid %s is attempting to illegally masquerade as package %s!",
@@ -1798,15 +1804,15 @@
loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
- // Distill the caller's package signatures into a single digest.
- final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
-
if (notifyOnAuthFailure) {
loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
}
long identityToken = clearCallingIdentity();
try {
+ // Distill the caller's package signatures into a single digest.
+ final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
+
// if the caller has permission, do the peek. otherwise go the more expensive
// route of starting a Session
if (!customTokens && permissionGranted) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 745cb7e..80b8a93 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1121,25 +1121,23 @@
}
final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
- if (r.nextReceiver > 0) {
- Object curReceiver = r.receivers.get(r.nextReceiver-1);
+ final int logIndex = r.nextReceiver - 1;
+ if (logIndex >= 0 && logIndex < r.receivers.size()) {
+ Object curReceiver = r.receivers.get(logIndex);
if (curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter) curReceiver;
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
bf.owningUserId, System.identityHashCode(r),
- r.intent.getAction(),
- r.nextReceiver - 1,
- System.identityHashCode(bf));
+ r.intent.getAction(), logIndex, System.identityHashCode(bf));
} else {
- ResolveInfo ri = (ResolveInfo)curReceiver;
+ ResolveInfo ri = (ResolveInfo) curReceiver;
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
- System.identityHashCode(r), r.intent.getAction(),
- r.nextReceiver - 1, ri.toString());
+ System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
}
} else {
- Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
- + r);
+ if (logIndex < 0) Slog.w(TAG,
+ "Discarding broadcast before first receiver is invoked: " + r);
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-1, System.identityHashCode(r),
r.intent.getAction(),
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b8d0692..46793b9 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1087,16 +1087,19 @@
}
}
- private void sleepPress(KeyEvent event) {
+ private void sleepPress(long eventTime) {
+ if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
+ launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
+ }
+ }
+
+ private void sleepRelease(long eventTime) {
switch (mShortPressOnSleepBehavior) {
case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
- mPowerManager.goToSleep(event.getEventTime(),
- PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
- break;
case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
- launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
- mPowerManager.goToSleep(event.getEventTime(),
- PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+ Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
+ mPowerManager.goToSleep(eventTime,
+ PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
break;
}
}
@@ -2116,7 +2119,9 @@
case TYPE_KEYGUARD_SCRIM:
return false;
default:
- return true;
+ // Hide only windows below the keyguard host window.
+ return windowTypeToLayerLw(win.getBaseType())
+ < windowTypeToLayerLw(TYPE_STATUS_BAR);
}
}
@@ -4903,7 +4908,11 @@
if (!mPowerManager.isInteractive()) {
useHapticFeedback = false; // suppress feedback if already non-interactive
}
- sleepPress(event);
+ if (down) {
+ sleepPress(event.getEventTime());
+ } else {
+ sleepRelease(event.getEventTime());
+ }
break;
}
@@ -5517,6 +5526,12 @@
/** {@inheritDoc} */
@Override
+ public boolean isKeyguardShowingOrOccluded() {
+ return mKeyguardDelegate == null ? false : mKeyguardDelegate.isShowing();
+ }
+
+ /** {@inheritDoc} */
+ @Override
public boolean inKeyguardRestrictedKeyInputMode() {
if (mKeyguardDelegate == null) return false;
return mKeyguardDelegate.isInputRestricted();
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index 01c110f..f1f9c50 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -36,10 +36,11 @@
// Keyguard changes its state, it always triggers a layout in window manager. Because
// IKeyguardStateCallback is synchronous and because these states are declared volatile, it's
// guaranteed that window manager picks up the new state all the time in the layout caused by
- // the state change of Keyguard.
- private volatile boolean mIsShowing;
- private volatile boolean mSimSecure;
- private volatile boolean mInputRestricted;
+ // the state change of Keyguard. To be extra safe, assume most restrictive values until Keyguard
+ // tells us the actual value.
+ private volatile boolean mIsShowing = true;
+ private volatile boolean mSimSecure = true;
+ private volatile boolean mInputRestricted = true;
private int mCurrentUserId;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 9169351..3305e1e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -191,7 +191,8 @@
private boolean shouldForceHide(WindowState win) {
final WindowState imeTarget = mService.mInputMethodTarget;
final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleNow() &&
- (imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+ ((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0
+ || !mPolicy.canBeForceHidden(imeTarget, imeTarget.mAttrs));
final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
@@ -203,7 +204,11 @@
|| (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.isAnimatingLw()
// Show error dialogs over apps that dismiss keyguard.
|| (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0)));
- return (mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked;
+
+ // Only hide windows if the keyguard is active and not animating away.
+ boolean keyguardOn = mPolicy.isKeyguardShowingOrOccluded()
+ && mForceHiding != KEYGUARD_ANIMATING_OUT;
+ return keyguardOn && hideWhenLocked;
}
private void updateWindowsLocked(final int displayId) {
@@ -228,6 +233,7 @@
winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
winAnimator.mAnimationIsEntrance = false;
winAnimator.mAnimationStartTime = -1;
+ winAnimator.mKeyguardGoingAwayAnimation = true;
}
} else {
if (DEBUG_KEYGUARD) Slog.d(TAG,
@@ -310,7 +316,7 @@
mKeyguardGoingAway = false;
}
if (win.isReadyForDisplay()) {
- if (nowAnimating) {
+ if (nowAnimating && win.mWinAnimator.mKeyguardGoingAwayAnimation) {
mForceHiding = KEYGUARD_ANIMATING_OUT;
} else {
mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
index c0e1d19..334f708 100644
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -30,8 +30,8 @@
private final Inet4Address mSrcIp;
DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address clientIp, byte[] clientMac) {
- super(transId, secs, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
+ Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
+ super(transId, secs, clientIp, yourIp, serverAddress, INADDR_ANY, clientMac, broadcast);
mBroadcast = broadcast;
mSrcIp = serverAddress;
}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index d8d74e2..069b591 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -601,12 +601,15 @@
/**
* Retransmits packets using jittered exponential backoff with an optional timeout. Packet
- * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm.
+ * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
+ * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
+ * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
+ * state.
*
* Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
* packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
- * sent by the receive thread. They may implement timeout, which is called when the timeout
- * fires.
+ * sent by the receive thread. They may also set mTimeout and if desired override the default
+ * timeout implementation.
*/
abstract class PacketRetransmittingState extends LoggingState {
@@ -647,7 +650,19 @@
abstract protected boolean sendPacket();
abstract protected void receivePacket(DhcpPacket packet);
- protected void timeout() {}
+
+ // Default implementation of timeout. This is only invoked if mTimeout > 0, so it will never
+ // be called if the subclass does not set a timeout.
+ protected void timeout() {
+ maybeLog("Timeout in " + getName());
+ notifyFailure();
+ if (this != mDhcpInitState) {
+ // Only transition to INIT if we're not already there. Otherwise, we'll exit the
+ // state and re-enter it, which will reset the packet transmission interval, re-set
+ // the timeout, etc.
+ transitionTo(mDhcpInitState);
+ }
+ }
protected void initTimer() {
mTimer = FIRST_TIMEOUT_MS;
@@ -696,11 +711,6 @@
return sendDiscoverPacket();
}
- protected void timeout() {
- maybeLog("Timeout");
- notifyFailure();
- }
-
protected void receivePacket(DhcpPacket packet) {
if (!isValidPacket(packet)) return;
if (!(packet instanceof DhcpOfferPacket)) return;
@@ -747,11 +757,6 @@
transitionTo(mDhcpInitState);
}
}
-
- protected void timeout() {
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
}
class DhcpHaveAddressState extends LoggingState {
@@ -760,6 +765,8 @@
super.enter();
if (!setIpAddress(mDhcpLease.ipAddress)) {
notifyFailure();
+ // There's likely no point in going into DhcpInitState here, we'll probably just
+ // repeat the transaction, get the same IP address as before, and fail.
transitionTo(mStoppedState);
}
}
@@ -797,7 +804,6 @@
}
}
- // TODO: timeout.
class DhcpRenewingState extends PacketRetransmittingState {
public DhcpRenewingState() {
super();
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
index af41708..7ca7100 100644
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -32,8 +32,8 @@
* Generates a OFFER packet with the specified parameters.
*/
DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address clientIp, byte[] clientMac) {
- super(transId, secs, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+ Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
+ super(transId, secs, clientIp, yourIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
mSrcIp = serverAddress;
}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 2a25d30..d42404b 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -919,7 +919,7 @@
break;
case DHCP_MESSAGE_TYPE_OFFER:
newPacket = new DhcpOfferPacket(
- transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
+ transactionId, secs, broadcast, ipSrc, clientIp, yourIp, clientMac);
break;
case DHCP_MESSAGE_TYPE_REQUEST:
newPacket = new DhcpRequestPacket(
@@ -932,7 +932,7 @@
break;
case DHCP_MESSAGE_TYPE_ACK:
newPacket = new DhcpAckPacket(
- transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
+ transactionId, secs, broadcast, ipSrc, clientIp, yourIp, clientMac);
break;
case DHCP_MESSAGE_TYPE_NAK:
newPacket = new DhcpNakPacket(
@@ -982,9 +982,9 @@
*/
public DhcpResults toDhcpResults() {
Inet4Address ipAddress = mYourIp;
- if (ipAddress == Inet4Address.ANY) {
+ if (ipAddress.equals(Inet4Address.ANY)) {
ipAddress = mClientIp;
- if (ipAddress == Inet4Address.ANY) {
+ if (ipAddress.equals(Inet4Address.ANY)) {
return null;
}
}
@@ -1052,7 +1052,7 @@
Inet4Address gateway, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName) {
DhcpPacket pkt = new DhcpOfferPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
+ transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
pkt.mGateway = gateway;
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
@@ -1072,7 +1072,7 @@
Inet4Address gateway, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName) {
DhcpPacket pkt = new DhcpAckPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
+ transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
pkt.mGateway = gateway;
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index f116042..e0e3fcf 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -18,6 +18,7 @@
import android.net.NetworkUtils;
import android.net.DhcpResults;
+import android.net.LinkAddress;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -34,19 +35,27 @@
(Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
private static Inet4Address CLIENT_ADDR =
(Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.234");
+ // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
+ // doesn't use == instead of equals when comparing addresses.
+ private static Inet4Address ANY = (Inet4Address) NetworkUtils.numericToInetAddress("0.0.0.0");
+
private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
class TestDhcpPacket extends DhcpPacket {
private byte mType;
// TODO: Make this a map of option numbers to bytes instead.
- private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes;
+ private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
- public TestDhcpPacket(byte type) {
- super(0xdeadbeef, (short) 0, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY,
+ public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
+ super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
CLIENT_MAC, true);
mType = type;
}
+ public TestDhcpPacket(byte type) {
+ this(type, INADDR_ANY, CLIENT_ADDR);
+ }
+
public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
mDomainBytes = domainBytes;
return this;
@@ -62,6 +71,11 @@
return this;
}
+ public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
+ mNetmaskBytes = netmaskBytes;
+ return this;
+ }
+
public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
@@ -80,6 +94,9 @@
if (mLeaseTimeBytes != null) {
addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
}
+ if (mNetmaskBytes != null) {
+ addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
+ }
addTlvEnd(buffer);
}
@@ -175,4 +192,50 @@
assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
}
+
+ private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
+ byte[] netmaskBytes) {
+ checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
+ checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
+ }
+
+ private void checkIpAddress(String expected, byte type,
+ Inet4Address clientIp, Inet4Address yourIp,
+ byte[] netmaskBytes) {
+ ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
+ .setNetmaskBytes(netmaskBytes)
+ .build();
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ DhcpResults results = offerPacket.toDhcpResults();
+
+ if (expected != null) {
+ LinkAddress expectedAddress = new LinkAddress(expected);
+ assertEquals(expectedAddress, results.ipAddress);
+ } else {
+ assertNull(results);
+ }
+ }
+
+ @SmallTest
+ public void testIpAddress() throws Exception {
+ byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
+ byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
+ byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
+ Inet4Address example1 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
+ Inet4Address example2 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.43");
+
+ // A packet without any addresses is not valid.
+ checkIpAddress(null, ANY, ANY, slash24Netmask);
+
+ // ClientIP is used iff YourIP is not present.
+ checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
+ checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
+ checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
+
+ // Invalid netmasks are ignored.
+ checkIpAddress(null, example2, ANY, invalidNetmask);
+
+ // If there is no netmask, implicit netmasks are used.
+ checkIpAddress("192.0.2.43/24", ANY, example2, null);
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1d751d5..dfb02bb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -51,6 +51,10 @@
public static final String
ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ // Below are the keys used in carrier config bundles. To add a new variable, define the key and
+ // give it a default value in sDefaults. If you need to ship a per-network override in the
+ // system image, that can be added in packages/apps/CarrierConfig.
+
/**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 5f430f0..a3dc077 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1007,7 +1007,7 @@
((ParcelableScanResults) msg.obj).getResults());
return;
case CMD_SINGLE_SCAN_COMPLETED:
- Log.d(TAG, "removing listener for single scan");
+ if (DBG) Log.d(TAG, "removing listener for single scan");
removeListener(msg.arg2);
break;
default: