summaryrefslogtreecommitdiff
path: root/android/hardware
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2018-04-03 23:21:57 -0400
committerJustin Klaassen <justinklaassen@google.com>2018-04-03 23:21:57 -0400
commit4d01eeaffaa720e4458a118baa137a11614f00f7 (patch)
tree66751893566986236788e3c796a7cc5e90d05f52 /android/hardware
parenta192cc2a132cb0ee8588e2df755563ec7008c179 (diff)
downloadandroid-28-4d01eeaffaa720e4458a118baa137a11614f00f7.tar.gz
Import Android SDK Platform P [4697573]
/google/data/ro/projects/android/fetch_artifact \ --bid 4697573 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4697573.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: If80578c3c657366cc9cf75f8db13d46e2dd4e077
Diffstat (limited to 'android/hardware')
-rw-r--r--android/hardware/Camera.java15
-rw-r--r--android/hardware/ConsumerIrManager.java3
-rw-r--r--android/hardware/Sensor.java4
-rw-r--r--android/hardware/biometrics/BiometricAuthenticator.java184
-rw-r--r--android/hardware/biometrics/BiometricConstants.java165
-rw-r--r--android/hardware/biometrics/BiometricDialog.java494
-rw-r--r--android/hardware/biometrics/BiometricFingerprintConstants.java168
-rw-r--r--android/hardware/biometrics/CryptoObject.java79
-rw-r--r--android/hardware/camera2/CameraCaptureSession.java174
-rw-r--r--android/hardware/camera2/CameraCharacteristics.java120
-rw-r--r--android/hardware/camera2/CameraDevice.java78
-rw-r--r--android/hardware/camera2/CameraManager.java285
-rw-r--r--android/hardware/camera2/CameraMetadata.java108
-rw-r--r--android/hardware/camera2/CaptureRequest.java86
-rw-r--r--android/hardware/camera2/CaptureResult.java162
-rw-r--r--android/hardware/camera2/TotalCaptureResult.java41
-rw-r--r--android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java85
-rw-r--r--android/hardware/camera2/dispatch/BroadcastDispatcher.java64
-rw-r--r--android/hardware/camera2/dispatch/Dispatchable.java35
-rw-r--r--android/hardware/camera2/dispatch/DuckTypingDispatcher.java55
-rw-r--r--android/hardware/camera2/dispatch/HandlerDispatcher.java85
-rw-r--r--android/hardware/camera2/dispatch/InvokeDispatcher.java55
-rw-r--r--android/hardware/camera2/dispatch/MethodNameInvoker.java97
-rw-r--r--android/hardware/camera2/dispatch/NullDispatcher.java38
-rw-r--r--android/hardware/camera2/impl/CallbackProxies.java179
-rw-r--r--android/hardware/camera2/impl/CameraCaptureSessionImpl.java357
-rw-r--r--android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java43
-rw-r--r--android/hardware/camera2/impl/CameraDeviceImpl.java303
-rw-r--r--android/hardware/camera2/impl/CameraMetadataNative.java60
-rw-r--r--android/hardware/camera2/impl/PhysicalCaptureResultInfo.java76
-rw-r--r--android/hardware/camera2/legacy/CameraDeviceUserShim.java7
-rw-r--r--android/hardware/camera2/legacy/LegacyCameraDevice.java6
-rw-r--r--android/hardware/camera2/legacy/LegacyExceptionUtils.java4
-rw-r--r--android/hardware/camera2/params/OisSample.java137
-rw-r--r--android/hardware/camera2/params/OutputConfiguration.java12
-rw-r--r--android/hardware/camera2/params/SessionConfiguration.java25
-rw-r--r--android/hardware/camera2/utils/SurfaceUtils.java14
-rw-r--r--android/hardware/camera2/utils/TaskDrainer.java26
-rw-r--r--android/hardware/camera2/utils/TaskSingleDrainer.java19
-rw-r--r--android/hardware/display/AmbientBrightnessDayStats.java240
-rw-r--r--android/hardware/display/BrightnessChangeEvent.java51
-rw-r--r--android/hardware/display/DisplayManager.java68
-rw-r--r--android/hardware/display/DisplayManagerGlobal.java52
-rw-r--r--android/hardware/display/DisplayManagerInternal.java7
-rw-r--r--android/hardware/display/WifiDisplay.java4
-rw-r--r--android/hardware/fingerprint/Fingerprint.java3
-rw-r--r--android/hardware/fingerprint/FingerprintDialog.java293
-rw-r--r--android/hardware/fingerprint/FingerprintManager.java294
-rw-r--r--android/hardware/hdmi/HdmiControlManager.java3
-rw-r--r--android/hardware/input/InputManager.java51
-rw-r--r--android/hardware/input/InputManagerInternal.java13
-rw-r--r--android/hardware/input/TouchCalibration.java6
-rw-r--r--android/hardware/location/ContextHubMessage.java2
-rw-r--r--android/hardware/location/NanoApp.java2
-rw-r--r--android/hardware/location/NanoAppFilter.java2
-rw-r--r--android/hardware/location/NanoAppInstanceInfo.java2
-rw-r--r--android/hardware/radio/ProgramList.java11
-rw-r--r--android/hardware/radio/ProgramSelector.java9
-rw-r--r--android/hardware/radio/RadioManager.java122
-rw-r--r--android/hardware/radio/RadioMetadata.java49
-rw-r--r--android/hardware/radio/RadioTuner.java61
-rw-r--r--android/hardware/radio/TunerAdapter.java36
-rw-r--r--android/hardware/radio/TunerCallbackAdapter.java51
-rw-r--r--android/hardware/radio/Utils.java23
-rw-r--r--android/hardware/soundtrigger/SoundTrigger.java227
-rw-r--r--android/hardware/usb/AccessoryFilter.java18
-rw-r--r--android/hardware/usb/DeviceFilter.java22
-rw-r--r--android/hardware/usb/UsbConfiguration.java12
-rw-r--r--android/hardware/usb/UsbConstants.java18
-rw-r--r--android/hardware/usb/UsbDeviceConnection.java14
-rw-r--r--android/hardware/usb/UsbManager.java303
-rw-r--r--android/hardware/usb/UsbRequest.java26
72 files changed, 4102 insertions, 1941 deletions
diff --git a/android/hardware/Camera.java b/android/hardware/Camera.java
index 931b5c91..9a276fbd 100644
--- a/android/hardware/Camera.java
+++ b/android/hardware/Camera.java
@@ -242,6 +242,9 @@ public class Camera {
/**
* Returns the number of physical cameras available on this device.
+ * The return value of this method might change dynamically if the device
+ * supports external cameras and an external camera is connected or
+ * disconnected.
*
* @return total number of accessible camera devices, or 0 if there are no
* cameras or an error was encountered enumerating them.
@@ -3532,8 +3535,8 @@ public class Camera {
/**
* Gets the focal length (in millimeter) of the camera.
*
- * @return the focal length. This method will always return a valid
- * value.
+ * @return the focal length. Returns -1.0 when the device
+ * doesn't report focal length information.
*/
public float getFocalLength() {
return Float.parseFloat(get(KEY_FOCAL_LENGTH));
@@ -3542,8 +3545,8 @@ public class Camera {
/**
* Gets the horizontal angle of view in degrees.
*
- * @return horizontal angle of view. This method will always return a
- * valid value.
+ * @return horizontal angle of view. Returns -1.0 when the device
+ * doesn't report view angle information.
*/
public float getHorizontalViewAngle() {
return Float.parseFloat(get(KEY_HORIZONTAL_VIEW_ANGLE));
@@ -3552,8 +3555,8 @@ public class Camera {
/**
* Gets the vertical angle of view in degrees.
*
- * @return vertical angle of view. This method will always return a
- * valid value.
+ * @return vertical angle of view. Returns -1.0 when the device
+ * doesn't report view angle information.
*/
public float getVerticalViewAngle() {
return Float.parseFloat(get(KEY_VERTICAL_VIEW_ANGLE));
diff --git a/android/hardware/ConsumerIrManager.java b/android/hardware/ConsumerIrManager.java
index c7a33ffa..6f589cd9 100644
--- a/android/hardware/ConsumerIrManager.java
+++ b/android/hardware/ConsumerIrManager.java
@@ -16,8 +16,10 @@
package android.hardware;
+import android.annotation.RequiresFeature;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -27,6 +29,7 @@ import android.util.Log;
* Class that operates consumer infrared on the device.
*/
@SystemService(Context.CONSUMER_IR_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_CONSUMER_IR)
public final class ConsumerIrManager {
private static final String TAG = "ConsumerIr";
diff --git a/android/hardware/Sensor.java b/android/hardware/Sensor.java
index 7fb0c89e..72974261 100644
--- a/android/hardware/Sensor.java
+++ b/android/hardware/Sensor.java
@@ -22,7 +22,9 @@ import android.os.Build;
/**
* Class representing a sensor. Use {@link SensorManager#getSensorList} to get
- * the list of available Sensors.
+ * the list of available sensors. For more information about Android sensors,
+ * read the
+ * <a href="/guide/topics/sensors/sensors_motion.html">Motion Sensors guide</a>.</p>
*
* @see SensorManager
* @see SensorEventListener
diff --git a/android/hardware/biometrics/BiometricAuthenticator.java b/android/hardware/biometrics/BiometricAuthenticator.java
new file mode 100644
index 00000000..c811999c
--- /dev/null
+++ b/android/hardware/biometrics/BiometricAuthenticator.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.CancellationSignal;
+import android.os.Parcelable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This is the common interface that all biometric authentication classes should implement.
+ * @hide
+ */
+public interface BiometricAuthenticator {
+
+ /**
+ * Container for biometric data
+ * @hide
+ */
+ abstract class BiometricIdentifier implements Parcelable {}
+
+ /**
+ * Container for callback data from {@link BiometricAuthenticator#authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} and
+ * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ */
+ class AuthenticationResult {
+ private BiometricIdentifier mIdentifier;
+ private CryptoObject mCryptoObject;
+ private int mUserId;
+
+ /**
+ * @hide
+ */
+ public AuthenticationResult() { }
+
+ /**
+ * Authentication result
+ * @param crypto
+ * @param identifier
+ * @param userId
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ int userId) {
+ mCryptoObject = crypto;
+ mIdentifier = identifier;
+ mUserId = userId;
+ }
+
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+ */
+ public CryptoObject getCryptoObject() {
+ return mCryptoObject;
+ }
+
+ /**
+ * Obtain the biometric identifier associated with this operation. Applications are strongly
+ * discouraged from associating specific identifiers with specific applications or
+ * operations.
+ * @hide
+ */
+ public BiometricIdentifier getId() {
+ return mIdentifier;
+ }
+
+ /**
+ * Obtain the userId for which this biometric was authenticated.
+ * @hide
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+ };
+
+ /**
+ * Callback structure provided to {@link BiometricAuthenticator#authenticate(CancellationSignal,
+ * Executor, AuthenticationCallback)} or {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}. Users must provide
+ * an implementation of this for listening to biometric events.
+ */
+ abstract class AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further actions will be made on this object.
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown on an UI
+ */
+ public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+ * please clean it."
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown on an UI
+ */
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+ /**
+ * Called when a biometric is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+ /**
+ * Called when a biometric is valid but not recognized.
+ */
+ public void onAuthenticationFailed() {}
+
+ /**
+ * Called when a biometric has been acquired, but hasn't been processed yet.
+ * @hide
+ */
+ public void onAuthenticationAcquired(int acquireInfo) {}
+ };
+
+ /**
+ * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+ * when {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called, at which point the crypto object becomes invalid. This
+ * operation can be canceled by using the provided cancel object. The application wil receive
+ * authentication errors through {@link AuthenticationCallback}. Calling
+ * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)} while an existing authentication attempt is occurring will stop
+ * the previous client and start a new authentication. The interrupted client will receive a
+ * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ void authenticate(@NonNull CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback);
+
+ /**
+ * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+ * when {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called. This operation can be canceled by using the provided cancel
+ * object. The application wil receive authentication errors through
+ * {@link AuthenticationCallback}. Calling {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)} while an existing
+ * authentication attempt is occurring will stop the previous client and start a new
+ * authentication. The interrupted client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback);
+}
diff --git a/android/hardware/biometrics/BiometricConstants.java b/android/hardware/biometrics/BiometricConstants.java
new file mode 100644
index 00000000..a037289a
--- /dev/null
+++ b/android/hardware/biometrics/BiometricConstants.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+
+/**
+ * Interface containing all of the biometric modality agnostic constants.
+ * @hide
+ */
+public interface BiometricConstants {
+ //
+ // Error messages from biometric hardware during initilization, enrollment, authentication or
+ // removal.
+ //
+
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1;
+
+ /**
+ * Error state returned when the sensor was unable to process the current image.
+ */
+ int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2;
+
+ /**
+ * Error state returned when the current request has been running too long. This is intended to
+ * prevent programs from waiting for the biometric sensor indefinitely. The timeout is platform
+ * and sensor-specific, but is generally on the order of 30 seconds.
+ */
+ int BIOMETRIC_ERROR_TIMEOUT = 3;
+
+ /**
+ * Error state returned for operations like enrollment; the operation cannot be completed
+ * because there's not enough storage remaining to complete the operation.
+ */
+ int BIOMETRIC_ERROR_NO_SPACE = 4;
+
+ /**
+ * The operation was canceled because the biometric sensor is unavailable. For example, this may
+ * happen when the user is switched, the device is locked or another pending operation prevents
+ * or disables it.
+ */
+ int BIOMETRIC_ERROR_CANCELED = 5;
+
+ /**
+ * The {@link BiometricManager#remove} call failed. Typically this will happen when the provided
+ * biometric id was incorrect.
+ *
+ * @hide
+ */
+ int BIOMETRIC_ERROR_UNABLE_TO_REMOVE = 6;
+
+ /**
+ * The operation was canceled because the API is locked out due to too many attempts.
+ * This occurs after 5 failed attempts, and lasts for 30 seconds.
+ */
+ int BIOMETRIC_ERROR_LOCKOUT = 7;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * These messages are typically reserved for internal operations such as enrollment, but may be
+ * used to express vendor errors not otherwise covered. Applications are expected to show the
+ * error message string if they happen, but are advised not to rely on the message id since they
+ * will be device and vendor-specific
+ */
+ int BIOMETRIC_ERROR_VENDOR = 8;
+
+ /**
+ * The operation was canceled because BIOMETRIC_ERROR_LOCKOUT occurred too many times.
+ * Biometric authentication is disabled until the user unlocks with strong authentication
+ * (PIN/Pattern/Password)
+ */
+ int BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9;
+
+ /**
+ * The user canceled the operation. Upon receiving this, applications should use alternate
+ * authentication (e.g. a password). The application should also provide the means to return to
+ * biometric authentication, such as a "use <biometric>" button.
+ */
+ int BIOMETRIC_ERROR_USER_CANCELED = 10;
+
+ /**
+ * The user does not have any biometrics enrolled.
+ */
+ int BIOMETRIC_ERROR_NO_BIOMETRICS = 11;
+
+ /**
+ * The device does not have a biometric sensor.
+ */
+ int BIOMETRIC_ERROR_HW_NOT_PRESENT = 12;
+
+ /**
+ * @hide
+ */
+ int BIOMETRIC_ERROR_VENDOR_BASE = 1000;
+
+ //
+ // Image acquisition messages.
+ //
+
+ /**
+ * The image acquired was good.
+ */
+ int BIOMETRIC_ACQUIRED_GOOD = 0;
+
+ /**
+ * Only a partial biometric image was detected. During enrollment, the user should be informed
+ * on what needs to happen to resolve this problem, e.g. "press firmly on sensor." (for
+ * fingerprint)
+ */
+ int BIOMETRIC_ACQUIRED_PARTIAL = 1;
+
+ /**
+ * The biometric image was too noisy to process due to a detected condition or a possibly dirty
+ * sensor (See {@link #BIOMETRIC_ACQUIRED_IMAGER_DIRTY}).
+ */
+ int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2;
+
+ /**
+ * The biometric image was too noisy due to suspected or detected dirt on the sensor. For
+ * example, it's reasonable return this after multiple {@link #BIOMETRIC_ACQUIRED_INSUFFICIENT}
+ * or actual detection of dirt on the sensor (stuck pixels, swaths, etc.). The user is expected
+ * to take action to clean the sensor when this is returned.
+ */
+ int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3;
+
+ /**
+ * The biometric image was unreadable due to lack of motion.
+ */
+ int BIOMETRIC_ACQUIRED_TOO_SLOW = 4;
+
+ /**
+ * The biometric image was incomplete due to quick motion. For example, this could also happen
+ * if the user moved during acquisition. The user should be asked to repeat the operation more
+ * slowly.
+ */
+ int BIOMETRIC_ACQUIRED_TOO_FAST = 5;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * @hide
+ */
+ int BIOMETRIC_ACQUIRED_VENDOR = 6;
+ /**
+ * @hide
+ */
+ int BIOMETRICT_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/android/hardware/biometrics/BiometricDialog.java b/android/hardware/biometrics/BiometricDialog.java
new file mode 100644
index 00000000..dd848a34
--- /dev/null
+++ b/android/hardware/biometrics/BiometricDialog.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.text.TextUtils;
+
+import java.security.Signature;
+import java.util.concurrent.Executor;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A class that manages a system-provided biometric dialog.
+ */
+public class BiometricDialog implements BiometricAuthenticator, BiometricConstants {
+
+ /**
+ * @hide
+ */
+ public static final String KEY_TITLE = "title";
+ /**
+ * @hide
+ */
+ public static final String KEY_SUBTITLE = "subtitle";
+ /**
+ * @hide
+ */
+ public static final String KEY_DESCRIPTION = "description";
+ /**
+ * @hide
+ */
+ public static final String KEY_POSITIVE_TEXT = "positive_text";
+ /**
+ * @hide
+ */
+ public static final String KEY_NEGATIVE_TEXT = "negative_text";
+
+ /**
+ * Error/help message will show for this amount of time.
+ * For error messages, the dialog will also be dismissed after this amount of time.
+ * Error messages will be propagated back to the application via AuthenticationCallback
+ * after this amount of time.
+ * @hide
+ */
+ public static final int HIDE_DIALOG_DELAY = 2000; // ms
+ /**
+ * @hide
+ */
+ public static final int DISMISSED_REASON_POSITIVE = 1;
+
+ /**
+ * @hide
+ */
+ public static final int DISMISSED_REASON_NEGATIVE = 2;
+
+ /**
+ * @hide
+ */
+ public static final int DISMISSED_REASON_USER_CANCEL = 3;
+
+ private static class ButtonInfo {
+ Executor executor;
+ DialogInterface.OnClickListener listener;
+ ButtonInfo(Executor ex, DialogInterface.OnClickListener l) {
+ executor = ex;
+ listener = l;
+ }
+ }
+
+ /**
+ * A builder that collects arguments to be shown on the system-provided biometric dialog.
+ **/
+ public static class Builder {
+ private final Bundle mBundle;
+ private ButtonInfo mPositiveButtonInfo;
+ private ButtonInfo mNegativeButtonInfo;
+ private Context mContext;
+
+ /**
+ * Creates a builder for a biometric dialog.
+ * @param context
+ */
+ public Builder(Context context) {
+ mBundle = new Bundle();
+ mContext = context;
+ }
+
+ /**
+ * Required: Set the title to display.
+ * @param title
+ * @return
+ */
+ public Builder setTitle(@NonNull CharSequence title) {
+ mBundle.putCharSequence(KEY_TITLE, title);
+ return this;
+ }
+
+ /**
+ * Optional: Set the subtitle to display.
+ * @param subtitle
+ * @return
+ */
+ public Builder setSubtitle(@NonNull CharSequence subtitle) {
+ mBundle.putCharSequence(KEY_SUBTITLE, subtitle);
+ return this;
+ }
+
+ /**
+ * Optional: Set the description to display.
+ * @param description
+ * @return
+ */
+ public Builder setDescription(@NonNull CharSequence description) {
+ mBundle.putCharSequence(KEY_DESCRIPTION, description);
+ return this;
+ }
+
+ /**
+ * Optional: Set the text for the positive button. If not set, the positive button
+ * will not show.
+ * @param text
+ * @return
+ * @hide
+ */
+ public Builder setPositiveButton(@NonNull CharSequence text,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull DialogInterface.OnClickListener listener) {
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Text must be set and non-empty");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Executor must not be null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null");
+ }
+ mBundle.putCharSequence(KEY_POSITIVE_TEXT, text);
+ mPositiveButtonInfo = new ButtonInfo(executor, listener);
+ return this;
+ }
+
+ /**
+ * Required: Set the text for the negative button. This would typically be used as a
+ * "Cancel" button, but may be also used to show an alternative method for authentication,
+ * such as screen that asks for a backup password.
+ * @param text
+ * @return
+ */
+ public Builder setNegativeButton(@NonNull CharSequence text,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull DialogInterface.OnClickListener listener) {
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Text must be set and non-empty");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Executor must not be null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null");
+ }
+ mBundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
+ mNegativeButtonInfo = new ButtonInfo(executor, listener);
+ return this;
+ }
+
+ /**
+ * Creates a {@link BiometricDialog}.
+ * @return a {@link BiometricDialog}
+ * @throws IllegalArgumentException if any of the required fields are not set.
+ */
+ public BiometricDialog build() {
+ final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
+ final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
+
+ if (TextUtils.isEmpty(title)) {
+ throw new IllegalArgumentException("Title must be set and non-empty");
+ } else if (TextUtils.isEmpty(negative)) {
+ throw new IllegalArgumentException("Negative text must be set and non-empty");
+ }
+ return new BiometricDialog(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
+ }
+ }
+
+ private PackageManager mPackageManager;
+ private FingerprintManager mFingerprintManager;
+ private Bundle mBundle;
+ private ButtonInfo mPositiveButtonInfo;
+ private ButtonInfo mNegativeButtonInfo;
+
+ IBiometricDialogReceiver mDialogReceiver = new IBiometricDialogReceiver.Stub() {
+ @Override
+ public void onDialogDismissed(int reason) {
+ // Check the reason and invoke OnClickListener(s) if necessary
+ if (reason == DISMISSED_REASON_POSITIVE) {
+ mPositiveButtonInfo.executor.execute(() -> {
+ mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
+ });
+ } else if (reason == DISMISSED_REASON_NEGATIVE) {
+ mNegativeButtonInfo.executor.execute(() -> {
+ mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
+ });
+ }
+ }
+ };
+
+ private BiometricDialog(Context context, Bundle bundle,
+ ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
+ mBundle = bundle;
+ mPositiveButtonInfo = positiveButtonInfo;
+ mNegativeButtonInfo = negativeButtonInfo;
+ mFingerprintManager = context.getSystemService(FingerprintManager.class);
+ mPackageManager = context.getPackageManager();
+ }
+
+ /**
+ * A wrapper class for the crypto objects supported by BiometricDialog. Currently the framework
+ * supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ */
+ public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
+ public CryptoObject(@NonNull Signature signature) {
+ super(signature);
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ super(cipher);
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ super(mac);
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return super.getSignature();
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return super.getCipher();
+ }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return super.getMac();
+ }
+ }
+
+ /**
+ * Container for callback data from {@link #authenticate( CancellationSignal, Executor,
+ * AuthenticationCallback)} and {@link #authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ */
+ public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
+ /**
+ * Authentication result
+ * @param crypto
+ * @param identifier
+ * @param userId
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ int userId) {
+ super(crypto, identifier, userId);
+ }
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link #authenticate( CryptoObject, CancellationSignal,
+ * Executor, AuthenticationCallback)}
+ */
+ public CryptoObject getCryptoObject() {
+ return (CryptoObject) super.getCryptoObject();
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link BiometricDialog#authenticate(CancellationSignal,
+ * Executor, AuthenticationCallback)} or {@link BiometricDialog#authenticate(CryptoObject,
+ * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation
+ * of this for listening to authentication events.
+ */
+ public abstract static class AuthenticationCallback extends
+ BiometricAuthenticator.AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further actions will be made on this object.
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown on an UI
+ */
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+ * please clean it."
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown on an UI
+ */
+ @Override
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+ /**
+ * Called when a biometric is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+ /**
+ * Called when a biometric is valid but not recognized.
+ */
+ @Override
+ public void onAuthenticationFailed() {}
+
+ /**
+ * Called when a biometric has been acquired, but hasn't been processed yet.
+ * @hide
+ */
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {}
+
+ /**
+ * @param result An object containing authentication-related data
+ * @hide
+ */
+ @Override
+ public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+ onAuthenticationSucceeded(new AuthenticationResult(
+ (CryptoObject) result.getCryptoObject(),
+ result.getId(),
+ result.getUserId()));
+ }
+ }
+
+ /**
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @hide
+ */
+ @Override
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
+ throw new IllegalArgumentException("Callback cannot be casted");
+ }
+ authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
+ }
+
+ /**
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @hide
+ */
+ @Override
+ public void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
+ throw new IllegalArgumentException("Callback cannot be casted");
+ }
+ authenticate(cancel, executor, (AuthenticationCallback) callback);
+ }
+
+ /**
+ * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts
+ * scanning for a fingerprint. It terminates when {@link
+ * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link
+ * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)}, or when the user
+ * dismisses the system-provided dialog, at which point the crypto object becomes invalid. This
+ * operation can be canceled by using the provided cancel object. The application will receive
+ * authentication errors through {@link AuthenticationCallback}, and button events through the
+ * corresponding callback set in {@link Builder#setNegativeButton(CharSequence, Executor,
+ * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricDialog} object,
+ * and calling {@link BiometricDialog#authenticate( CancellationSignal, Executor,
+ * AuthenticationCallback)} while an existing authentication attempt is occurring will stop the
+ * previous client and start a new authentication. The interrupted client will receive a
+ * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public void authenticate(@NonNull CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback) {
+ if (handlePreAuthenticationErrors(callback, executor)) {
+ return;
+ }
+ mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
+ callback);
+ }
+
+ /**
+ * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts
+ * scanning for a fingerprint. It terminates when {@link
+ * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link
+ * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)} is called, or when
+ * the user dismisses the system-provided dialog. This operation can be canceled by using the
+ * provided cancel object. The application will receive authentication errors through {@link
+ * AuthenticationCallback}, and button events through the corresponding callback set in {@link
+ * Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. It is
+ * safe to reuse the {@link BiometricDialog} object, and calling {@link
+ * BiometricDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while
+ * an existing authentication attempt is occurring will stop the previous client and start a new
+ * authentication. The interrupted client will receive a cancelled notification through {@link
+ * AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback) {
+ if (handlePreAuthenticationErrors(callback, executor)) {
+ return;
+ }
+ mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
+ }
+
+ private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
+ Executor executor) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ sendError(BiometricDialog.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
+ executor);
+ return true;
+ } else if (!mFingerprintManager.isHardwareDetected()) {
+ sendError(BiometricDialog.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
+ executor);
+ return true;
+ } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
+ sendError(BiometricDialog.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
+ executor);
+ return true;
+ }
+ return false;
+ }
+
+ private void sendError(int error, AuthenticationCallback callback, Executor executor) {
+ executor.execute(() -> {
+ callback.onAuthenticationError(error, mFingerprintManager.getErrorString(
+ error, 0 /* vendorCode */));
+ });
+ }
+}
diff --git a/android/hardware/biometrics/BiometricFingerprintConstants.java b/android/hardware/biometrics/BiometricFingerprintConstants.java
new file mode 100644
index 00000000..638f525b
--- /dev/null
+++ b/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.hardware.fingerprint.FingerprintManager;
+
+/**
+ * Interface containing all of the fingerprint-specific constants.
+ * @hide
+ */
+public interface BiometricFingerprintConstants {
+ //
+ // Error messages from fingerprint hardware during initilization, enrollment, authentication or
+ // removal. Must agree with the list in fingerprint.h
+ //
+
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+
+ /**
+ * Error state returned when the sensor was unable to process the current image.
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+
+ /**
+ * Error state returned when the current request has been running too long. This is intended to
+ * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
+ * platform and sensor-specific, but is generally on the order of 30 seconds.
+ */
+ public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+
+ /**
+ * Error state returned for operations like enrollment; the operation cannot be completed
+ * because there's not enough storage remaining to complete the operation.
+ */
+ public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+ /**
+ * The operation was canceled because the fingerprint sensor is unavailable. For example,
+ * this may happen when the user is switched, the device is locked or another pending operation
+ * prevents or disables it.
+ */
+ public static final int FINGERPRINT_ERROR_CANCELED = 5;
+
+ /**
+ * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
+ * provided fingerprint id was incorrect.
+ *
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+
+ /**
+ * The operation was canceled because the API is locked out due to too many attempts.
+ * This occurs after 5 failed attempts, and lasts for 30 seconds.
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * These messages are typically reserved for internal operations such as enrollment, but may be
+ * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
+ * expected to show the error message string if they happen, but are advised not to rely on the
+ * message id since they will be device and vendor-specific
+ */
+ public static final int FINGERPRINT_ERROR_VENDOR = 8;
+
+ /**
+ * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
+ * Fingerprint authentication is disabled until the user unlocks with strong authentication
+ * (PIN/Pattern/Password)
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
+
+ /**
+ * The user canceled the operation. Upon receiving this, applications should use alternate
+ * authentication (e.g. a password). The application should also provide the means to return
+ * to fingerprint authentication, such as a "use fingerprint" button.
+ */
+ public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
+
+ /**
+ * The user does not have any fingerprints enrolled.
+ */
+ public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
+
+ /**
+ * The device does not have a fingerprint sensor.
+ */
+ public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
+
+ /**
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
+
+ //
+ // Image acquisition messages. Must agree with those in fingerprint.h
+ //
+
+ /**
+ * The image acquired was good.
+ */
+ public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+
+ /**
+ * Only a partial fingerprint image was detected. During enrollment, the user should be
+ * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
+ */
+ public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+
+ /**
+ * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
+ * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
+ */
+ public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+
+ /**
+ * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
+ * For example, it's reasonable return this after multiple
+ * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
+ * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
+ * when this is returned.
+ */
+ public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+
+ /**
+ * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
+ * linear array sensors that require a swipe motion.
+ */
+ public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+
+ /**
+ * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
+ * linear array sensors, this could also happen if the finger was moved during acquisition.
+ * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
+ * longer.
+ */
+ public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * @hide
+ */
+ public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
+ /**
+ * @hide
+ */
+ public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/android/hardware/biometrics/CryptoObject.java b/android/hardware/biometrics/CryptoObject.java
new file mode 100644
index 00000000..496d9c57
--- /dev/null
+++ b/android/hardware/biometrics/CryptoObject.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.security.keystore.AndroidKeyStoreProvider;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * @hide
+ */
+public class CryptoObject {
+ private final Object mCrypto;
+
+ public CryptoObject(@NonNull Signature signature) {
+ mCrypto = signature;
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ mCrypto = cipher;
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ mCrypto = mac;
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ }
+
+ /**
+ * @hide
+ * @return the opId associated with this object or 0 if none
+ */
+ public final long getOpId() {
+ return mCrypto != null
+ ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+ }
+};
diff --git a/android/hardware/camera2/CameraCaptureSession.java b/android/hardware/camera2/CameraCaptureSession.java
index ff69bd89..eafe5938 100644
--- a/android/hardware/camera2/CameraCaptureSession.java
+++ b/android/hardware/camera2/CameraCaptureSession.java
@@ -16,15 +16,16 @@
package android.hardware.camera2;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.camera2.params.OutputConfiguration;
import android.os.Handler;
import android.view.Surface;
+import java.util.concurrent.Executor;
import java.util.List;
-
/**
* A configured capture session for a {@link CameraDevice}, used for capturing images from the
* camera or reprocessing images captured from the camera in the same session previously.
@@ -354,6 +355,50 @@ public abstract class CameraCaptureSession implements AutoCloseable {
throws CameraAccessException;
/**
+ * <p>Submit a request for an image to be captured by the camera device.</p>
+ *
+ * <p>The behavior of this method matches that of
+ * {@link #capture(CaptureRequest, CaptureCallback, Handler)},
+ * except that it uses {@link java.util.concurrent.Executor} as an argument
+ * instead of {@link android.os.Handler}.</p>
+ *
+ * @param request the settings for this capture
+ * @param executor the executor which will be used for invoking the listener.
+ * @param listener The callback object to notify once this request has been
+ * processed.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureCallback#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
+ * configured as outputs for this session; or the request
+ * targets a set of Surfaces that cannot be submitted
+ * simultaneously in a reprocessable capture session; or a
+ * reprocess capture request is submitted in a
+ * non-reprocessable capture session; or the reprocess capture
+ * request was created with a {@link TotalCaptureResult} from
+ * a different session; or the capture targets a Surface in
+ * the middle of being {@link #prepare prepared}; or the
+ * executor is null, or the listener is not null.
+ *
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ * @see #abortCaptures
+ * @see CameraDevice#createReprocessableCaptureSession
+ */
+ public int captureSingleRequest(@NonNull CaptureRequest request,
+ @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* Submit a list of requests to be captured in sequence as a burst. The
* burst will be captured in the minimum amount of time possible, and will
* not be interleaved with requests submitted by other capture or repeat
@@ -416,6 +461,53 @@ public abstract class CameraCaptureSession implements AutoCloseable {
throws CameraAccessException;
/**
+ * Submit a list of requests to be captured in sequence as a burst. The
+ * burst will be captured in the minimum amount of time possible, and will
+ * not be interleaved with requests submitted by other capture or repeat
+ * calls.
+ *
+ * <p>The behavior of this method matches that of
+ * {@link #captureBurst(List, CaptureCallback, Handler)},
+ * except that it uses {@link java.util.concurrent.Executor} as an argument
+ * instead of {@link android.os.Handler}.</p>
+ *
+ * @param requests the list of settings for this burst capture
+ * @param executor the executor which will be used for invoking the listener.
+ * @param listener The callback object to notify each time one of the
+ * requests in the burst has been processed.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureCallback#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target
+ * Surfaces not currently configured as outputs; or one of the
+ * requests targets a set of Surfaces that cannot be submitted
+ * simultaneously in a reprocessable capture session; or a
+ * reprocess capture request is submitted in a
+ * non-reprocessable capture session; or one of the reprocess
+ * capture requests was created with a
+ * {@link TotalCaptureResult} from a different session; or one
+ * of the captures targets a Surface in the middle of being
+ * {@link #prepare prepared}; or if the executor is null; or if
+ * the listener is null.
+ *
+ * @see #capture
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ * @see #abortCaptures
+ */
+ public int captureBurstRequests(@NonNull List<CaptureRequest> requests,
+ @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* Request endlessly repeating capture of images by this capture session.
*
* <p>With this method, the camera device will continually capture images
@@ -483,6 +575,45 @@ public abstract class CameraCaptureSession implements AutoCloseable {
throws CameraAccessException;
/**
+ * Request endlessly repeating capture of images by this capture session.
+ *
+ * <p>The behavior of this method matches that of
+ * {@link #setRepeatingRequest(CaptureRequest, CaptureCallback, Handler)},
+ * except that it uses {@link java.util.concurrent.Executor} as an argument
+ * instead of {@link android.os.Handler}.</p>
+ *
+ * @param request the request to repeat indefinitely
+ * @param executor the executor which will be used for invoking the listener.
+ * @param listener The callback object to notify every time the
+ * request finishes processing.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureCallback#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ * @throws IllegalArgumentException If the request references no Surfaces or references Surfaces
+ * that are not currently configured as outputs; or the request
+ * is a reprocess capture request; or the capture targets a
+ * Surface in the middle of being {@link #prepare prepared}; or
+ * the executor is null; or the listener is null.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingBurst
+ * @see #stopRepeating
+ * @see #abortCaptures
+ */
+ public int setSingleRepeatingRequest(@NonNull CaptureRequest request,
+ @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* <p>Request endlessly repeating capture of a sequence of images by this
* capture session.</p>
*
@@ -555,6 +686,47 @@ public abstract class CameraCaptureSession implements AutoCloseable {
throws CameraAccessException;
/**
+ * <p>Request endlessly repeating capture of a sequence of images by this
+ * capture session.</p>
+ *
+ * <p>The behavior of this method matches that of
+ * {@link #setRepeatingBurst(List, CaptureCallback, Handler)},
+ * except that it uses {@link java.util.concurrent.Executor} as an argument
+ * instead of {@link android.os.Handler}.</p>
+ *
+ * @param requests the list of requests to cycle through indefinitely
+ * @param executor the executor which will be used for invoking the listener.
+ * @param listener The callback object to notify each time one of the
+ * requests in the repeating bursts has finished processing.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureCallback#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ * @throws IllegalArgumentException If the requests reference no Surfaces or reference Surfaces
+ * not currently configured as outputs; or one of the requests
+ * is a reprocess capture request; or one of the captures
+ * targets a Surface in the middle of being
+ * {@link #prepare prepared}; or the executor is null; or the
+ * listener is null.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #stopRepeating
+ * @see #abortCaptures
+ */
+ public int setRepeatingBurstRequests(@NonNull List<CaptureRequest> requests,
+ @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* <p>Cancel any ongoing repeating capture set by either
* {@link #setRepeatingRequest setRepeatingRequest} or
* {@link #setRepeatingBurst}. Has no effect on requests submitted through
diff --git a/android/hardware/camera2/CameraCharacteristics.java b/android/hardware/camera2/CameraCharacteristics.java
index 96d043c2..4279b197 100644
--- a/android/hardware/camera2/CameraCharacteristics.java
+++ b/android/hardware/camera2/CameraCharacteristics.java
@@ -28,7 +28,9 @@ import android.util.Rational;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* <p>The properties describing a
@@ -341,7 +343,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*/
@SuppressWarnings({"unchecked"})
public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() {
- if (mAvailableSessionKeys == null) {
+ if (mAvailablePhysicalRequestKeys == null) {
Object crKey = CaptureRequest.Key.class;
Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
@@ -450,23 +452,20 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
}
/**
- * Returns the list of physical camera ids that this logical {@link CameraDevice} is
+ * Returns the set of physical camera ids that this logical {@link CameraDevice} is
* made up of.
*
* <p>A camera device is a logical camera if it has
* REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability. If the camera device
- * doesn't have the capability, the return value will be an empty list. </p>
+ * doesn't have the capability, the return value will be an empty set. </p>
*
- * <p>The list returned is not modifiable, so any attempts to modify it will throw
+ * <p>The set returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
- * <p>Each physical camera id is only listed once in the list. The order of the keys
- * is undefined.</p>
- *
- * @return List of physical camera ids for this logical camera device.
+ * @return Set of physical camera ids for this logical camera device.
*/
@NonNull
- public List<String> getPhysicalCameraIds() {
+ public Set<String> getPhysicalCameraIds() {
int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
if (availableCapabilities == null) {
throw new AssertionError("android.request.availableCapabilities must be non-null "
@@ -475,7 +474,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
if (!ArrayUtils.contains(availableCapabilities,
REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
- return Collections.emptyList();
+ return Collections.emptySet();
}
byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
@@ -485,9 +484,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
} catch (java.io.UnsupportedEncodingException e) {
throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
}
- String[] physicalCameraIdList = physicalCamIdString.split("\0");
+ String[] physicalCameraIdArray = physicalCamIdString.split("\0");
- return Collections.unmodifiableList(Arrays.asList(physicalCameraIdList));
+ return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
}
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -1243,7 +1242,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* from the main sensor along the +X axis (to the right from the user's perspective) will
* report <code>(0.03, 0, 0)</code>.</p>
* <p>To transform a pixel coordinates between two cameras facing the same direction, first
- * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for. Then the source
+ * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for. Then the source
* camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
* {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
* relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
@@ -1257,10 +1256,10 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
+ * @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
* @see CameraCharacteristics#LENS_POSE_ROTATION
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
*/
@PublicKey
public static final Key<float[]> LENS_POSE_TRANSLATION =
@@ -1306,7 +1305,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* where <code>(0,0)</code> is the top-left of the
* preCorrectionActiveArraySize rectangle. Once the pose and
* intrinsic calibration transforms have been applied to a
- * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}
+ * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}
* transform needs to be applied, and the result adjusted to
* be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
* system (where <code>(0, 0)</code> is the top-left of the
@@ -1319,9 +1318,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* coordinate system.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
+ * @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_POSE_ROTATION
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@@ -1363,7 +1362,14 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+ * @deprecated
+ * <p>This field was inconsistently defined in terms of its
+ * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p>
+ *
+ * @see CameraCharacteristics#LENS_DISTORTION
+
*/
+ @Deprecated
@PublicKey
public static final Key<float[]> LENS_RADIAL_DISTORTION =
new Key<float[]>("android.lens.radialDistortion", float[].class);
@@ -1372,9 +1378,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
* <p>Different calibration methods and use cases can produce better or worse results
* depending on the selected coordinate origin.</p>
- * <p>For devices designed to support the MOTION_TRACKING capability, the GYROSCOPE origin
- * makes device calibration and later usage by applications combining camera and gyroscope
- * information together simpler.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #LENS_POSE_REFERENCE_PRIMARY_CAMERA PRIMARY_CAMERA}</li>
@@ -1391,6 +1394,46 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<Integer>("android.lens.poseReference", int.class);
/**
+ * <p>The correction coefficients to correct for this camera device's
+ * radial and tangential lens distortion.</p>
+ * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was
+ * inconsistently defined.</p>
+ * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+ * kappa_3]</code> and two tangential distortion coefficients
+ * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+ * lens's geometric distortion with the mapping equations:</p>
+ * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+ * kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+ * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+ * kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+ * </code></pre>
+ * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+ * input image that correspond to the pixel values in the
+ * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+ * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+ * </code></pre>
+ * <p>The pixel coordinates are defined in a coordinate system
+ * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
+ * calibration fields; see that entry for details of the mapping stages.
+ * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+ * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+ * the range of the coordinates depends on the focal length
+ * terms of the intrinsic calibration.</p>
+ * <p>Finally, <code>r</code> represents the radial distance from the
+ * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+ * <p>The distortion model used is the Brown-Conrady model.</p>
+ * <p><b>Units</b>:
+ * Unitless coefficients.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+ * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_DISTORTION =
+ new Key<float[]>("android.lens.distortion", float[].class);
+
+ /**
* <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
* by this camera device.</p>
* <p>Full-capability camera devices will always support OFF and FAST.</p>
@@ -1422,6 +1465,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* consideration of future support.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
+ * <p>Not used in HALv3 or newer; replaced by better partials mechanism</p>
+
* @hide
*/
@Deprecated
@@ -1663,6 +1708,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -1679,6 +1725,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
* @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
* @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
*/
@PublicKey
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1793,11 +1840,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* The respective value of such request key can be obtained by calling
* {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
* individual physical device requests must be built via
- * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
- * Such extended capture requests can be passed only to
- * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
- * not to {@link CameraCaptureSession#setRepeatingRequest } or
- * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+ * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.</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
@@ -1816,6 +1859,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>When set to YUV_420_888, application can access the YUV420 data directly.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
+ * <p>Not used in HALv3 or newer</p>
+
* @hide
*/
@Deprecated
@@ -1836,6 +1881,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* TODO: Remove property.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
+ * <p>Not used in HALv3 or newer</p>
+
* @hide
*/
@Deprecated
@@ -1852,6 +1899,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @deprecated
+ * <p>Not used in HALv3 or newer</p>
+
* @hide
*/
@Deprecated
@@ -1891,6 +1940,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
+ * <p>Not used in HALv3 or newer</p>
+
* @hide
*/
@Deprecated
@@ -1913,6 +1964,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* check if it limits the maximum size for image data.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
+ * <p>Not used in HALv3 or newer</p>
+
* @hide
*/
@Deprecated
@@ -2552,7 +2605,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
* <p>The currently supported fields that correct for geometric distortion are:</p>
* <ol>
- * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}.</li>
+ * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}.</li>
* </ol>
* <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
* as the post-distortion-corrected rectangle given in
@@ -2565,7 +2618,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p><b>Units</b>: Pixel coordinates on the image sensor</p>
* <p>This key is available on all devices.</p>
*
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+ * @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
@@ -3341,6 +3394,21 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
public static final Key<Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =
new Key<Integer>("android.logicalMultiCamera.sensorSyncType", int.class);
+ /**
+ * <p>List of distortion correction modes for {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} that are
+ * supported by this camera device.</p>
+ * <p>No device is required to support this API; such devices will always list only 'OFF'.
+ * All devices that support this API will list both FAST and HIGH_QUALITY.</p>
+ * <p><b>Range of valid values:</b><br>
+ * Any value listed in {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode}</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+ */
+ @PublicKey
+ public static final Key<int[]> DISTORTION_CORRECTION_AVAILABLE_MODES =
+ new Key<int[]>("android.distortionCorrection.availableModes", int[].class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/android/hardware/camera2/CameraDevice.java b/android/hardware/camera2/CameraDevice.java
index 40ee8348..f47d4640 100644
--- a/android/hardware/camera2/CameraDevice.java
+++ b/android/hardware/camera2/CameraDevice.java
@@ -145,37 +145,6 @@ public abstract class CameraDevice implements AutoCloseable {
*/
public static final int TEMPLATE_MANUAL = 6;
- /**
- * A template for selecting camera parameters that match TEMPLATE_PREVIEW as closely as
- * possible while improving the camera output for motion tracking use cases.
- *
- * <p>This template is best used by applications that are frequently switching between motion
- * tracking use cases and regular still capture use cases, to minimize the IQ changes
- * when swapping use cases.</p>
- *
- * <p>This template is guaranteed to be supported on camera devices that support the
- * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}
- * capability.</p>
- *
- * @see #createCaptureRequest
- */
- public static final int TEMPLATE_MOTION_TRACKING_PREVIEW = 7;
-
- /**
- * A template for selecting camera parameters that maximize the quality of camera output for
- * motion tracking use cases.
- *
- * <p>This template is best used by applications dedicated to motion tracking applications,
- * which aren't concerned about fast switches between motion tracking and other use cases.</p>
- *
- * <p>This template is guaranteed to be supported on camera devices that support the
- * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}
- * capability.</p>
- *
- * @see #createCaptureRequest
- */
- public static final int TEMPLATE_MOTION_TRACKING_BEST = 8;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"TEMPLATE_"}, value =
@@ -184,9 +153,7 @@ public abstract class CameraDevice implements AutoCloseable {
TEMPLATE_RECORD,
TEMPLATE_VIDEO_SNAPSHOT,
TEMPLATE_ZERO_SHUTTER_LAG,
- TEMPLATE_MANUAL,
- TEMPLATE_MOTION_TRACKING_PREVIEW,
- TEMPLATE_MOTION_TRACKING_BEST})
+ TEMPLATE_MANUAL})
public @interface RequestTemplate {};
/**
@@ -420,27 +387,6 @@ public abstract class CameraDevice implements AutoCloseable {
* </table><br>
* </p>
*
- * <p>MOTION_TRACKING-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
- * includes
- * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING})
- * devices support at least the below stream combinations in addition to those for
- * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices. The
- * {@code FULL FOV 640} entry means that the device will support a resolution that's 640 pixels
- * wide, with the height set so that the resolution aspect ratio matches the MAXIMUM output
- * aspect ratio, rounded down. So for a device with a 4:3 image sensor, this will be 640x480,
- * and for a device with a 16:9 sensor, this will be 640x360, and so on. And the
- * {@code MAX 30FPS} entry means the largest JPEG resolution on the device for which
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration}
- * returns a value less than or equal to 1/30s.
- *
- * <table>
- * <tr><th colspan="7">MOTION_TRACKING-capability additional guaranteed configurations</th></tr>
- * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th rowspan="2">Sample use case(s)</th> </tr>
- * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
- * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code FULL FOV 640}</td> <td>{@code JPEG}</td><td id="rb">{@code MAX 30FPS}</td> <td>Preview with a tracking YUV output and a as-large-as-possible JPEG for still captures.</td> </tr>
- * </table><br>
- * </p>
- *
* <p>BURST-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes
* {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}) devices
* support at least the below stream combinations in addition to those for
@@ -873,7 +819,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @param config A session configuration (see {@link SessionConfiguration}).
*
* @throws IllegalArgumentException In case the session configuration is invalid; or the output
- * configurations are empty.
+ * configurations are empty; or the session configuration
+ * executor is invalid.
* @throws CameraAccessException In case the camera device is no longer connected or has
* encountered a fatal error.
* @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
@@ -917,11 +864,16 @@ public abstract class CameraDevice implements AutoCloseable {
* request for a specific physical camera. The settings are chosen
* to be the best options for the specific logical camera device. If
* additional physical camera ids are passed, then they will also use the
- * same settings template. Requests containing individual physical camera
- * settings can be passed only to {@link CameraCaptureSession#capture} or
- * {@link CameraCaptureSession#captureBurst} and not to
- * {@link CameraCaptureSession#setRepeatingRequest} or
- * {@link CameraCaptureSession#setRepeatingBurst}</p>
+ * same settings template. Clients can further modify individual camera
+ * settings by calling {@link CaptureRequest.Builder#setPhysicalCameraKey}.</p>
+ *
+ * <p>Individual physical camera settings will only be honored for camera session
+ * that was initialiazed with corresponding physical camera id output configuration
+ * {@link OutputConfiguration#setPhysicalCameraId} and the same output targets are
+ * also attached in the request by {@link CaptureRequest.Builder#addTarget}.</p>
+ *
+ * <p>The output is undefined for any logical camera streams in case valid physical camera
+ * settings are attached.</p>
*
* @param templateType An enumeration selecting the use case for this request. Not all template
* types are supported on every device. See the documentation for each template type for
@@ -942,8 +894,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @see #TEMPLATE_STILL_CAPTURE
* @see #TEMPLATE_VIDEO_SNAPSHOT
* @see #TEMPLATE_MANUAL
- * @see CaptureRequest.Builder#setKey
- * @see CaptureRequest.Builder#getKey
+ * @see CaptureRequest.Builder#setPhysicalCameraKey
+ * @see CaptureRequest.Builder#getPhysicalCameraKey
*/
@NonNull
public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,
diff --git a/android/hardware/camera2/CameraManager.java b/android/hardware/camera2/CameraManager.java
index a2bc91e0..4124536d 100644
--- a/android/hardware/camera2/CameraManager.java
+++ b/android/hardware/camera2/CameraManager.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -25,6 +26,7 @@ import android.hardware.CameraInfo;
import android.hardware.CameraStatus;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
+import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
import android.hardware.camera2.legacy.LegacyMetadataMapper;
@@ -41,6 +43,11 @@ import android.util.ArrayMap;
import android.util.Log;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
/**
* <p>A system service manager for detecting, characterizing, and connecting to
@@ -123,16 +130,29 @@ public final class CameraManager {
*/
public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
@Nullable Handler handler) {
- if (handler == null) {
- Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException(
- "No handler given, and current thread has no looper!");
- }
- handler = new Handler(looper);
- }
+ CameraManagerGlobal.get().registerAvailabilityCallback(callback,
+ CameraDeviceImpl.checkAndWrapHandler(handler));
+ }
- CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
+ /**
+ * Register a callback to be notified about camera device availability.
+ *
+ * <p>The behavior of this method matches that of
+ * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)},
+ * except that it uses {@link java.util.concurrent.Executor} as an argument
+ * instead of {@link android.os.Handler}.</p>
+ *
+ * @param executor The executor which will be used to invoke the callback.
+ * @param callback the new callback to send camera availability notices to
+ *
+ * @throws IllegalArgumentException if the executor is {@code null}.
+ */
+ public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AvailabilityCallback callback) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor was null");
+ }
+ CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor);
}
/**
@@ -170,15 +190,29 @@ public final class CameraManager {
* no looper.
*/
public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
- if (handler == null) {
- Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException(
- "No handler given, and current thread has no looper!");
- }
- handler = new Handler(looper);
+ CameraManagerGlobal.get().registerTorchCallback(callback,
+ CameraDeviceImpl.checkAndWrapHandler(handler));
+ }
+
+ /**
+ * Register a callback to be notified about torch mode status.
+ *
+ * <p>The behavior of this method matches that of
+ * {@link #registerTorchCallback(TorchCallback, Handler)},
+ * except that it uses {@link java.util.concurrent.Executor} as an argument
+ * instead of {@link android.os.Handler}.</p>
+ *
+ * @param executor The executor which will be used to invoke the callback
+ * @param callback The new callback to send torch mode status to
+ *
+ * @throws IllegalArgumentException if the executor is {@code null}.
+ */
+ public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull TorchCallback callback) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor was null");
}
- CameraManagerGlobal.get().registerTorchCallback(callback, handler);
+ CameraManagerGlobal.get().registerTorchCallback(callback, executor);
}
/**
@@ -257,7 +291,7 @@ public final class CameraManager {
*
* @param cameraId The unique identifier of the camera device to open
* @param callback The callback for the camera. Must not be null.
- * @param handler The handler to invoke the callback on. Must not be null.
+ * @param executor The executor to invoke the callback with. Must not be null.
* @param uid The UID of the application actually opening the camera.
* Must be USE_CALLING_UID unless the caller is a service
* that is trusted to open the device on behalf of an
@@ -276,7 +310,7 @@ public final class CameraManager {
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
private CameraDevice openCameraDeviceUserAsync(String cameraId,
- CameraDevice.StateCallback callback, Handler handler, final int uid)
+ CameraDevice.StateCallback callback, Executor executor, final int uid)
throws CameraAccessException {
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraDevice device = null;
@@ -289,7 +323,7 @@ public final class CameraManager {
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
callback,
- handler,
+ executor,
characteristics,
mContext.getApplicationInfo().targetSdkVersion);
@@ -430,7 +464,47 @@ public final class CameraManager {
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
- openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
+ openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
+ USE_CALLING_UID);
+ }
+
+ /**
+ * Open a connection to a camera with the given ID.
+ *
+ * <p>The behavior of this method matches that of
+ * {@link #openCamera(String, StateCallback, Handler)}, except that it uses
+ * {@link java.util.concurrent.Executor} as an argument instead of
+ * {@link android.os.Handler}.</p>
+ *
+ * @param cameraId
+ * The unique identifier of the camera device to open
+ * @param executor
+ * The executor which will be used when invoking the callback.
+ * @param callback
+ * The callback which is invoked once the camera is opened
+ *
+ * @throws CameraAccessException if the camera is disabled by device policy,
+ * has been disconnected, or is being used by a higher-priority camera API client.
+ *
+ * @throws IllegalArgumentException if cameraId, the callback or the executor was null,
+ * or the cameraId does not match any currently or previously available
+ * camera device.
+ *
+ * @throws SecurityException if the application does not have permission to
+ * access the camera
+ *
+ * @see #getCameraIdList
+ * @see android.app.admin.DevicePolicyManager#setCameraDisabled
+ */
+ @RequiresPermission(android.Manifest.permission.CAMERA)
+ public void openCamera(@NonNull String cameraId,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull final CameraDevice.StateCallback callback)
+ throws CameraAccessException {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor was null");
+ }
+ openCameraForUid(cameraId, callback, executor, USE_CALLING_UID);
}
/**
@@ -449,7 +523,7 @@ public final class CameraManager {
* @hide
*/
public void openCameraForUid(@NonNull String cameraId,
- @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
+ @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
int clientUid)
throws CameraAccessException {
@@ -457,19 +531,12 @@ public final class CameraManager {
throw new IllegalArgumentException("cameraId was null");
} else if (callback == null) {
throw new IllegalArgumentException("callback was null");
- } else if (handler == null) {
- if (Looper.myLooper() != null) {
- handler = new Handler();
- } else {
- throw new IllegalArgumentException(
- "Handler argument is null, but no looper exists in the calling thread");
- }
}
if (CameraManagerGlobal.sCameraServiceDisabled) {
throw new IllegalArgumentException("No cameras available on device");
}
- openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
+ openCameraDeviceUserAsync(cameraId, callback, executor, clientUid);
}
/**
@@ -728,12 +795,13 @@ public final class CameraManager {
*/
private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+ private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
// Camera ID -> Status map
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
- // Registered availablility callbacks and their handlers
- private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
- new ArrayMap<AvailabilityCallback, Handler>();
+ // Registered availablility callbacks and their executors
+ private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
+ new ArrayMap<AvailabilityCallback, Executor>();
// torch client binder to set the torch mode with.
private Binder mTorchClientBinder = new Binder();
@@ -741,9 +809,9 @@ public final class CameraManager {
// Camera ID -> Torch status map
private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
- // Registered torch callbacks and their handlers
- private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap =
- new ArrayMap<TorchCallback, Handler>();
+ // Registered torch callbacks and their executors
+ private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap =
+ new ArrayMap<TorchCallback, Executor>();
private final Object mLock = new Object();
@@ -925,49 +993,63 @@ public final class CameraManager {
}
}
- private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
+ private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
final String id, final int status) {
if (isAvailable(status)) {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onCameraAvailable(id);
- }
- });
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onCameraAvailable(id);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onCameraUnavailable(id);
- }
- });
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onCameraUnavailable(id);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
- private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler,
+ private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor,
final String id, final int status) {
switch(status) {
case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
- case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onTorchModeChanged(id, status ==
- ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
- }
+ case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ callback.onTorchModeChanged(id, status ==
+ ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
});
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
break;
- default:
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- callback.onTorchModeUnavailable(id);
- }
+ default: {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ callback.onTorchModeUnavailable(id);
});
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
break;
}
}
@@ -976,11 +1058,11 @@ public final class CameraManager {
* Send the state of all known cameras to the provided listener, to initialize
* the listener's knowledge of camera state.
*/
- private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
+ private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) {
for (int i = 0; i < mDeviceStatus.size(); i++) {
String id = mDeviceStatus.keyAt(i);
Integer status = mDeviceStatus.valueAt(i);
- postSingleUpdate(callback, handler, id, status);
+ postSingleUpdate(callback, executor, id, status);
}
}
@@ -1039,18 +1121,18 @@ public final class CameraManager {
final int callbackCount = mCallbackMap.size();
for (int i = 0; i < callbackCount; i++) {
- Handler handler = mCallbackMap.valueAt(i);
+ Executor executor = mCallbackMap.valueAt(i);
final AvailabilityCallback callback = mCallbackMap.keyAt(i);
- postSingleUpdate(callback, handler, id, status);
+ postSingleUpdate(callback, executor, id, status);
}
} // onStatusChangedLocked
- private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) {
+ private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
for (int i = 0; i < mTorchStatus.size(); i++) {
String id = mTorchStatus.keyAt(i);
Integer status = mTorchStatus.valueAt(i);
- postSingleTorchUpdate(callback, handler, id, status);
+ postSingleTorchUpdate(callback, executor, id, status);
}
}
@@ -1078,9 +1160,9 @@ public final class CameraManager {
final int callbackCount = mTorchCallbackMap.size();
for (int i = 0; i < callbackCount; i++) {
- final Handler handler = mTorchCallbackMap.valueAt(i);
+ final Executor executor = mTorchCallbackMap.valueAt(i);
final TorchCallback callback = mTorchCallbackMap.keyAt(i);
- postSingleTorchUpdate(callback, handler, id, status);
+ postSingleTorchUpdate(callback, executor, id, status);
}
} // onTorchStatusChangedLocked
@@ -1089,16 +1171,16 @@ public final class CameraManager {
* global listener singleton.
*
* @param callback the new callback to send camera availability notices to
- * @param handler The handler on which the callback should be invoked. May not be null.
+ * @param executor The executor which should invoke the callback. May not be null.
*/
- public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
+ public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) {
synchronized (mLock) {
connectCameraServiceLocked();
- Handler oldHandler = mCallbackMap.put(callback, handler);
+ Executor oldExecutor = mCallbackMap.put(callback, executor);
// For new callbacks, provide initial availability information
- if (oldHandler == null) {
- updateCallbackLocked(callback, handler);
+ if (oldExecutor == null) {
+ updateCallbackLocked(callback, executor);
}
// If not connected to camera service, schedule a reconnect to camera service.
@@ -1120,14 +1202,14 @@ public final class CameraManager {
}
}
- public void registerTorchCallback(TorchCallback callback, Handler handler) {
+ public void registerTorchCallback(TorchCallback callback, Executor executor) {
synchronized(mLock) {
connectCameraServiceLocked();
- Handler oldHandler = mTorchCallbackMap.put(callback, handler);
+ Executor oldExecutor = mTorchCallbackMap.put(callback, executor);
// For new callbacks, provide initial torch information
- if (oldHandler == null) {
- updateTorchCallbackLocked(callback, handler);
+ if (oldExecutor == null) {
+ updateTorchCallbackLocked(callback, executor);
}
// If not connected to camera service, schedule a reconnect to camera service.
@@ -1165,13 +1247,7 @@ public final class CameraManager {
* availability callback or torch status callback.
*/
private void scheduleCameraServiceReconnectionLocked() {
- final Handler handler;
-
- if (mCallbackMap.size() > 0) {
- handler = mCallbackMap.valueAt(0);
- } else if (mTorchCallbackMap.size() > 0) {
- handler = mTorchCallbackMap.valueAt(0);
- } else {
+ if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) {
// Not necessary to reconnect camera service if no client registers a callback.
return;
}
@@ -1181,22 +1257,21 @@ public final class CameraManager {
" ms");
}
- handler.postDelayed(
- new Runnable() {
- @Override
- public void run() {
- ICameraService cameraService = getCameraService();
- if (cameraService == null) {
- synchronized(mLock) {
- if (DEBUG) {
- Log.v(TAG, "Reconnecting Camera Service failed.");
- }
- scheduleCameraServiceReconnectionLocked();
- }
+ try {
+ mScheduler.schedule(() -> {
+ ICameraService cameraService = getCameraService();
+ if (cameraService == null) {
+ synchronized(mLock) {
+ if (DEBUG) {
+ Log.v(TAG, "Reconnecting Camera Service failed.");
}
+ scheduleCameraServiceReconnectionLocked();
}
- },
- CAMERA_SERVICE_RECONNECT_DELAY_MS);
+ }
+ }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
+ } catch (RejectedExecutionException e) {
+ Log.e(TAG, "Failed to schedule camera service re-connect: " + e);
+ }
}
/**
diff --git a/android/hardware/camera2/CameraMetadata.java b/android/hardware/camera2/CameraMetadata.java
index e7c89611..1a5d3ac9 100644
--- a/android/hardware/camera2/CameraMetadata.java
+++ b/android/hardware/camera2/CameraMetadata.java
@@ -342,7 +342,7 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the optical center of
* the largest camera device facing the same direction as this camera.</p>
- * <p>This default value for API levels before Android P.</p>
+ * <p>This is the default value for API levels before Android P.</p>
*
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
@@ -352,7 +352,6 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the position of the
* primary gyroscope of this Android device.</p>
- * <p>This is the value reported by all devices that support the MOTION_TRACKING capability.</p>
*
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
@@ -685,7 +684,7 @@ public abstract class CameraMetadata<TKey> {
* <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
* <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
* <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
- * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+ * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
* </ul>
* </li>
* <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li>
@@ -703,12 +702,12 @@ public abstract class CameraMetadata<TKey> {
* rate, including depth stall time.</p>
*
* @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE
+ * @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_FACING
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
* @see CameraCharacteristics#LENS_POSE_ROTATION
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
@@ -801,46 +800,12 @@ public abstract class CameraMetadata<TKey> {
public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9;
/**
- * <p>The device supports controls and metadata required for accurate motion tracking for
- * use cases such as augmented reality, electronic image stabilization, and so on.</p>
- * <p>This means this camera device has accurate optical calibration and timestamps relative
- * to the inertial sensors.</p>
- * <p>This capability requires the camera device to support the following:</p>
- * <ul>
- * <li>Capture request templates {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_PREVIEW } and {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_BEST } are defined.</li>
- * <li>The stream configurations listed in {@link android.hardware.camera2.CameraDevice#createCaptureSession } for MOTION_TRACKING are
- * supported, either at 30 or 60fps maximum frame rate.</li>
- * <li>The following camera characteristics and capture result metadata are provided:<ul>
- * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
- * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
- * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
- * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
- * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} with value GYROSCOPE</li>
- * </ul>
- * </li>
- * <li>The {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} field has value <code>REALTIME</code>. When compared to
- * timestamps from the device's gyroscopes, the clock difference for events occuring at
- * the same actual time instant will be less than 1 ms.</li>
- * <li>The value of the {@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew} field is accurate to within 1 ms.</li>
- * <li>The value of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} is guaranteed to be available in the
- * capture result.</li>
- * <li>The {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} control supports MOTION_TRACKING to limit maximum
- * exposure to 20 milliseconds.</li>
- * <li>The stream configurations required for MOTION_TRACKING (listed at {@link android.hardware.camera2.CameraDevice#createCaptureSession }) can operate at least at
- * 30fps; optionally, they can operate at 60fps, and '[60, 60]' is listed in
- * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}.</li>
- * </ul>
+ * <p>The camera device supports the MOTION_TRACKING value for
+ * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent}, which limits maximum exposure time to 20 ms.</p>
+ * <p>This limits the motion blur of capture images, resulting in better image tracking
+ * results for use cases such as image stabilization or augmented reality.</p>
*
- * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
- * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
- * @see CameraCharacteristics#LENS_POSE_REFERENCE
- * @see CameraCharacteristics#LENS_POSE_ROTATION
- * @see CameraCharacteristics#LENS_POSE_TRANSLATION
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
- * @see CaptureRequest#SENSOR_EXPOSURE_TIME
- * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
- * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10;
@@ -861,37 +826,49 @@ public abstract class CameraMetadata<TKey> {
* <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
* <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
* <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
- * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+ * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
* </ul>
* </li>
+ * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
+ * the same.</li>
* <li>The logical camera device must be LIMITED or higher device.</li>
* </ul>
* <p>Both the logical camera device and its underlying physical devices support the
* mandatory stream combinations required for their device levels.</p>
* <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
* <ul>
- * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
+ * <li>For each guaranteed stream combination, the logical camera supports replacing one
+ * logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
* or raw stream with two physical streams of the same size and format, each from a
* separate physical camera, given that the size and format are supported by both
* physical cameras.</li>
- * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
- * advertise RAW capability, but the underlying physical cameras do. This is usually
- * the case when the physical cameras have different sensor sizes.</li>
+ * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical
+ * cameras do, the logical camera will support guaranteed stream combinations for RAW
+ * capability, except that the RAW streams will be physical streams, each from a separate
+ * physical camera. This is usually the case when the physical cameras have different
+ * sensor sizes.</li>
* </ul>
* <p>Using physical streams in place of a logical stream of the same size and format will
* not slow down the frame rate of the capture, as long as the minimum frame duration
* of the physical and logical streams are the same.</p>
*
+ * @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
* @see CameraCharacteristics#LENS_POSE_ROTATION
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
* @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11;
+ /**
+ * <p>The camera device is a monochrome camera that doesn't contain a color filter array,
+ * and the pixel values on U and Y planes are all 128.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
@@ -1465,9 +1442,11 @@ public abstract class CameraMetadata<TKey> {
* for the external flash. Otherwise, this mode acts like ON.</p>
* <p>When the external flash is turned off, AE mode should be changed to one of the
* other available AE modes.</p>
- * <p>If the camera device supports AE external flash mode, aeState must be
- * FLASH_REQUIRED after the camera device finishes AE scan and it's too dark without
+ * <p>If the camera device supports AE external flash mode, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} must
+ * be FLASH_REQUIRED after the camera device finishes AE scan and it's too dark without
* flash.</p>
+ *
+ * @see CaptureResult#CONTROL_AE_STATE
* @see CaptureRequest#CONTROL_AE_MODE
*/
public static final int CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5;
@@ -2676,6 +2655,10 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>Include OIS data in the capture result.</p>
+ * <p>{@link CaptureResult#STATISTICS_OIS_SAMPLES android.statistics.oisSamples} provides OIS sample data in the
+ * output result metadata.</p>
+ *
+ * @see CaptureResult#STATISTICS_OIS_SAMPLES
* @see CaptureRequest#STATISTICS_OIS_DATA_MODE
*/
public static final int STATISTICS_OIS_DATA_MODE_ON = 1;
@@ -2754,6 +2737,31 @@ public abstract class CameraMetadata<TKey> {
public static final int TONEMAP_PRESET_CURVE_REC709 = 1;
//
+ // Enumeration values for CaptureRequest#DISTORTION_CORRECTION_MODE
+ //
+
+ /**
+ * <p>No distortion correction is applied.</p>
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+ */
+ public static final int DISTORTION_CORRECTION_MODE_OFF = 0;
+
+ /**
+ * <p>Lens distortion correction is applied without reducing frame rate
+ * relative to sensor output. It may be the same as OFF if distortion correction would
+ * reduce frame rate relative to sensor.</p>
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+ */
+ public static final int DISTORTION_CORRECTION_MODE_FAST = 1;
+
+ /**
+ * <p>High-quality distortion correction is applied, at the cost of
+ * possibly reduced frame rate relative to sensor output.</p>
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+ */
+ public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2;
+
+ //
// Enumeration values for CaptureResult#CONTROL_AE_STATE
//
diff --git a/android/hardware/camera2/CaptureRequest.java b/android/hardware/camera2/CaptureRequest.java
index 481b7649..22525719 100644
--- a/android/hardware/camera2/CaptureRequest.java
+++ b/android/hardware/camera2/CaptureRequest.java
@@ -24,6 +24,7 @@ import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.TypeReference;
+import android.hardware.camera2.utils.SurfaceUtils;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -643,6 +644,30 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
break;
}
}
+
+ if (!streamFound) {
+ // Check if we can match s by native object ID
+ long reqSurfaceId = SurfaceUtils.getSurfaceId(s);
+ for (int j = 0; j < configuredOutputs.size(); ++j) {
+ int streamId = configuredOutputs.keyAt(j);
+ OutputConfiguration outConfig = configuredOutputs.valueAt(j);
+ int surfaceId = 0;
+ for (Surface outSurface : outConfig.getSurfaces()) {
+ if (reqSurfaceId == SurfaceUtils.getSurfaceId(outSurface)) {
+ streamFound = true;
+ mStreamIdxArray[i] = streamId;
+ mSurfaceIdxArray[i] = surfaceId;
+ i++;
+ break;
+ }
+ surfaceId++;
+ }
+ if (streamFound) {
+ break;
+ }
+ }
+ }
+
if (!streamFound) {
mStreamIdxArray = null;
mSurfaceIdxArray = null;
@@ -783,7 +808,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*
*<p>This method can be called for logical camera devices, which are devices that have
* REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
- * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
+ * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty set of
* physical devices that are backing the logical camera. The camera Id included in the
* 'physicalCameraId' argument selects an individual physical device that will receive
* the customized capture request field.</p>
@@ -1432,7 +1457,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* is used, all non-zero weights will have the same effect. A region with 0 weight is
* ignored.</p>
* <p>If all regions have 0 weight, then no specific metering area needs to be used by the
- * camera device.</p>
+ * camera device. The capture result will either be a zero weight region as well, or
+ * the region selected by the camera device as the focus area of interest.</p>
* <p>If the metering region is outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in
* capture result metadata, the camera device will ignore the sections outside the crop
* region and output only the intersection rectangle as the metering region in the result
@@ -2757,21 +2783,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
/**
- * <p>Whether the camera device outputs the OIS data in output
+ * <p>A control for selecting whether OIS position information is included in output
* result metadata.</p>
- * <p>When set to ON,
- * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
- * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
* <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
* </ul></p>
* <p><b>Available values for this device:</b><br>
- * android.Statistics.info.availableOisDataModes</p>
+ * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
- * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+ * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES
* @see #STATISTICS_OIS_DATA_MODE_OFF
* @see #STATISTICS_OIS_DATA_MODE_ON
*/
@@ -2830,6 +2853,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+ * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+ * are ignored.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
@@ -2892,6 +2917,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+ * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+ * are ignored.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
@@ -3144,6 +3171,49 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
+ /**
+ * <p>Mode of operation for the lens distortion correction block.</p>
+ * <p>The lens distortion correction block attempts to improve image quality by fixing
+ * radial, tangential, or other geometric aberrations in the camera device's optics. If
+ * available, the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} field documents the lens's distortion parameters.</p>
+ * <p>OFF means no distortion correction is done.</p>
+ * <p>FAST/HIGH_QUALITY both mean camera device determined distortion correction will be
+ * applied. HIGH_QUALITY mode indicates that the camera device will use the highest-quality
+ * correction algorithms, even if it slows down capture rate. FAST means the camera device
+ * will not slow down capture rate when applying correction. FAST may be the same as OFF if
+ * any correction at all would slow down capture rate. Every output stream will have a
+ * similar amount of enhancement applied.</p>
+ * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
+ * applied to any RAW output. Metadata coordinates such as face rectangles or metering
+ * regions are also not affected by correction.</p>
+ * <p>Applications enabling distortion correction need to pay extra attention when converting
+ * image coordinates between corrected output buffers and the sensor array. For example, if
+ * the app supports tap-to-focus and enables correction, it then has to apply the distortion
+ * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
+ * calculate the tap position on the sensor active array to be used with
+ * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
+ * they need to be drawn on top of the corrected output buffers.</p>
+ * <p><b>Possible values:</b>
+ * <ul>
+ * <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
+ * <li>{@link #DISTORTION_CORRECTION_MODE_FAST FAST}</li>
+ * <li>{@link #DISTORTION_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+ * </ul></p>
+ * <p><b>Available values for this device:</b><br>
+ * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AF_REGIONS
+ * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
+ * @see CameraCharacteristics#LENS_DISTORTION
+ * @see #DISTORTION_CORRECTION_MODE_OFF
+ * @see #DISTORTION_CORRECTION_MODE_FAST
+ * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
+ */
+ @PublicKey
+ public static final Key<Integer> DISTORTION_CORRECTION_MODE =
+ new Key<Integer>("android.distortionCorrection.mode", int.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/android/hardware/camera2/CaptureResult.java b/android/hardware/camera2/CaptureResult.java
index d730fa8a..8df54472 100644
--- a/android/hardware/camera2/CaptureResult.java
+++ b/android/hardware/camera2/CaptureResult.java
@@ -1001,8 +1001,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* </tbody>
* </table>
* <p>If the camera device supports AE external flash mode (ON_EXTERNAL_FLASH is included in
- * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES android.control.aeAvailableModes}), aeState must be FLASH_REQUIRED after the camera device
- * finishes AE scan and it's too dark without flash.</p>
+ * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES android.control.aeAvailableModes}), {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} must be FLASH_REQUIRED after
+ * the camera device finishes AE scan and it's too dark without flash.</p>
* <p>For the above table, the camera device may skip reporting any state changes that happen
* without application intervention (i.e. mode switch, trigger, locking). Any state that
* can be skipped in that manner is called a transient state.</p>
@@ -1081,6 +1081,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* @see CaptureRequest#CONTROL_AE_LOCK
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CaptureResult#CONTROL_AE_STATE
* @see CaptureRequest#CONTROL_MODE
* @see CaptureRequest#CONTROL_SCENE_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
@@ -1156,7 +1157,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* is used, all non-zero weights will have the same effect. A region with 0 weight is
* ignored.</p>
* <p>If all regions have 0 weight, then no specific metering area needs to be used by the
- * camera device.</p>
+ * camera device. The capture result will either be a zero weight region as well, or
+ * the region selected by the camera device as the focus area of interest.</p>
* <p>If the metering region is outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in
* capture result metadata, the camera device will ignore the sections outside the crop
* region and output only the intersection rectangle as the metering region in the result
@@ -2781,7 +2783,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* from the main sensor along the +X axis (to the right from the user's perspective) will
* report <code>(0.03, 0, 0)</code>.</p>
* <p>To transform a pixel coordinates between two cameras facing the same direction, first
- * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for. Then the source
+ * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for. Then the source
* camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
* {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
* relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
@@ -2795,10 +2797,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
+ * @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
* @see CameraCharacteristics#LENS_POSE_ROTATION
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
*/
@PublicKey
public static final Key<float[]> LENS_POSE_TRANSLATION =
@@ -2844,7 +2846,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* where <code>(0,0)</code> is the top-left of the
* preCorrectionActiveArraySize rectangle. Once the pose and
* intrinsic calibration transforms have been applied to a
- * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}
+ * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}
* transform needs to be applied, and the result adjusted to
* be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
* system (where <code>(0, 0)</code> is the top-left of the
@@ -2857,9 +2859,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* coordinate system.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
+ * @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_POSE_ROTATION
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@@ -2901,12 +2903,59 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+ * @deprecated
+ * <p>This field was inconsistently defined in terms of its
+ * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p>
+ *
+ * @see CameraCharacteristics#LENS_DISTORTION
+
*/
+ @Deprecated
@PublicKey
public static final Key<float[]> LENS_RADIAL_DISTORTION =
new Key<float[]>("android.lens.radialDistortion", float[].class);
/**
+ * <p>The correction coefficients to correct for this camera device's
+ * radial and tangential lens distortion.</p>
+ * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was
+ * inconsistently defined.</p>
+ * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+ * kappa_3]</code> and two tangential distortion coefficients
+ * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+ * lens's geometric distortion with the mapping equations:</p>
+ * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+ * kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+ * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+ * kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+ * </code></pre>
+ * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+ * input image that correspond to the pixel values in the
+ * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+ * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+ * </code></pre>
+ * <p>The pixel coordinates are defined in a coordinate system
+ * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
+ * calibration fields; see that entry for details of the mapping stages.
+ * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+ * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+ * the range of the coordinates depends on the focal length
+ * terms of the intrinsic calibration.</p>
+ * <p>Finally, <code>r</code> represents the radial distance from the
+ * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+ * <p>The distortion model used is the Brown-Conrady model.</p>
+ * <p><b>Units</b>:
+ * Unitless coefficients.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+ * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_DISTORTION =
+ new Key<float[]>("android.lens.distortion", float[].class);
+
+ /**
* <p>Mode of operation for the noise reduction algorithm.</p>
* <p>The noise reduction algorithm attempts to improve image quality by removing
* excessive noise added by the capture process, especially in dark conditions.</p>
@@ -2979,6 +3028,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* Optional. Default value is FINAL.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
+ * <p>Not used in HALv3 or newer</p>
+
* @hide
*/
@Deprecated
@@ -2995,6 +3046,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* &gt; 0</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
+ * <p>Not used in HALv3 or newer</p>
+
* @hide
*/
@Deprecated
@@ -3775,6 +3828,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
*
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @deprecated
+ * <p>Never fully implemented or specified; do not use</p>
+
* @hide
*/
@Deprecated
@@ -3799,6 +3854,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* regardless of the android.control.* current values.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
+ * <p>Never fully implemented or specified; do not use</p>
+
* @hide
*/
@Deprecated
@@ -3909,21 +3966,18 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
/**
- * <p>Whether the camera device outputs the OIS data in output
+ * <p>A control for selecting whether OIS position information is included in output
* result metadata.</p>
- * <p>When set to ON,
- * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
- * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
* <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
* </ul></p>
* <p><b>Available values for this device:</b><br>
- * android.Statistics.info.availableOisDataModes</p>
+ * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
- * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+ * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES
* @see #STATISTICS_OIS_DATA_MODE_OFF
* @see #STATISTICS_OIS_DATA_MODE_ON
*/
@@ -3939,8 +3993,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CaptureResult#SENSOR_TIMESTAMP
+ * @hide
*/
- @PublicKey
public static final Key<long[]> STATISTICS_OIS_TIMESTAMPS =
new Key<long[]>("android.statistics.oisTimestamps", long[].class);
@@ -3948,16 +4002,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>An array of shifts of OIS samples, in x direction.</p>
* <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
* A positive value is a shift from left to right in active array coordinate system. For
- * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+ * example, if the optical center is (1000, 500) in active array coordinates, a shift of
* (3, 0) puts the new optical center at (1003, 500).</p>
* <p>The number of shifts must match the number of timestamps in
- * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+ * android.statistics.oisTimestamps.</p>
* <p><b>Units</b>: Pixels in active array.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
- *
- * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+ * @hide
*/
- @PublicKey
public static final Key<float[]> STATISTICS_OIS_X_SHIFTS =
new Key<float[]>("android.statistics.oisXShifts", float[].class);
@@ -3965,20 +4017,35 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>An array of shifts of OIS samples, in y direction.</p>
* <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
* A positive value is a shift from top to bottom in active array coordinate system. For
- * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+ * example, if the optical center is (1000, 500) in active array coordinates, a shift of
* (0, 5) puts the new optical center at (1000, 505).</p>
* <p>The number of shifts must match the number of timestamps in
- * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+ * android.statistics.oisTimestamps.</p>
* <p><b>Units</b>: Pixels in active array.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
- *
- * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+ * @hide
*/
- @PublicKey
public static final Key<float[]> STATISTICS_OIS_Y_SHIFTS =
new Key<float[]>("android.statistics.oisYShifts", float[].class);
/**
+ * <p>An array of OIS samples.</p>
+ * <p>Each OIS sample contains the timestamp and the amount of shifts in x and y direction,
+ * in pixels, of the OIS sample.</p>
+ * <p>A positive value for a shift in x direction is a shift from left to right in active array
+ * coordinate system. For example, if the optical center is (1000, 500) in active array
+ * coordinates, a shift of (3, 0) puts the new optical center at (1003, 500).</p>
+ * <p>A positive value for a shift in y direction is a shift from top to bottom in active array
+ * coordinate system. For example, if the optical center is (1000, 500) in active array
+ * coordinates, a shift of (0, 5) puts the new optical center at (1000, 505).</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.OisSample[]> STATISTICS_OIS_SAMPLES =
+ new Key<android.hardware.camera2.params.OisSample[]>("android.statistics.oisSamples", android.hardware.camera2.params.OisSample[].class);
+
+ /**
* <p>Tonemapping / contrast / gamma curve for the blue
* channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
* CONTRAST_CURVE.</p>
@@ -4029,6 +4096,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+ * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+ * are ignored.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
@@ -4091,6 +4160,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+ * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+ * are ignored.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
@@ -4383,6 +4454,49 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
+ /**
+ * <p>Mode of operation for the lens distortion correction block.</p>
+ * <p>The lens distortion correction block attempts to improve image quality by fixing
+ * radial, tangential, or other geometric aberrations in the camera device's optics. If
+ * available, the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} field documents the lens's distortion parameters.</p>
+ * <p>OFF means no distortion correction is done.</p>
+ * <p>FAST/HIGH_QUALITY both mean camera device determined distortion correction will be
+ * applied. HIGH_QUALITY mode indicates that the camera device will use the highest-quality
+ * correction algorithms, even if it slows down capture rate. FAST means the camera device
+ * will not slow down capture rate when applying correction. FAST may be the same as OFF if
+ * any correction at all would slow down capture rate. Every output stream will have a
+ * similar amount of enhancement applied.</p>
+ * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
+ * applied to any RAW output. Metadata coordinates such as face rectangles or metering
+ * regions are also not affected by correction.</p>
+ * <p>Applications enabling distortion correction need to pay extra attention when converting
+ * image coordinates between corrected output buffers and the sensor array. For example, if
+ * the app supports tap-to-focus and enables correction, it then has to apply the distortion
+ * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
+ * calculate the tap position on the sensor active array to be used with
+ * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
+ * they need to be drawn on top of the corrected output buffers.</p>
+ * <p><b>Possible values:</b>
+ * <ul>
+ * <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
+ * <li>{@link #DISTORTION_CORRECTION_MODE_FAST FAST}</li>
+ * <li>{@link #DISTORTION_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+ * </ul></p>
+ * <p><b>Available values for this device:</b><br>
+ * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AF_REGIONS
+ * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
+ * @see CameraCharacteristics#LENS_DISTORTION
+ * @see #DISTORTION_CORRECTION_MODE_OFF
+ * @see #DISTORTION_CORRECTION_MODE_FAST
+ * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
+ */
+ @PublicKey
+ public static final Key<Integer> DISTORTION_CORRECTION_MODE =
+ new Key<Integer>("android.distortionCorrection.mode", int.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/android/hardware/camera2/TotalCaptureResult.java b/android/hardware/camera2/TotalCaptureResult.java
index 657745c8..4e20cb8d 100644
--- a/android/hardware/camera2/TotalCaptureResult.java
+++ b/android/hardware/camera2/TotalCaptureResult.java
@@ -19,10 +19,13 @@ package android.hardware.camera2;
import android.annotation.NonNull;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* <p>The total assembled results of a single image capture from the image sensor.</p>
@@ -44,6 +47,12 @@ import java.util.List;
* as {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE}). Refer to each key documentation on
* a case-by-case basis.</p>
*
+ * <p>For a logical multi-camera device, if the CaptureRequest contains a surface for an underlying
+ * physical camera, the corresponding {@link TotalCaptureResult} object will include the metadata
+ * for that physical camera. And the mapping between the physical camera id and result metadata
+ * can be accessed via {@link #getPhysicalCameraResults}. If all requested surfaces are for the
+ * logical camera, no metadata for physical camera will be included.</p>
+ *
* <p>{@link TotalCaptureResult} objects are immutable.</p>
*
* @see CameraDevice.CaptureCallback#onCaptureCompleted
@@ -52,6 +61,8 @@ public final class TotalCaptureResult extends CaptureResult {
private final List<CaptureResult> mPartialResults;
private final int mSessionId;
+ // The map between physical camera id and capture result
+ private final HashMap<String, CaptureResult> mPhysicalCaptureResults;
/**
* Takes ownership of the passed-in camera metadata and the partial results
@@ -60,7 +71,8 @@ public final class TotalCaptureResult extends CaptureResult {
* @hide
*/
public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
- CaptureResultExtras extras, List<CaptureResult> partials, int sessionId) {
+ CaptureResultExtras extras, List<CaptureResult> partials, int sessionId,
+ PhysicalCaptureResultInfo physicalResults[]) {
super(results, parent, extras);
if (partials == null) {
@@ -70,6 +82,14 @@ public final class TotalCaptureResult extends CaptureResult {
}
mSessionId = sessionId;
+
+ mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
+ for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
+ CaptureResult physicalResult = new CaptureResult(
+ onePhysicalResult.getCameraMetadata(), parent, extras);
+ mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
+ physicalResult);
+ }
}
/**
@@ -83,6 +103,7 @@ public final class TotalCaptureResult extends CaptureResult {
mPartialResults = new ArrayList<>();
mSessionId = CameraCaptureSession.SESSION_ID_NONE;
+ mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
}
/**
@@ -111,4 +132,22 @@ public final class TotalCaptureResult extends CaptureResult {
public int getSessionId() {
return mSessionId;
}
+
+ /**
+ * Get the map between physical camera ids and their capture result metadata
+ *
+ * <p>This function can be called for logical multi-camera devices, which are devices that have
+ * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to {@link
+ * CameraCharacteristics#getPhysicalCameraIds} return a non-empty set of physical devices that
+ * are backing the logical camera.</p>
+ *
+ * <p>If one or more streams from the underlying physical cameras were requested by the
+ * corresponding capture request, this function returns the result metadata for those physical
+ * cameras. Otherwise, an empty map is returned.</p>
+
+ * @return unmodifiable map between physical camera ids and their capture result metadata
+ */
+ public Map<String, CaptureResult> getPhysicalCameraResults() {
+ return Collections.unmodifiableMap(mPhysicalCaptureResults);
+ }
}
diff --git a/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
deleted file mode 100644
index 866f370f..00000000
--- a/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * A dispatcher that replaces one argument with another; replaces any argument at an index
- * with another argument.
- *
- * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always
- * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to
- * be something
- * that's not an {@code int}.</p>
- *
- * @param <T>
- * source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <TArg>
- * argument replacement type, args in {@link #dispatch} matching {@code argumentIndex}
- * will be overriden to objects of this type
- */
-public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> {
-
- private final Dispatchable<T> mTarget;
- private final int mArgumentIndex;
- private final TArg mReplaceWith;
-
- /**
- * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target}
- * after the argument is replaced.
- *
- * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted
- * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set
- * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p>
- *
- * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is
- * passed through with the arguments unchanged.</p>
- *
- * @param target destination dispatch type, methods will be redirected to this dispatcher
- * @param argumentIndex the numeric index of the argument {@code >= 0}
- * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object
- */
- public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex,
- TArg replaceWith) {
- mTarget = checkNotNull(target, "target must not be null");
- mArgumentIndex = checkArgumentNonnegative(argumentIndex,
- "argumentIndex must not be negative");
- mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null");
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) throws Throwable {
-
- if (args.length > mArgumentIndex) {
- args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches
- args[mArgumentIndex] = mReplaceWith;
- }
-
- return mTarget.dispatch(method, args);
- }
-
- private static Object[] arrayCopy(Object[] array) {
- int length = array.length;
- Object[] newArray = new Object[length];
- for (int i = 0; i < length; ++i) {
- newArray[i] = array[i];
- }
- return newArray;
- }
-}
diff --git a/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/android/hardware/camera2/dispatch/BroadcastDispatcher.java
deleted file mode 100644
index fe575b27..00000000
--- a/android/hardware/camera2/dispatch/BroadcastDispatcher.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Broadcast a single dispatch into multiple other dispatchables.
- *
- * <p>Every time {@link #dispatch} is invoked, all the broadcast targets will
- * see the same dispatch as well. The first target's return value is returned.</p>
- *
- * <p>This enables a single listener to be converted into a multi-listener.</p>
- */
-public class BroadcastDispatcher<T> implements Dispatchable<T> {
-
- private final List<Dispatchable<T>> mDispatchTargets;
-
- /**
- * Create a broadcast dispatcher from the supplied dispatch targets.
- *
- * @param dispatchTargets one or more targets to dispatch to
- */
- @SafeVarargs
- public BroadcastDispatcher(Dispatchable<T>... dispatchTargets) {
- mDispatchTargets = Arrays.asList(
- checkNotNull(dispatchTargets, "dispatchTargets must not be null"));
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) throws Throwable {
- Object result = null;
- boolean gotResult = false;
-
- for (Dispatchable<T> dispatchTarget : mDispatchTargets) {
- Object localResult = dispatchTarget.dispatch(method, args);
-
- if (!gotResult) {
- gotResult = true;
- result = localResult;
- }
- }
-
- return result;
- }
-}
diff --git a/android/hardware/camera2/dispatch/Dispatchable.java b/android/hardware/camera2/dispatch/Dispatchable.java
deleted file mode 100644
index 753103f4..00000000
--- a/android/hardware/camera2/dispatch/Dispatchable.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-/**
- * Dynamically dispatch a method and its argument to some object.
- *
- * <p>This can be used to intercept method calls and do work around them, redirect work,
- * or block calls entirely.</p>
- */
-public interface Dispatchable<T> {
- /**
- * Dispatch the method and arguments to this object.
- * @param method a method defined in class {@code T}
- * @param args arguments corresponding to said {@code method}
- * @return the object returned when invoking {@code method}
- * @throws Throwable any exception that might have been raised while invoking the method
- */
- public Object dispatch(Method method, Object[] args) throws Throwable;
-}
diff --git a/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
deleted file mode 100644
index 75f97e46..00000000
--- a/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Duck typing dispatcher; converts dispatch methods calls from one class to another by
- * looking up equivalently methods at runtime by name.
- *
- * <p>For example, if two types have identical method names and arguments, but
- * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be
- * made from one type to the other.</p>
- *
- * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <T> destination dispatch type, methods will be converted to the class of {@code T}
- */
-public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> {
-
- private final MethodNameInvoker<T> mDuck;
-
- /**
- * Create a new duck typing dispatcher.
- *
- * @param target destination dispatch type, methods will be redirected to this dispatcher
- * @param targetClass destination dispatch class, methods will be converted to this class's
- */
- public DuckTypingDispatcher(Dispatchable<T> target, Class<T> targetClass) {
- checkNotNull(targetClass, "targetClass must not be null");
- checkNotNull(target, "target must not be null");
-
- mDuck = new MethodNameInvoker<T>(target, targetClass);
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) {
- return mDuck.invoke(method.getName(), args);
- }
-}
diff --git a/android/hardware/camera2/dispatch/HandlerDispatcher.java b/android/hardware/camera2/dispatch/HandlerDispatcher.java
deleted file mode 100644
index f8e9d49a..00000000
--- a/android/hardware/camera2/dispatch/HandlerDispatcher.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.os.Handler;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Forward all interface calls into a handler by posting it as a {@code Runnable}.
- *
- * <p>All calls will return immediately; functions with return values will return a default
- * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p>
- *
- * <p>Any exceptions thrown on the handler while trying to invoke a method
- * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any
- * checked exceptions to be thrown will result in "undefined" behavior
- * (although in practice it is usually thrown as normal).</p>
- */
-public class HandlerDispatcher<T> implements Dispatchable<T> {
-
- private static final String TAG = "HandlerDispatcher";
-
- private final Dispatchable<T> mDispatchTarget;
- private final Handler mHandler;
-
- /**
- * Create a dispatcher that forwards it's dispatch calls by posting
- * them onto the {@code handler} as a {@code Runnable}.
- *
- * @param dispatchTarget the destination whose method calls will be redirected into the handler
- * @param handler all calls into {@code dispatchTarget} will be posted onto this handler
- * @param <T> the type of the element you want to wrap.
- * @return a dispatcher that will forward it's dispatch calls to a handler
- */
- public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) {
- mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mHandler = checkNotNull(handler, "handler must not be null");
- }
-
- @Override
- public Object dispatch(final Method method, final Object[] args) throws Throwable {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- mDispatchTarget.dispatch(method, args);
- } catch (InvocationTargetException e) {
- Throwable t = e.getTargetException();
- // Potential UB. Hopefully 't' is a runtime exception.
- UncheckedThrow.throwAnyException(t);
- } catch (IllegalAccessException e) {
- // Impossible
- Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
- } catch (IllegalArgumentException e) {
- // Impossible
- Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
- } catch (Throwable e) {
- UncheckedThrow.throwAnyException(e);
- }
- }
- });
-
- // TODO handle primitive return values that would avoid NPE if unboxed
- return null;
- }
-}
diff --git a/android/hardware/camera2/dispatch/InvokeDispatcher.java b/android/hardware/camera2/dispatch/InvokeDispatcher.java
deleted file mode 100644
index ac5f5267..00000000
--- a/android/hardware/camera2/dispatch/InvokeDispatcher.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-
-public class InvokeDispatcher<T> implements Dispatchable<T> {
-
- private static final String TAG = "InvocationSink";
- private final T mTarget;
-
- public InvokeDispatcher(T target) {
- mTarget = checkNotNull(target, "target must not be null");
- }
-
- @Override
- public Object dispatch(Method method, Object[] args) {
- try {
- return method.invoke(mTarget, args);
- } catch (InvocationTargetException e) {
- Throwable t = e.getTargetException();
- // Potential UB. Hopefully 't' is a runtime exception.
- UncheckedThrow.throwAnyException(t);
- } catch (IllegalAccessException e) {
- // Impossible
- Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
- } catch (IllegalArgumentException e) {
- // Impossible
- Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
- }
-
- // unreachable
- return null;
- }
-}
diff --git a/android/hardware/camera2/dispatch/MethodNameInvoker.java b/android/hardware/camera2/dispatch/MethodNameInvoker.java
deleted file mode 100644
index 8296b7a9..00000000
--- a/android/hardware/camera2/dispatch/MethodNameInvoker.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
- *
- * @param <T> destination dispatch type, methods will be looked up in the class of {@code T}
- */
-public class MethodNameInvoker<T> {
-
- private final Dispatchable<T> mTarget;
- private final Class<T> mTargetClass;
- private final Method[] mTargetClassMethods;
- private final ConcurrentHashMap<String, Method> mMethods =
- new ConcurrentHashMap<>();
-
- /**
- * Create a new method name invoker.
- *
- * @param target destination dispatch type, invokes will be redirected to this dispatcher
- * @param targetClass destination dispatch class, the invoked methods will be from this class
- */
- public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
- mTargetClass = targetClass;
- mTargetClassMethods = targetClass.getMethods();
- mTarget = target;
- }
-
- /**
- * Invoke a method by its name.
- *
- * <p>If more than one method exists in {@code targetClass}, the first method with the right
- * number of arguments will be used, and later calls will all use that method.</p>
- *
- * @param methodName
- * The name of the method, which will be matched 1:1 to the destination method
- * @param params
- * Variadic parameter list.
- * @return
- * The same kind of value that would normally be returned by calling {@code methodName}
- * statically.
- *
- * @throws IllegalArgumentException if {@code methodName} does not exist on the target class
- * @throws Throwable will rethrow anything that the target method would normally throw
- */
- @SuppressWarnings("unchecked")
- public <K> K invoke(String methodName, Object... params) {
- checkNotNull(methodName, "methodName must not be null");
-
- Method targetMethod = mMethods.get(methodName);
- if (targetMethod == null) {
- for (Method method : mTargetClassMethods) {
- // TODO future: match types of params if possible
- if (method.getName().equals(methodName) &&
- (params.length == method.getParameterTypes().length) ) {
- targetMethod = method;
- mMethods.put(methodName, targetMethod);
- break;
- }
- }
-
- if (targetMethod == null) {
- throw new IllegalArgumentException(
- "Method " + methodName + " does not exist on class " + mTargetClass);
- }
- }
-
- try {
- return (K) mTarget.dispatch(targetMethod, params);
- } catch (Throwable e) {
- UncheckedThrow.throwAnyException(e);
- // unreachable
- return null;
- }
- }
-}
diff --git a/android/hardware/camera2/dispatch/NullDispatcher.java b/android/hardware/camera2/dispatch/NullDispatcher.java
deleted file mode 100644
index fada075c..00000000
--- a/android/hardware/camera2/dispatch/NullDispatcher.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-/**
- * Do nothing when dispatching; follows the null object pattern.
- */
-public class NullDispatcher<T> implements Dispatchable<T> {
- /**
- * Create a dispatcher that does nothing when dispatched to.
- */
- public NullDispatcher() {
- }
-
- /**
- * Do nothing; all parameters are ignored.
- */
- @Override
- public Object dispatch(Method method, Object[] args) {
- return null;
- }
-}
diff --git a/android/hardware/camera2/impl/CallbackProxies.java b/android/hardware/camera2/impl/CallbackProxies.java
index c9eecf10..9e4cb80b 100644
--- a/android/hardware/camera2/impl/CallbackProxies.java
+++ b/android/hardware/camera2/impl/CallbackProxies.java
@@ -15,16 +15,17 @@
*/
package android.hardware.camera2.impl;
+import android.os.Binder;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.dispatch.Dispatchable;
-import android.hardware.camera2.dispatch.MethodNameInvoker;
import android.view.Surface;
+import java.util.concurrent.Executor;
+
import static com.android.internal.util.Preconditions.*;
/**
@@ -34,164 +35,86 @@ import static com.android.internal.util.Preconditions.*;
* to use our own proxy mechanism.</p>
*/
public class CallbackProxies {
-
- // TODO: replace with codegen
-
- public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK {
- private final MethodNameInvoker<CameraDeviceImpl.StateCallbackKK> mProxy;
-
- public DeviceStateCallbackProxy(
- Dispatchable<CameraDeviceImpl.StateCallbackKK> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateCallbackKK.class);
- }
-
- @Override
- public void onOpened(CameraDevice camera) {
- mProxy.invoke("onOpened", camera);
- }
-
- @Override
- public void onDisconnected(CameraDevice camera) {
- mProxy.invoke("onDisconnected", camera);
- }
-
- @Override
- public void onError(CameraDevice camera, int error) {
- mProxy.invoke("onError", camera, error);
- }
-
- @Override
- public void onUnconfigured(CameraDevice camera) {
- mProxy.invoke("onUnconfigured", camera);
- }
-
- @Override
- public void onActive(CameraDevice camera) {
- mProxy.invoke("onActive", camera);
- }
-
- @Override
- public void onBusy(CameraDevice camera) {
- mProxy.invoke("onBusy", camera);
- }
-
- @Override
- public void onClosed(CameraDevice camera) {
- mProxy.invoke("onClosed", camera);
- }
-
- @Override
- public void onIdle(CameraDevice camera) {
- mProxy.invoke("onIdle", camera);
- }
- }
-
- @SuppressWarnings("deprecation")
- public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback {
- private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy;
-
- public DeviceCaptureCallbackProxy(
- Dispatchable<CameraDeviceImpl.CaptureCallback> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureCallback.class);
- }
-
- @Override
- public void onCaptureStarted(CameraDevice camera,
- CaptureRequest request, long timestamp, long frameNumber) {
- mProxy.invoke("onCaptureStarted", camera, request, timestamp, frameNumber);
- }
-
- @Override
- public void onCapturePartial(CameraDevice camera,
- CaptureRequest request, CaptureResult result) {
- mProxy.invoke("onCapturePartial", camera, request, result);
- }
-
- @Override
- public void onCaptureProgressed(CameraDevice camera,
- CaptureRequest request, CaptureResult partialResult) {
- mProxy.invoke("onCaptureProgressed", camera, request, partialResult);
- }
-
- @Override
- public void onCaptureCompleted(CameraDevice camera,
- CaptureRequest request, TotalCaptureResult result) {
- mProxy.invoke("onCaptureCompleted", camera, request, result);
- }
-
- @Override
- public void onCaptureFailed(CameraDevice camera,
- CaptureRequest request, CaptureFailure failure) {
- mProxy.invoke("onCaptureFailed", camera, request, failure);
- }
-
- @Override
- public void onCaptureSequenceCompleted(CameraDevice camera,
- int sequenceId, long frameNumber) {
- mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber);
- }
-
- @Override
- public void onCaptureSequenceAborted(CameraDevice camera,
- int sequenceId) {
- mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
- }
-
- @Override
- public void onCaptureBufferLost(CameraDevice camera,
- CaptureRequest request, Surface target, long frameNumber) {
- mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber);
- }
-
- }
-
public static class SessionStateCallbackProxy
extends CameraCaptureSession.StateCallback {
- private final MethodNameInvoker<CameraCaptureSession.StateCallback> mProxy;
+ private final Executor mExecutor;
+ private final CameraCaptureSession.StateCallback mCallback;
- public SessionStateCallbackProxy(
- Dispatchable<CameraCaptureSession.StateCallback> dispatchTarget) {
- dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget,
- CameraCaptureSession.StateCallback.class);
+ public SessionStateCallbackProxy(Executor executor,
+ CameraCaptureSession.StateCallback callback) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
+ mCallback = checkNotNull(callback, "callback must not be null");
}
@Override
public void onConfigured(CameraCaptureSession session) {
- mProxy.invoke("onConfigured", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onConfigured(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
- mProxy.invoke("onConfigureFailed", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onConfigureFailed(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onReady(CameraCaptureSession session) {
- mProxy.invoke("onReady", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onReady(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onActive(CameraCaptureSession session) {
- mProxy.invoke("onActive", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onActive(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onCaptureQueueEmpty(CameraCaptureSession session) {
- mProxy.invoke("onCaptureQueueEmpty", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onClosed(CameraCaptureSession session) {
- mProxy.invoke("onClosed", session);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onClosed(session));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
- mProxy.invoke("onSurfacePrepared", session, surface);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
diff --git a/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 8b8bbc34..a4640c1f 100644
--- a/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -20,20 +20,17 @@ import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceUser;
-import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
-import android.hardware.camera2.dispatch.BroadcastDispatcher;
-import android.hardware.camera2.dispatch.DuckTypingDispatcher;
-import android.hardware.camera2.dispatch.HandlerDispatcher;
-import android.hardware.camera2.dispatch.InvokeDispatcher;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.TaskDrainer;
import android.hardware.camera2.utils.TaskSingleDrainer;
+import android.os.Binder;
import android.os.Handler;
import android.util.Log;
import android.view.Surface;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.Executor;
import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
import static com.android.internal.util.Preconditions.*;
@@ -51,16 +48,16 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
private final Surface mInput;
/**
* User-specified state callback, used for outgoing events; calls to this object will be
- * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
+ * automatically invoked via {@code mStateExecutor}.
*/
private final CameraCaptureSession.StateCallback mStateCallback;
- /** User-specified state handler used for outgoing state callback events */
- private final Handler mStateHandler;
+ /** User-specified state executor used for outgoing state callback events */
+ private final Executor mStateExecutor;
/** Internal camera device; used to translate calls into existing deprecated API */
private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
- /** Internal handler; used for all incoming events to preserve total order */
- private final Handler mDeviceHandler;
+ /** Internal executor; used for all incoming events to preserve total order */
+ private final Executor mDeviceExecutor;
/** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
private final TaskDrainer<Integer> mSequenceDrainer;
@@ -87,9 +84,9 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
* (e.g. no pending captures, no repeating requests, no flush).</p>
*/
CameraCaptureSessionImpl(int id, Surface input,
- CameraCaptureSession.StateCallback callback, Handler stateHandler,
+ CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Handler deviceStateHandler, boolean configureSuccess) {
+ Executor deviceStateExecutor, boolean configureSuccess) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
@@ -98,10 +95,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
mIdString = String.format("Session %d: ", mId);
mInput = input;
- mStateHandler = checkHandler(stateHandler);
- mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
+ mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");
+ mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);
- mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
+ mDeviceExecutor = checkNotNull(deviceStateExecutor,
+ "deviceStateExecutor must not be null");
mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
/*
@@ -110,11 +108,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
* This ensures total ordering between CameraDevice.StateCallback and
* CameraDeviceImpl.CaptureCallback events.
*/
- mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
+ mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),
/*name*/"seq");
- mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
+ mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(),
/*name*/"idle");
- mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
+ mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(),
/*name*/"abort");
// CameraDevice should call configureOutputs and have it finish before constructing us
@@ -160,6 +158,49 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
@Override
public int capture(CaptureRequest request, CaptureCallback callback,
Handler handler) throws CameraAccessException {
+ checkCaptureRequest(request);
+
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ checkNotClosed();
+
+ handler = checkHandler(handler, callback);
+
+ if (DEBUG) {
+ Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
+ " handler " + handler);
+ }
+
+ return addPendingSequence(mDeviceImpl.capture(request,
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
+ }
+ }
+
+ @Override
+ public int captureSingleRequest(CaptureRequest request, Executor executor,
+ CaptureCallback callback) throws CameraAccessException {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ } else if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
+ checkCaptureRequest(request);
+
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ checkNotClosed();
+
+ executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+ if (DEBUG) {
+ Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
+ " executor " + executor);
+ }
+
+ return addPendingSequence(mDeviceImpl.capture(request,
+ createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+ }
+ }
+
+ private void checkCaptureRequest(CaptureRequest request) {
if (request == null) {
throw new IllegalArgumentException("request must not be null");
} else if (request.isReprocess() && !isReprocessable()) {
@@ -168,6 +209,12 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
} else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
throw new IllegalArgumentException("capture request was created for another session");
}
+ }
+
+ @Override
+ public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
+ Handler handler) throws CameraAccessException {
+ checkCaptureRequests(requests);
synchronized (mDeviceImpl.mInterfaceLock) {
checkNotClosed();
@@ -175,18 +222,43 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
handler = checkHandler(handler, callback);
if (DEBUG) {
- Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
- " handler " + handler);
+ CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+ Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
+ ", callback " + callback + " handler " + handler);
}
- return addPendingSequence(mDeviceImpl.capture(request,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ return addPendingSequence(mDeviceImpl.captureBurst(requests,
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@Override
- public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ public int captureBurstRequests(List<CaptureRequest> requests, Executor executor,
+ CaptureCallback callback) throws CameraAccessException {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ } else if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
+ checkCaptureRequests(requests);
+
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ checkNotClosed();
+
+ executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+ if (DEBUG) {
+ CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+ Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
+ ", callback " + callback + " executor " + executor);
+ }
+
+ return addPendingSequence(mDeviceImpl.captureBurst(requests,
+ createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+ }
+ }
+
+ private void checkCaptureRequests(List<CaptureRequest> requests) {
if (requests == null) {
throw new IllegalArgumentException("Requests must not be null");
} else if (requests.isEmpty()) {
@@ -205,76 +277,122 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
}
}
+ }
+
+ @Override
+ public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
+ Handler handler) throws CameraAccessException {
+ checkRepeatingRequest(request);
+
synchronized (mDeviceImpl.mInterfaceLock) {
checkNotClosed();
handler = checkHandler(handler, callback);
if (DEBUG) {
- CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
- Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
- ", callback " + callback + " handler " + handler);
+ Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
+ callback + " handler" + " " + handler);
}
- return addPendingSequence(mDeviceImpl.captureBurst(requests,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
}
}
@Override
- public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
- if (request == null) {
- throw new IllegalArgumentException("request must not be null");
- } else if (request.isReprocess()) {
- throw new IllegalArgumentException("repeating reprocess requests are not supported");
+ public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+ CaptureCallback callback) throws CameraAccessException {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ } else if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
}
+ checkRepeatingRequest(request);
synchronized (mDeviceImpl.mInterfaceLock) {
checkNotClosed();
- handler = checkHandler(handler, callback);
+ executor = CameraDeviceImpl.checkExecutor(executor, callback);
if (DEBUG) {
Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
- callback + " handler" + " " + handler);
+ callback + " executor" + " " + executor);
}
return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+ }
+ }
+
+ private void checkRepeatingRequest(CaptureRequest request) {
+ if (request == null) {
+ throw new IllegalArgumentException("request must not be null");
+ } else if (request.isReprocess()) {
+ throw new IllegalArgumentException("repeating reprocess requests are not supported");
}
}
@Override
public int setRepeatingBurst(List<CaptureRequest> requests,
CaptureCallback callback, Handler handler) throws CameraAccessException {
- if (requests == null) {
- throw new IllegalArgumentException("requests must not be null");
- } else if (requests.isEmpty()) {
- throw new IllegalArgumentException("requests must have at least one element");
- }
+ checkRepeatingRequests(requests);
- for (CaptureRequest r : requests) {
- if (r.isReprocess()) {
- throw new IllegalArgumentException("repeating reprocess burst requests are not " +
- "supported");
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ checkNotClosed();
+
+ handler = checkHandler(handler, callback);
+
+ if (DEBUG) {
+ CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+ Log.v(TAG, mIdString + "setRepeatingBurst - requests " +
+ Arrays.toString(requestArray) + ", callback " + callback +
+ " handler" + "" + handler);
}
+
+ return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
+ createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
+ }
+ }
+
+ @Override
+ public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor,
+ CaptureCallback callback) throws CameraAccessException {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ } else if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
}
+ checkRepeatingRequests(requests);
synchronized (mDeviceImpl.mInterfaceLock) {
checkNotClosed();
- handler = checkHandler(handler, callback);
+ executor = CameraDeviceImpl.checkExecutor(executor, callback);
if (DEBUG) {
CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
Log.v(TAG, mIdString + "setRepeatingBurst - requests " +
Arrays.toString(requestArray) + ", callback " + callback +
- " handler" + "" + handler);
+ " executor" + "" + executor);
}
return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
- createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+ createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+ }
+ }
+
+ private void checkRepeatingRequests(List<CaptureRequest> requests) {
+ if (requests == null) {
+ throw new IllegalArgumentException("requests must not be null");
+ } else if (requests.isEmpty()) {
+ throw new IllegalArgumentException("requests must have at least one element");
+ }
+
+ for (CaptureRequest r : requests) {
+ if (r.isReprocess()) {
+ throw new IllegalArgumentException("repeating reprocess burst requests are not " +
+ "supported");
+ }
}
}
@@ -446,119 +564,150 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
}
/**
- * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
+ * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}.
*/
- private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
- InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
- HandlerDispatcher<StateCallback> handlerPassthrough =
- new HandlerDispatcher<>(userCallbackSink, handler);
-
- return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
+ private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) {
+ return new CallbackProxies.SessionStateCallbackProxy(executor, callback);
}
/**
* Forward callbacks from
* CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
*
- * <p>In particular, all calls are automatically split to go both to our own
- * internal callback, and to the user-specified callback (by transparently posting
- * to the user-specified handler).</p>
- *
* <p>When a capture sequence finishes, update the pending checked sequences set.</p>
*/
@SuppressWarnings("deprecation")
private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
Handler handler, CaptureCallback callback) {
- CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
+ final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
+ handler) : null;
+ return createCaptureCallbackProxyWithExecutor(executor, callback);
+ }
+
+ private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxyWithExecutor(
+ Executor executor, CaptureCallback callback) {
+ return new CameraDeviceImpl.CaptureCallback() {
@Override
public void onCaptureStarted(CameraDevice camera,
CaptureRequest request, long timestamp, long frameNumber) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureStarted(
+ CameraCaptureSessionImpl.this, request, timestamp,
+ frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCapturePartial(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureResult result) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCapturePartial(
+ CameraCaptureSessionImpl.this, request, result));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureProgressed(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureProgressed(
+ CameraCaptureSessionImpl.this, request, partialResult));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureCompleted(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureCompleted(
+ CameraCaptureSessionImpl.this, request, result));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureFailed(CameraDevice camera,
CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureFailed(
+ CameraCaptureSessionImpl.this, request, failure));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public void onCaptureSequenceCompleted(CameraDevice camera,
int sequenceId, long frameNumber) {
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureSequenceCompleted(
+ CameraCaptureSessionImpl.this, sequenceId, frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
finishPendingSequence(sequenceId);
}
@Override
public void onCaptureSequenceAborted(CameraDevice camera,
int sequenceId) {
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureSequenceAborted(
+ CameraCaptureSessionImpl.this, sequenceId));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
finishPendingSequence(sequenceId);
}
@Override
public void onCaptureBufferLost(CameraDevice camera,
CaptureRequest request, Surface target, long frameNumber) {
- // Do nothing
+ if ((callback != null) && (executor != null)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onCaptureBufferLost(
+ CameraCaptureSessionImpl.this, request, target, frameNumber));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
-
};
-
- /*
- * Split the calls from the device callback into local callback and the following chain:
- * - replace the first CameraDevice arg with a CameraCaptureSession
- * - duck type from device callback to session callback
- * - then forward the call to a handler
- * - then finally invoke the destination method on the session callback object
- */
- if (callback == null) {
- // OK: API allows the user to not specify a callback, and the handler may
- // also be null in that case. Collapse whole dispatch chain to only call the local
- // callback
- return localCallback;
- }
-
- InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
- new InvokeDispatcher<>(localCallback);
-
- InvokeDispatcher<CaptureCallback> userCallbackSink =
- new InvokeDispatcher<>(callback);
- HandlerDispatcher<CaptureCallback> handlerPassthrough =
- new HandlerDispatcher<>(userCallbackSink, handler);
- DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
- = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
- ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
- replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
- /*argumentIndex*/0, this);
-
- BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
- new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
- replaceDeviceWithSession,
- localSink);
-
- return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
}
/**
*
- * Create an internal state callback, to be invoked on the mDeviceHandler
+ * Create an internal state callback, to be invoked on the mDeviceExecutor
*
* <p>It has a few behaviors:
* <ul>
diff --git a/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 06c2c25a..3494a7f2 100644
--- a/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -33,6 +33,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.Executor;
import static com.android.internal.util.Preconditions.*;
@@ -59,14 +60,14 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
* (e.g. no pending captures, no repeating requests, no flush).</p>
*/
CameraConstrainedHighSpeedCaptureSessionImpl(int id,
- CameraCaptureSession.StateCallback callback, Handler stateHandler,
+ CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Handler deviceStateHandler, boolean configureSuccess,
+ Executor deviceStateExecutor, boolean configureSuccess,
CameraCharacteristics characteristics) {
mCharacteristics = characteristics;
CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
- stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
+ stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
}
@Override
@@ -192,6 +193,13 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
}
@Override
+ public int captureSingleRequest(CaptureRequest request, Executor executor,
+ CaptureCallback listener) throws CameraAccessException {
+ throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+ + " this method");
+ }
+
+ @Override
public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
Handler handler) throws CameraAccessException {
if (!isConstrainedHighSpeedRequestList(requests)) {
@@ -203,6 +211,17 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
}
@Override
+ public int captureBurstRequests(List<CaptureRequest> requests, Executor executor,
+ CaptureCallback listener) throws CameraAccessException {
+ if (!isConstrainedHighSpeedRequestList(requests)) {
+ throw new IllegalArgumentException(
+ "Only request lists created by createHighSpeedRequestList() can be submitted to " +
+ "a constrained high speed capture session");
+ }
+ return mSessionImpl.captureBurstRequests(requests, executor, listener);
+ }
+
+ @Override
public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
Handler handler) throws CameraAccessException {
throw new UnsupportedOperationException("Constrained high speed session doesn't support"
@@ -210,6 +229,13 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
}
@Override
+ public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+ CaptureCallback listener) throws CameraAccessException {
+ throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+ + " this method");
+ }
+
+ @Override
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
Handler handler) throws CameraAccessException {
if (!isConstrainedHighSpeedRequestList(requests)) {
@@ -221,6 +247,17 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
}
@Override
+ public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor,
+ CaptureCallback listener) throws CameraAccessException {
+ if (!isConstrainedHighSpeedRequestList(requests)) {
+ throw new IllegalArgumentException(
+ "Only request lists created by createHighSpeedRequestList() can be submitted to " +
+ "a constrained high speed capture session");
+ }
+ return mSessionImpl.setRepeatingBurstRequests(requests, executor, listener);
+ }
+
+ @Override
public void stopRepeating() throws CameraAccessException {
mSessionImpl.stopRepeating();
}
diff --git a/android/hardware/camera2/impl/CameraDeviceImpl.java b/android/hardware/camera2/impl/CameraDeviceImpl.java
index cab9d704..d967fbaf 100644
--- a/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,6 +18,7 @@ package android.hardware.camera2.impl;
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import android.annotation.NonNull;
import android.hardware.ICameraService;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
@@ -35,6 +36,7 @@ import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SubmitInfo;
import android.hardware.camera2.utils.SurfaceUtils;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -47,6 +49,8 @@ import android.util.Size;
import android.util.SparseArray;
import android.view.Surface;
+import com.android.internal.util.Preconditions;
+
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
@@ -58,6 +62,8 @@ import java.util.List;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Executor;
+
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -78,7 +84,7 @@ public class CameraDeviceImpl extends CameraDevice
private final StateCallback mDeviceCallback;
private volatile StateCallbackKK mSessionStateCallback;
- private final Handler mDeviceHandler;
+ private final Executor mDeviceExecutor;
private final AtomicBoolean mClosing = new AtomicBoolean();
private boolean mInError = false;
@@ -234,14 +240,14 @@ public class CameraDeviceImpl extends CameraDevice
}
};
- public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
+ public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
CameraCharacteristics characteristics, int appTargetSdkVersion) {
- if (cameraId == null || callback == null || handler == null || characteristics == null) {
+ if (cameraId == null || callback == null || executor == null || characteristics == null) {
throw new IllegalArgumentException("Null argument given");
}
mCameraId = cameraId;
mDeviceCallback = callback;
- mDeviceHandler = handler;
+ mDeviceExecutor = executor;
mCharacteristics = characteristics;
mAppTargetSdkVersion = appTargetSdkVersion;
@@ -288,15 +294,15 @@ public class CameraDeviceImpl extends CameraDevice
try {
remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
} catch (RemoteException e) {
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"The camera device has encountered a serious error");
}
}
- mDeviceHandler.post(mCallOnOpened);
- mDeviceHandler.post(mCallOnUnconfigured);
+ mDeviceExecutor.execute(mCallOnOpened);
+ mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
@@ -335,7 +341,7 @@ public class CameraDeviceImpl extends CameraDevice
final boolean isError = failureIsError;
synchronized(mInterfaceLock) {
mInError = true;
- mDeviceHandler.post(new Runnable() {
+ mDeviceExecutor.execute(new Runnable() {
@Override
public void run() {
if (isError) {
@@ -423,7 +429,7 @@ public class CameraDeviceImpl extends CameraDevice
}
}
- mDeviceHandler.post(mCallOnBusy);
+ mDeviceExecutor.execute(mCallOnBusy);
stopRepeating();
try {
@@ -482,10 +488,10 @@ public class CameraDeviceImpl extends CameraDevice
throw e;
} finally {
if (success && outputs.size() > 0) {
- mDeviceHandler.post(mCallOnIdle);
+ mDeviceExecutor.execute(mCallOnIdle);
} else {
// Always return to the 'unconfigured' state if we didn't hit a fatal error
- mDeviceHandler.post(mCallOnUnconfigured);
+ mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
}
@@ -501,8 +507,9 @@ public class CameraDeviceImpl extends CameraDevice
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(null, outConfigurations, callback, handler,
- /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+ createCaptureSessionInternal(null, outConfigurations, callback,
+ checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+ /*sessionParams*/ null);
}
@Override
@@ -517,7 +524,7 @@ public class CameraDeviceImpl extends CameraDevice
// OutputConfiguration objects are immutable, but need to have our own array
List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
- createCaptureSessionInternal(null, currentOutputs, callback, handler,
+ createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler),
/*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null);
}
@@ -537,8 +544,9 @@ public class CameraDeviceImpl extends CameraDevice
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
- /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+ createCaptureSessionInternal(inputConfig, outConfigurations, callback,
+ checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+ /*sessionParams*/ null);
}
@Override
@@ -566,8 +574,8 @@ public class CameraDeviceImpl extends CameraDevice
currentOutputs.add(new OutputConfiguration(output));
}
createCaptureSessionInternal(inputConfig, currentOutputs,
- callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
- /*sessionParams*/ null);
+ callback, checkAndWrapHandler(handler),
+ /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
}
@Override
@@ -582,7 +590,8 @@ public class CameraDeviceImpl extends CameraDevice
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionInternal(null, outConfigurations, callback, handler,
+ createCaptureSessionInternal(null, outConfigurations, callback,
+ checkAndWrapHandler(handler),
/*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE,
/*sessionParams*/ null);
}
@@ -597,8 +606,8 @@ public class CameraDeviceImpl extends CameraDevice
for (OutputConfiguration output : outputs) {
currentOutputs.add(new OutputConfiguration(output));
}
- createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode,
- /*sessionParams*/ null);
+ createCaptureSessionInternal(inputConfig, currentOutputs, callback,
+ checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null);
}
@Override
@@ -612,14 +621,17 @@ public class CameraDeviceImpl extends CameraDevice
if (outputConfigs == null) {
throw new IllegalArgumentException("Invalid output configurations");
}
+ if (config.getExecutor() == null) {
+ throw new IllegalArgumentException("Invalid executor");
+ }
createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
- config.getStateCallback(), config.getHandler(), config.getSessionType(),
+ config.getStateCallback(), config.getExecutor(), config.getSessionType(),
config.getSessionParameters());
}
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
- CameraCaptureSession.StateCallback callback, Handler handler,
+ CameraCaptureSession.StateCallback callback, Executor executor,
int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
synchronized(mInterfaceLock) {
if (DEBUG) {
@@ -673,12 +685,11 @@ public class CameraDeviceImpl extends CameraDevice
SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
- callback, handler, this, mDeviceHandler, configureSuccess,
+ callback, executor, this, mDeviceExecutor, configureSuccess,
mCharacteristics);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
- callback, handler, this, mDeviceHandler,
- configureSuccess);
+ callback, executor, this, mDeviceExecutor, configureSuccess);
}
// TODO: wait until current session closes, then create the new session
@@ -893,22 +904,22 @@ public class CameraDeviceImpl extends CameraDevice
}
}
- public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
+ public int capture(CaptureRequest request, CaptureCallback callback, Executor executor)
throws CameraAccessException {
if (DEBUG) {
Log.d(TAG, "calling capture");
}
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
requestList.add(request);
- return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
+ return submitCaptureRequest(requestList, callback, executor, /*streaming*/false);
}
public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
if (requests == null || requests.isEmpty()) {
throw new IllegalArgumentException("At least one request must be given");
}
- return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
+ return submitCaptureRequest(requests, callback, executor, /*streaming*/false);
}
/**
@@ -963,7 +974,12 @@ public class CameraDeviceImpl extends CameraDevice
}
}
};
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
Log.w(TAG, String.format(
"did not register callback to request %d",
@@ -982,11 +998,11 @@ public class CameraDeviceImpl extends CameraDevice
}
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
- Handler handler, boolean repeating) throws CameraAccessException {
+ Executor executor, boolean repeating) throws CameraAccessException {
- // Need a valid handler, or current thread needs to have a looper, if
+ // Need a valid executor, or current thread needs to have a looper, if
// callback is valid
- handler = checkHandler(handler, callback);
+ executor = checkExecutor(executor, callback);
// Make sure that there all requests have at least 1 surface; all surfaces are non-null;
// the surface isn't a physical stream surface for reprocessing request
@@ -1001,19 +1017,17 @@ public class CameraDeviceImpl extends CameraDevice
throw new IllegalArgumentException("Null Surface targets are not allowed");
}
- if (!request.isReprocess()) {
- continue;
- }
for (int i = 0; i < mConfiguredOutputs.size(); i++) {
OutputConfiguration configuration = mConfiguredOutputs.valueAt(i);
if (configuration.isForPhysicalCamera()
&& configuration.getSurfaces().contains(surface)) {
- throw new IllegalArgumentException(
- "Reprocess request on physical stream is not allowed");
+ if (request.isReprocess()) {
+ throw new IllegalArgumentException(
+ "Reprocess request on physical stream is not allowed");
+ }
}
}
}
-
}
synchronized(mInterfaceLock) {
@@ -1042,7 +1056,7 @@ public class CameraDeviceImpl extends CameraDevice
if (callback != null) {
mCaptureCallbackMap.put(requestInfo.getRequestId(),
new CaptureCallbackHolder(
- callback, requestList, handler, repeating, mNextSessionId - 1));
+ callback, requestList, executor, repeating, mNextSessionId - 1));
} else {
if (DEBUG) {
Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
@@ -1061,7 +1075,7 @@ public class CameraDeviceImpl extends CameraDevice
}
if (mIdle) {
- mDeviceHandler.post(mCallOnActive);
+ mDeviceExecutor.execute(mCallOnActive);
}
mIdle = false;
@@ -1070,18 +1084,18 @@ public class CameraDeviceImpl extends CameraDevice
}
public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
requestList.add(request);
- return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
+ return submitCaptureRequest(requestList, callback, executor, /*streaming*/true);
}
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
- Handler handler) throws CameraAccessException {
+ Executor executor) throws CameraAccessException {
if (requests == null || requests.isEmpty()) {
throw new IllegalArgumentException("At least one request must be given");
}
- return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
+ return submitCaptureRequest(requests, callback, executor, /*streaming*/true);
}
public void stopRepeating() throws CameraAccessException {
@@ -1126,12 +1140,12 @@ public class CameraDeviceImpl extends CameraDevice
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
- mDeviceHandler.post(mCallOnBusy);
+ mDeviceExecutor.execute(mCallOnBusy);
// If already idle, just do a busy->idle transition immediately, don't actually
// flush.
if (mIdle) {
- mDeviceHandler.post(mCallOnIdle);
+ mDeviceExecutor.execute(mCallOnIdle);
return;
}
@@ -1159,7 +1173,7 @@ public class CameraDeviceImpl extends CameraDevice
// either a normal close where the remote device is valid
// or a close after a startup error (no remote device but in error state)
if (mRemoteDevice != null || mInError) {
- mDeviceHandler.post(mCallOnClosed);
+ mDeviceExecutor.execute(mCallOnClosed);
}
mRemoteDevice = null;
@@ -1356,7 +1370,7 @@ public class CameraDeviceImpl extends CameraDevice
private final boolean mRepeating;
private final CaptureCallback mCallback;
private final List<CaptureRequest> mRequestList;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final int mSessionId;
/**
* <p>Determine if the callback holder is for a constrained high speed request list that
@@ -1368,13 +1382,13 @@ public class CameraDeviceImpl extends CameraDevice
private final boolean mHasBatchedOutputs;
CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
- Handler handler, boolean repeating, int sessionId) {
- if (callback == null || handler == null) {
+ Executor executor, boolean repeating, int sessionId) {
+ if (callback == null || executor == null) {
throw new UnsupportedOperationException(
"Must have a valid handler and a valid callback");
}
mRepeating = repeating;
- mHandler = handler;
+ mExecutor = executor;
mRequestList = new ArrayList<CaptureRequest>(requestList);
mCallback = callback;
mSessionId = sessionId;
@@ -1427,8 +1441,8 @@ public class CameraDeviceImpl extends CameraDevice
return getRequest(0);
}
- public Handler getHandler() {
- return mHandler;
+ public Executor getExecutor() {
+ return mExecutor;
}
public int getSessionId() {
@@ -1812,7 +1826,12 @@ public class CameraDeviceImpl extends CameraDevice
}
}
};
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
@@ -1840,7 +1859,12 @@ public class CameraDeviceImpl extends CameraDevice
switch (errorCode) {
case ERROR_CAMERA_DISCONNECTED:
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
break;
case ERROR_CAMERA_REQUEST:
case ERROR_CAMERA_RESULT:
@@ -1862,8 +1886,13 @@ public class CameraDeviceImpl extends CameraDevice
private void scheduleNotifyError(int code) {
mInError = true;
- CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
- CameraDeviceCallbacks::notifyError, this, code));
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable(
+ CameraDeviceCallbacks::notifyError, this, code));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private void notifyError(int code) {
@@ -1902,7 +1931,12 @@ public class CameraDeviceImpl extends CameraDevice
if (mRemoteDevice == null) return; // Camera already closed
if (!CameraDeviceImpl.this.mIdle) {
- CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnIdle);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
CameraDeviceImpl.this.mIdle = true;
}
@@ -1931,42 +1965,48 @@ public class CameraDeviceImpl extends CameraDevice
if (isClosed()) return;
// Dispatch capture start notice
- holder.getHandler().post(
- new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
- final int subsequenceId = resultExtras.getSubsequenceId();
- final CaptureRequest request = holder.getRequest(subsequenceId);
-
- if (holder.hasBatchedOutputs()) {
- // Send derived onCaptureStarted for requests within the batch
- final Range<Integer> fpsRange =
- request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
- for (int i = 0; i < holder.getRequestCount(); i++) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ final int subsequenceId = resultExtras.getSubsequenceId();
+ final CaptureRequest request = holder.getRequest(subsequenceId);
+
+ if (holder.hasBatchedOutputs()) {
+ // Send derived onCaptureStarted for requests within the
+ // batch
+ final Range<Integer> fpsRange =
+ request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+ for (int i = 0; i < holder.getRequestCount(); i++) {
+ holder.getCallback().onCaptureStarted(
+ CameraDeviceImpl.this,
+ holder.getRequest(i),
+ timestamp - (subsequenceId - i) *
+ NANO_PER_SECOND/fpsRange.getUpper(),
+ frameNumber - (subsequenceId - i));
+ }
+ } else {
holder.getCallback().onCaptureStarted(
CameraDeviceImpl.this,
- holder.getRequest(i),
- timestamp - (subsequenceId - i) *
- NANO_PER_SECOND/fpsRange.getUpper(),
- frameNumber - (subsequenceId - i));
+ holder.getRequest(resultExtras.getSubsequenceId()),
+ timestamp, frameNumber);
}
- } else {
- holder.getCallback().onCaptureStarted(
- CameraDeviceImpl.this,
- holder.getRequest(resultExtras.getSubsequenceId()),
- timestamp, frameNumber);
}
}
- }
- });
-
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@Override
public void onResultReceived(CameraMetadataNative result,
- CaptureResultExtras resultExtras) throws RemoteException {
+ CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])
+ throws RemoteException {
int requestId = resultExtras.getRequestId();
long frameNumber = resultExtras.getFrameNumber();
@@ -2073,7 +2113,8 @@ public class CameraDeviceImpl extends CameraDevice
request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
final int subsequenceId = resultExtras.getSubsequenceId();
final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
- request, resultExtras, partialResults, holder.getSessionId());
+ request, resultExtras, partialResults, holder.getSessionId(),
+ physicalResults);
// Final capture result
resultDispatch = new Runnable() {
@Override
@@ -2088,9 +2129,11 @@ public class CameraDeviceImpl extends CameraDevice
NANO_PER_SECOND/fpsRange.getUpper());
CameraMetadataNative resultLocal =
new CameraMetadataNative(resultCopy);
+ // No logical multi-camera support for batched output mode.
TotalCaptureResult resultInBatch = new TotalCaptureResult(
resultLocal, holder.getRequest(i), resultExtras,
- partialResults, holder.getSessionId());
+ partialResults, holder.getSessionId(),
+ new PhysicalCaptureResultInfo[0]);
holder.getCallback().onCaptureCompleted(
CameraDeviceImpl.this,
@@ -2109,7 +2152,12 @@ public class CameraDeviceImpl extends CameraDevice
finalResult = resultAsCapture;
}
- holder.getHandler().post(resultDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
// Collect the partials for a total result; or mark the frame as totally completed
mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
@@ -2205,7 +2253,12 @@ public class CameraDeviceImpl extends CameraDevice
}
};
// Dispatch the failure callback
- holder.getHandler().post(failureDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(failureDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
} else {
boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
@@ -2245,7 +2298,12 @@ public class CameraDeviceImpl extends CameraDevice
checkAndFireSequenceComplete();
// Dispatch the failure callback
- holder.getHandler().post(failureDispatch);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(failureDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -2253,6 +2311,62 @@ public class CameraDeviceImpl extends CameraDevice
} // public class CameraDeviceCallbacks
/**
+ * A camera specific adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+ private static class CameraHandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public CameraHandlerExecutor(@NonNull Handler handler) {
+ mHandler = Preconditions.checkNotNull(handler);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ // Return value of 'post()' will be ignored in order to keep the
+ // same camera behavior. For further details see b/74605221 .
+ mHandler.post(command);
+ }
+ }
+
+ /**
+ * Default executor management.
+ *
+ * <p>
+ * If executor is null, get the current thread's
+ * Looper to create a Executor with. If no looper exists, throw
+ * {@code IllegalArgumentException}.
+ * </p>
+ */
+ static Executor checkExecutor(Executor executor) {
+ return (executor == null) ? checkAndWrapHandler(null) : executor;
+ }
+
+ /**
+ * Default executor management.
+ *
+ * <p>If the callback isn't null, check the executor, otherwise pass it through.</p>
+ */
+ public static <T> Executor checkExecutor(Executor executor, T callback) {
+ return (callback != null) ? checkExecutor(executor) : executor;
+ }
+
+ /**
+ * Wrap Handler in Executor.
+ *
+ * <p>
+ * If handler is null, get the current thread's
+ * Looper to create a Executor with. If no looper exists, throw
+ * {@code IllegalArgumentException}.
+ * </p>
+ */
+ public static Executor checkAndWrapHandler(Handler handler) {
+ return new CameraHandlerExecutor(checkHandler(handler));
+ }
+
+ /**
* Default handler management.
*
* <p>
@@ -2326,6 +2440,11 @@ public class CameraDeviceImpl extends CameraDevice
}
}
};
- CameraDeviceImpl.this.mDeviceHandler.post(r);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ CameraDeviceImpl.this.mDeviceExecutor.execute(r);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
diff --git a/android/hardware/camera2/impl/CameraMetadataNative.java b/android/hardware/camera2/impl/CameraMetadataNative.java
index ebe2fa17..e4b1339f 100644
--- a/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -22,12 +22,12 @@ import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.marshal.Marshaler;
import android.hardware.camera2.marshal.MarshalQueryable;
import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.marshal.Marshaler;
import android.hardware.camera2.marshal.impl.MarshalQueryableArray;
-import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
import android.hardware.camera2.marshal.impl.MarshalQueryableBlackLevelPattern;
+import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform;
import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
import android.hardware.camera2.marshal.impl.MarshalQueryableHighSpeedVideoConfiguration;
@@ -48,6 +48,7 @@ import android.hardware.camera2.marshal.impl.MarshalQueryableString;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
+import android.hardware.camera2.params.OisSample;
import android.hardware.camera2.params.ReprocessFormatsMap;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
@@ -56,8 +57,8 @@ import android.hardware.camera2.params.TonemapCurve;
import android.hardware.camera2.utils.TypeReference;
import android.location.Location;
import android.location.LocationManager;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.ServiceSpecificException;
import android.util.Log;
import android.util.Size;
@@ -614,6 +615,15 @@ public class CameraMetadataNative implements Parcelable {
return (T) metadata.getLensShadingMap();
}
});
+ sGetCommandMap.put(
+ CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getOisSamples();
+ }
+ });
}
private int[] getAvailableFormats() {
@@ -962,6 +972,50 @@ public class CameraMetadataNative implements Parcelable {
return tc;
}
+ private OisSample[] getOisSamples() {
+ long[] timestamps = getBase(CaptureResult.STATISTICS_OIS_TIMESTAMPS);
+ float[] xShifts = getBase(CaptureResult.STATISTICS_OIS_X_SHIFTS);
+ float[] yShifts = getBase(CaptureResult.STATISTICS_OIS_Y_SHIFTS);
+
+ if (timestamps == null) {
+ if (xShifts != null) {
+ throw new AssertionError("timestamps is null but xShifts is not");
+ }
+
+ if (yShifts != null) {
+ throw new AssertionError("timestamps is null but yShifts is not");
+ }
+
+ return null;
+ }
+
+ if (xShifts == null) {
+ throw new AssertionError("timestamps is not null but xShifts is");
+ }
+
+ if (yShifts == null) {
+ throw new AssertionError("timestamps is not null but yShifts is");
+ }
+
+ if (xShifts.length != timestamps.length) {
+ throw new AssertionError(String.format(
+ "timestamps has %d entries but xShifts has %d", timestamps.length,
+ xShifts.length));
+ }
+
+ if (yShifts.length != timestamps.length) {
+ throw new AssertionError(String.format(
+ "timestamps has %d entries but yShifts has %d", timestamps.length,
+ yShifts.length));
+ }
+
+ OisSample[] samples = new OisSample[timestamps.length];
+ for (int i = 0; i < timestamps.length; i++) {
+ samples[i] = new OisSample(timestamps[i], xShifts[i], yShifts[i]);
+ }
+ return samples;
+ }
+
private <T> void setBase(CameraCharacteristics.Key<T> key, T value) {
setBase(key.getNativeKey(), value);
}
diff --git a/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java b/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java
new file mode 100644
index 00000000..30eaf137
--- /dev/null
+++ b/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.impl;
+
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public class PhysicalCaptureResultInfo implements Parcelable {
+ private String cameraId;
+ private CameraMetadataNative cameraMetadata;
+
+ public static final Parcelable.Creator<PhysicalCaptureResultInfo> CREATOR =
+ new Parcelable.Creator<PhysicalCaptureResultInfo>() {
+ @Override
+ public PhysicalCaptureResultInfo createFromParcel(Parcel in) {
+ return new PhysicalCaptureResultInfo(in);
+ }
+
+ @Override
+ public PhysicalCaptureResultInfo[] newArray(int size) {
+ return new PhysicalCaptureResultInfo[size];
+ }
+ };
+
+ private PhysicalCaptureResultInfo(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public PhysicalCaptureResultInfo(String cameraId, CameraMetadataNative cameraMetadata) {
+ this.cameraId = cameraId;
+ this.cameraMetadata = cameraMetadata;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(cameraId);
+ cameraMetadata.writeToParcel(dest, flags);
+ }
+
+ public void readFromParcel(Parcel in) {
+ cameraId = in.readString();
+ cameraMetadata = new CameraMetadataNative();
+ cameraMetadata.readFromParcel(in);
+ }
+
+ public String getCameraId() {
+ return cameraId;
+ }
+
+ public CameraMetadataNative getCameraMetadata() {
+ return cameraMetadata;
+ }
+}
diff --git a/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index eccab750..bc7b1260 100644
--- a/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -26,6 +26,7 @@ import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.SubmitInfo;
import android.os.ConditionVariable;
@@ -249,7 +250,8 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
@Override
public void onResultReceived(final CameraMetadataNative result,
- final CaptureResultExtras resultExtras) {
+ final CaptureResultExtras resultExtras,
+ PhysicalCaptureResultInfo physicalResults[]) {
Object[] resultArray = new Object[] { result, resultExtras };
Message msg = getHandler().obtainMessage(RESULT_RECEIVED,
/*obj*/ resultArray);
@@ -320,7 +322,8 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
Object[] resultArray = (Object[]) msg.obj;
CameraMetadataNative result = (CameraMetadataNative) resultArray[0];
CaptureResultExtras resultExtras = (CaptureResultExtras) resultArray[1];
- mCallbacks.onResultReceived(result, resultExtras);
+ mCallbacks.onResultReceived(result, resultExtras,
+ new PhysicalCaptureResultInfo[0]);
break;
}
case PREPARED: {
diff --git a/android/hardware/camera2/legacy/LegacyCameraDevice.java b/android/hardware/camera2/legacy/LegacyCameraDevice.java
index cb59fd14..71a361bd 100644
--- a/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -23,6 +23,7 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.ArrayUtils;
@@ -253,7 +254,8 @@ public class LegacyCameraDevice implements AutoCloseable {
holder.getRequestId());
}
try {
- mDeviceCallbacks.onResultReceived(result, extras);
+ mDeviceCallbacks.onResultReceived(result, extras,
+ new PhysicalCaptureResultInfo[0]);
} catch (RemoteException e) {
throw new IllegalStateException(
"Received remote exception during onCameraError callback: ", e);
@@ -728,7 +730,7 @@ public class LegacyCameraDevice implements AutoCloseable {
LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
}
- static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
+ public static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
checkNotNull(surface);
try {
return nativeGetSurfaceId(surface);
diff --git a/android/hardware/camera2/legacy/LegacyExceptionUtils.java b/android/hardware/camera2/legacy/LegacyExceptionUtils.java
index 93d6001c..55130c8f 100644
--- a/android/hardware/camera2/legacy/LegacyExceptionUtils.java
+++ b/android/hardware/camera2/legacy/LegacyExceptionUtils.java
@@ -62,14 +62,14 @@ public class LegacyExceptionUtils {
* exceptions.</p>
*
* @param errorFlag error to throw as an exception.
- * @throws {@link BufferQueueAbandonedException} for -ENODEV.
+ * @throws {@link BufferQueueAbandonedException} for BAD_VALUE.
* @throws {@link UnsupportedOperationException} for an unknown negative error code.
* @return {@code errorFlag} if the value was non-negative, throws otherwise.
*/
public static int throwOnError(int errorFlag) throws BufferQueueAbandonedException {
if (errorFlag == NO_ERROR) {
return NO_ERROR;
- } else if (errorFlag == -ENODEV) {
+ } else if (errorFlag == BAD_VALUE) {
throw new BufferQueueAbandonedException();
}
diff --git a/android/hardware/camera2/params/OisSample.java b/android/hardware/camera2/params/OisSample.java
new file mode 100644
index 00000000..7ebaae35
--- /dev/null
+++ b/android/hardware/camera2/params/OisSample.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Immutable class to store an
+ * {@link CaptureResult#STATISTICS_OIS_SAMPLES optical image stabilization sample}.
+ */
+public final class OisSample {
+ /**
+ * Create a new {@link OisSample}.
+ *
+ * <p>{@link OisSample} contains the timestamp and the amount of shifts in x and y direction,
+ * in pixels, of the OIS sample.
+ *
+ * <p>A positive value for a shift in x direction is a shift from left to right in active array
+ * coordinate system. For example, if the optical center is {@code (1000, 500)} in active array
+ * coordinates, a shift of {@code (3, 0)} puts the new optical center at {@code (1003, 500)}.
+ * </p>
+ *
+ * <p>A positive value for a shift in y direction is a shift from top to bottom in active array
+ * coordinate system. For example, if the optical center is {@code (1000, 500)} in active array
+ * coordinates, a shift of {@code (0, 5)} puts the new optical center at {@code (1000, 505)}.
+ * </p>
+ *
+ * <p>xShift and yShift must be finite; NaN and infinity is not allowed.</p>
+ *
+ * @param timestamp timestamp of the OIS sample.
+ * @param xShift shift of the OIS sample in x direction.
+ * @param yShift shift of the OIS sample in y direction.
+ *
+ * @throws IllegalArgumentException if xShift or yShift is not finite
+ */
+ public OisSample(final long timestamp, final float xShift, final float yShift) {
+ mTimestampNs = timestamp;
+ mXShift = Preconditions.checkArgumentFinite(xShift, "xShift must be finite");
+ mYShift = Preconditions.checkArgumentFinite(yShift, "yShift must be finite");
+ }
+
+ /**
+ * Get the timestamp in nanoseconds.
+ *
+ *<p>The timestamps are in the same timebase as and comparable to
+ *{@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p>
+ *
+ * @return a long value (guaranteed to be finite)
+ */
+ public long getTimestamp() {
+ return mTimestampNs;
+ }
+
+ /**
+ * Get the shift in x direction.
+ *
+ * @return a floating point value (guaranteed to be finite)
+ */
+ public float getXshift() {
+ return mXShift;
+ }
+
+ /**
+ * Get the shift in y direction.
+ *
+ * @return a floating point value (guaranteed to be finite)
+ */
+ public float getYshift() {
+ return mYShift;
+ }
+
+ /**
+ * Check if this {@link OisSample} is equal to another {@link OisSample}.
+ *
+ * <p>Two samples are only equal if and only if each of the OIS information is equal.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ } else if (this == obj) {
+ return true;
+ } else if (obj instanceof OisSample) {
+ final OisSample other = (OisSample) obj;
+ return mTimestampNs == other.mTimestampNs
+ && mXShift == other.mXShift
+ && mYShift == other.mYShift;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int timestampHash = HashCodeHelpers.hashCode(mTimestampNs);
+ return HashCodeHelpers.hashCode(mXShift, mYShift, timestampHash);
+ }
+
+ /**
+ * Return the OisSample as a string representation.
+ *
+ * <p> {@code "OisSample{timestamp:%l, shift_x:%f, shift_y:%f}"} represents the OIS sample's
+ * timestamp, shift in x direction, and shift in y direction.</p>
+ *
+ * @return string representation of {@link OisSample}
+ */
+ @Override
+ public String toString() {
+ return String.format("OisSample{timestamp:%d, shift_x:%f, shift_y:%f}", mTimestampNs,
+ mXShift, mYShift);
+ }
+
+ private final long mTimestampNs;
+ private final float mXShift;
+ private final float mYShift;
+}
diff --git a/android/hardware/camera2/params/OutputConfiguration.java b/android/hardware/camera2/params/OutputConfiguration.java
index f47cd665..a040a09c 100644
--- a/android/hardware/camera2/params/OutputConfiguration.java
+++ b/android/hardware/camera2/params/OutputConfiguration.java
@@ -82,11 +82,9 @@ import java.util.List;
*
* </ul>
*
- * <p>Please note that surface sharing is currently only enabled for outputs that use the
- * {@link ImageFormat#PRIVATE} format. This includes surface sources like
- * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder},
- * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using
- * the aforementioned format.</p>
+ * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for
+ * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE}
+ * format may be used.</p>
*
* @see CameraDevice#createCaptureSessionByOutputConfigurations
*
@@ -368,7 +366,7 @@ public final class OutputConfiguration implements Parcelable {
* desirable for the camera application to request streams from individual physical cameras.
* This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
*
- * <p>The valid physical camera id can be queried by {@link
+ * <p>The valid physical camera ids can be queried by {@link
* android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
* </p>
*
@@ -576,7 +574,7 @@ public final class OutputConfiguration implements Parcelable {
*
* @see #enableSurfaceSharing
*/
- public static int getMaxSharedSurfaceCount() {
+ public int getMaxSharedSurfaceCount() {
return MAX_SURFACES_COUNT;
}
diff --git a/android/hardware/camera2/params/SessionConfiguration.java b/android/hardware/camera2/params/SessionConfiguration.java
index a79a6c17..7bdb4a2f 100644
--- a/android/hardware/camera2/params/SessionConfiguration.java
+++ b/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,10 +17,10 @@
package android.hardware.camera2.params;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.IntDef;
-import android.os.Handler;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -31,6 +31,7 @@ import android.hardware.camera2.params.OutputConfiguration;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -78,7 +79,7 @@ public final class SessionConfiguration {
private List<OutputConfiguration> mOutputConfigurations;
private CameraCaptureSession.StateCallback mStateCallback;
private int mSessionType;
- private Handler mHandler = null;
+ private Executor mExecutor = null;
private InputConfiguration mInputConfig = null;
private CaptureRequest mSessionParameters = null;
@@ -87,10 +88,9 @@ public final class SessionConfiguration {
*
* @param sessionType The session type.
* @param outputs A list of output configurations for the capture session.
+ * @param executor The executor which should be used to invoke the callback. In general it is
+ * recommended that camera operations are not done on the main (UI) thread.
* @param cb A state callback interface implementation.
- * @param handler The handler on which the callback will be invoked. If it is
- * set to null, the callback will be invoked on the current thread's
- * {@link android.os.Looper looper}.
*
* @see #SESSION_REGULAR
* @see #SESSION_HIGH_SPEED
@@ -101,11 +101,12 @@ public final class SessionConfiguration {
*/
public SessionConfiguration(@SessionMode int sessionType,
@NonNull List<OutputConfiguration> outputs,
- @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) {
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CameraCaptureSession.StateCallback cb) {
mSessionType = sessionType;
mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
mStateCallback = cb;
- mHandler = handler;
+ mExecutor = executor;
}
/**
@@ -136,14 +137,12 @@ public final class SessionConfiguration {
}
/**
- * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session.
+ * Retrieve the {@link java.util.concurrent.Executor} for the capture session.
*
- * @return The handler on which the callback will be invoked. If it is
- * set to null, the callback will be invoked on the current thread's
- * {@link android.os.Looper looper}.
+ * @return The Executor on which the callback will be invoked.
*/
- public Handler getHandler() {
- return mHandler;
+ public Executor getExecutor() {
+ return mExecutor;
}
/**
diff --git a/android/hardware/camera2/utils/SurfaceUtils.java b/android/hardware/camera2/utils/SurfaceUtils.java
index e1e1c4fd..92478441 100644
--- a/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/android/hardware/camera2/utils/SurfaceUtils.java
@@ -56,6 +56,20 @@ public class SurfaceUtils {
}
/**
+ * Get the native object id of a surface.
+ *
+ * @param surface The surface to be checked.
+ * @return the native object id of the surface, 0 if surface is not backed by a native object.
+ */
+ public static long getSurfaceId(Surface surface) {
+ try {
+ return LegacyCameraDevice.getSurfaceId(surface);
+ } catch (BufferQueueAbandonedException e) {
+ return 0;
+ }
+ }
+
+ /**
* Get the Surface size.
*
* @param surface The surface to be queried for size.
diff --git a/android/hardware/camera2/utils/TaskDrainer.java b/android/hardware/camera2/utils/TaskDrainer.java
index ed30ff34..e71f26a1 100644
--- a/android/hardware/camera2/utils/TaskDrainer.java
+++ b/android/hardware/camera2/utils/TaskDrainer.java
@@ -15,11 +15,11 @@
*/
package android.hardware.camera2.utils;
-import android.os.Handler;
import android.util.Log;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.Executor;
import static com.android.internal.util.Preconditions.*;
@@ -55,7 +55,7 @@ public class TaskDrainer<T> {
private static final String TAG = "TaskDrainer";
private final boolean DEBUG = false;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final DrainListener mListener;
private final String mName;
@@ -73,28 +73,27 @@ public class TaskDrainer<T> {
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
*/
- public TaskDrainer(Handler handler, DrainListener listener) {
- mHandler = checkNotNull(handler, "handler must not be null");
+ public TaskDrainer(Executor executor, DrainListener listener) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
mListener = checkNotNull(listener, "listener must not be null");
mName = null;
}
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
* @param name an optional name used for debug logging
*/
- public TaskDrainer(Handler handler, DrainListener listener, String name) {
- // XX: Probably don't need a handler at all here
- mHandler = checkNotNull(handler, "handler must not be null");
+ public TaskDrainer(Executor executor, DrainListener listener, String name) {
+ mExecutor = checkNotNull(executor, "executor must not be null");
mListener = checkNotNull(listener, "listener must not be null");
mName = name;
}
@@ -200,15 +199,12 @@ public class TaskDrainer<T> {
}
private void postDrained() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
+ mExecutor.execute(() -> {
if (DEBUG) {
Log.v(TAG + "[" + mName + "]", "onDrained");
}
mListener.onDrained();
- }
});
}
}
diff --git a/android/hardware/camera2/utils/TaskSingleDrainer.java b/android/hardware/camera2/utils/TaskSingleDrainer.java
index f6272c9e..9615450b 100644
--- a/android/hardware/camera2/utils/TaskSingleDrainer.java
+++ b/android/hardware/camera2/utils/TaskSingleDrainer.java
@@ -16,7 +16,8 @@
package android.hardware.camera2.utils;
import android.hardware.camera2.utils.TaskDrainer.DrainListener;
-import android.os.Handler;
+
+import java.util.concurrent.Executor;
/**
* Keep track of a single concurrent task starting and finishing;
@@ -38,25 +39,25 @@ public class TaskSingleDrainer {
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
*/
- public TaskSingleDrainer(Handler handler, DrainListener listener) {
- mTaskDrainer = new TaskDrainer<>(handler, listener);
+ public TaskSingleDrainer(Executor executor, DrainListener listener) {
+ mTaskDrainer = new TaskDrainer<>(executor, listener);
}
/**
* Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
- * via the {@code handler}.
+ * via the {@code executor}.
*
- * @param handler a non-{@code null} handler to use to post runnables to
+ * @param executor a non-{@code null} executor to use for listener execution
* @param listener a non-{@code null} listener where {@code onDrained} will be called
* @param name an optional name used for debug logging
*/
- public TaskSingleDrainer(Handler handler, DrainListener listener, String name) {
- mTaskDrainer = new TaskDrainer<>(handler, listener, name);
+ public TaskSingleDrainer(Executor executor, DrainListener listener, String name) {
+ mTaskDrainer = new TaskDrainer<>(executor, listener, name);
}
/**
diff --git a/android/hardware/display/AmbientBrightnessDayStats.java b/android/hardware/display/AmbientBrightnessDayStats.java
new file mode 100644
index 00000000..1aa2557f
--- /dev/null
+++ b/android/hardware/display/AmbientBrightnessDayStats.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+/**
+ * AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
+ * {@see DisplayManager.getAmbientBrightnessStats()}
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class AmbientBrightnessDayStats implements Parcelable {
+
+ /** The localdate for which brightness stats are being tracked */
+ private final LocalDate mLocalDate;
+
+ /** Ambient brightness values for creating bucket boundaries from */
+ private final float[] mBucketBoundaries;
+
+ /** Stats of how much time (in seconds) was spent in each of the buckets */
+ private final float[] mStats;
+
+ /**
+ * Initialize day stats from the given state. The time spent in each of the bucket is
+ * initialized to 0.
+ *
+ * @param localDate The date for which stats are being tracked
+ * @param bucketBoundaries Bucket boundaries used from creating the buckets from
+ * @hide
+ */
+ public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+ @NonNull float[] bucketBoundaries) {
+ this(localDate, bucketBoundaries, null);
+ }
+
+ /**
+ * Initialize day stats from the given state
+ *
+ * @param localDate The date for which stats are being tracked
+ * @param bucketBoundaries Bucket boundaries used from creating the buckets from
+ * @param stats Time spent in each of the buckets (in seconds)
+ * @hide
+ */
+ public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+ @NonNull float[] bucketBoundaries, float[] stats) {
+ Preconditions.checkNotNull(localDate);
+ Preconditions.checkNotNull(bucketBoundaries);
+ Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
+ "bucketBoundaries");
+ if (bucketBoundaries.length < 1) {
+ throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
+ }
+ checkSorted(bucketBoundaries);
+ if (stats == null) {
+ stats = new float[bucketBoundaries.length];
+ } else {
+ Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
+ if (bucketBoundaries.length != stats.length) {
+ throw new IllegalArgumentException(
+ "Bucket boundaries and stats must be of same size.");
+ }
+ }
+ mLocalDate = localDate;
+ mBucketBoundaries = bucketBoundaries;
+ mStats = stats;
+ }
+
+ /**
+ * @return The {@link LocalDate} for which brightness stats are being tracked.
+ */
+ public LocalDate getLocalDate() {
+ return mLocalDate;
+ }
+
+ /**
+ * @return Aggregated stats of time spent (in seconds) in various buckets.
+ */
+ public float[] getStats() {
+ return mStats;
+ }
+
+ /**
+ * Returns the bucket boundaries (in lux) used for creating buckets. For eg., if the bucket
+ * boundaries array is {b1, b2, b3}, the buckets will be [b1, b2), [b2, b3), [b3, inf).
+ *
+ * @return The list of bucket boundaries.
+ */
+ public float[] getBucketBoundaries() {
+ return mBucketBoundaries;
+ }
+
+ private AmbientBrightnessDayStats(Parcel source) {
+ mLocalDate = LocalDate.parse(source.readString());
+ mBucketBoundaries = source.createFloatArray();
+ mStats = source.createFloatArray();
+ }
+
+ public static final Creator<AmbientBrightnessDayStats> CREATOR =
+ new Creator<AmbientBrightnessDayStats>() {
+
+ @Override
+ public AmbientBrightnessDayStats createFromParcel(Parcel source) {
+ return new AmbientBrightnessDayStats(source);
+ }
+
+ @Override
+ public AmbientBrightnessDayStats[] newArray(int size) {
+ return new AmbientBrightnessDayStats[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj;
+ return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries,
+ other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = result * prime + mLocalDate.hashCode();
+ result = result * prime + Arrays.hashCode(mBucketBoundaries);
+ result = result * prime + Arrays.hashCode(mStats);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bucketBoundariesString = new StringBuilder();
+ StringBuilder statsString = new StringBuilder();
+ for (int i = 0; i < mBucketBoundaries.length; i++) {
+ if (i != 0) {
+ bucketBoundariesString.append(", ");
+ statsString.append(", ");
+ }
+ bucketBoundariesString.append(mBucketBoundaries[i]);
+ statsString.append(mStats[i]);
+ }
+ return new StringBuilder()
+ .append(mLocalDate).append(" ")
+ .append("{").append(bucketBoundariesString).append("} ")
+ .append("{").append(statsString).append("}").toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mLocalDate.toString());
+ dest.writeFloatArray(mBucketBoundaries);
+ dest.writeFloatArray(mStats);
+ }
+
+ /**
+ * Updates the stats by incrementing the time spent for the appropriate bucket based on ambient
+ * brightness reading.
+ *
+ * @param ambientBrightness Ambient brightness reading (in lux)
+ * @param durationSec Time spent with the given reading (in seconds)
+ * @hide
+ */
+ public void log(float ambientBrightness, float durationSec) {
+ int bucketIndex = getBucketIndex(ambientBrightness);
+ if (bucketIndex >= 0) {
+ mStats[bucketIndex] += durationSec;
+ }
+ }
+
+ private int getBucketIndex(float ambientBrightness) {
+ if (ambientBrightness < mBucketBoundaries[0]) {
+ return -1;
+ }
+ int low = 0;
+ int high = mBucketBoundaries.length - 1;
+ while (low < high) {
+ int mid = (low + high) / 2;
+ if (mBucketBoundaries[mid] <= ambientBrightness
+ && ambientBrightness < mBucketBoundaries[mid + 1]) {
+ return mid;
+ } else if (mBucketBoundaries[mid] < ambientBrightness) {
+ low = mid + 1;
+ } else if (mBucketBoundaries[mid] > ambientBrightness) {
+ high = mid - 1;
+ }
+ }
+ return low;
+ }
+
+ private static void checkSorted(float[] values) {
+ if (values.length <= 1) {
+ return;
+ }
+ float prevValue = values[0];
+ for (int i = 1; i < values.length; i++) {
+ Preconditions.checkState(prevValue < values[i]);
+ prevValue = values[i];
+ }
+ return;
+ }
+}
diff --git a/android/hardware/display/BrightnessChangeEvent.java b/android/hardware/display/BrightnessChangeEvent.java
index 2301824c..02eb28ce 100644
--- a/android/hardware/display/BrightnessChangeEvent.java
+++ b/android/hardware/display/BrightnessChangeEvent.java
@@ -54,19 +54,30 @@ public final class BrightnessChangeEvent implements Parcelable {
/** Most recent battery level when brightness was changed or Float.NaN */
public final float batteryLevel;
+ /** Factor applied to brightness due to battery level, 0.0-1.0 */
+ public final float powerBrightnessFactor;
+
/** Color filter active to provide night mode */
public final boolean nightMode;
/** If night mode color filter is active this will be the temperature in kelvin */
public final int colorTemperature;
- /** Brightness le vel before slider adjustment */
+ /** Brightness level before slider adjustment */
public final float lastBrightness;
+ /** Whether brightness configuration is default version */
+ public final boolean isDefaultBrightnessConfig;
+
+ /** Whether brightness curve includes a user brightness point */
+ public final boolean isUserSetBrightness;
+
+
/** @hide */
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
- boolean nightMode, int colorTemperature, float lastBrightness) {
+ float powerBrightnessFactor, boolean nightMode, int colorTemperature,
+ float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
@@ -74,9 +85,12 @@ public final class BrightnessChangeEvent implements Parcelable {
this.luxValues = luxValues;
this.luxTimestamps = luxTimestamps;
this.batteryLevel = batteryLevel;
+ this.powerBrightnessFactor = powerBrightnessFactor;
this.nightMode = nightMode;
this.colorTemperature = colorTemperature;
this.lastBrightness = lastBrightness;
+ this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ this.isUserSetBrightness = isUserSetBrightness;
}
/** @hide */
@@ -88,9 +102,12 @@ public final class BrightnessChangeEvent implements Parcelable {
this.luxValues = other.luxValues;
this.luxTimestamps = other.luxTimestamps;
this.batteryLevel = other.batteryLevel;
+ this.powerBrightnessFactor = other.powerBrightnessFactor;
this.nightMode = other.nightMode;
this.colorTemperature = other.colorTemperature;
this.lastBrightness = other.lastBrightness;
+ this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
+ this.isUserSetBrightness = other.isUserSetBrightness;
}
private BrightnessChangeEvent(Parcel source) {
@@ -101,9 +118,12 @@ public final class BrightnessChangeEvent implements Parcelable {
luxValues = source.createFloatArray();
luxTimestamps = source.createLongArray();
batteryLevel = source.readFloat();
+ powerBrightnessFactor = source.readFloat();
nightMode = source.readBoolean();
colorTemperature = source.readInt();
lastBrightness = source.readFloat();
+ isDefaultBrightnessConfig = source.readBoolean();
+ isUserSetBrightness = source.readBoolean();
}
public static final Creator<BrightnessChangeEvent> CREATOR =
@@ -130,9 +150,12 @@ public final class BrightnessChangeEvent implements Parcelable {
dest.writeFloatArray(luxValues);
dest.writeLongArray(luxTimestamps);
dest.writeFloat(batteryLevel);
+ dest.writeFloat(powerBrightnessFactor);
dest.writeBoolean(nightMode);
dest.writeInt(colorTemperature);
dest.writeFloat(lastBrightness);
+ dest.writeBoolean(isDefaultBrightnessConfig);
+ dest.writeBoolean(isUserSetBrightness);
}
/** @hide */
@@ -144,9 +167,12 @@ public final class BrightnessChangeEvent implements Parcelable {
private float[] mLuxValues;
private long[] mLuxTimestamps;
private float mBatteryLevel;
+ private float mPowerBrightnessFactor;
private boolean mNightMode;
private int mColorTemperature;
private float mLastBrightness;
+ private boolean mIsDefaultBrightnessConfig;
+ private boolean mIsUserSetBrightness;
/** {@see BrightnessChangeEvent#brightness} */
public Builder setBrightness(float brightness) {
@@ -190,6 +216,12 @@ public final class BrightnessChangeEvent implements Parcelable {
return this;
}
+ /** {@see BrightnessChangeEvent#powerSaveBrightness} */
+ public Builder setPowerBrightnessFactor(float powerBrightnessFactor) {
+ mPowerBrightnessFactor = powerBrightnessFactor;
+ return this;
+ }
+
/** {@see BrightnessChangeEvent#nightMode} */
public Builder setNightMode(boolean nightMode) {
mNightMode = nightMode;
@@ -208,11 +240,24 @@ public final class BrightnessChangeEvent implements Parcelable {
return this;
}
+ /** {@see BrightnessChangeEvent#isDefaultBrightnessConfig} */
+ public Builder setIsDefaultBrightnessConfig(boolean isDefaultBrightnessConfig) {
+ mIsDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#userBrightnessPoint} */
+ public Builder setUserBrightnessPoint(boolean isUserSetBrightness) {
+ mIsUserSetBrightness = isUserSetBrightness;
+ return this;
+ }
+
/** Builds a BrightnessChangeEvent */
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
- mNightMode, mColorTemperature, mLastBrightness);
+ mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
+ mIsDefaultBrightnessConfig, mIsUserSetBrightness);
}
}
}
diff --git a/android/hardware/display/DisplayManager.java b/android/hardware/display/DisplayManager.java
index 4de4880b..efb9517a 100644
--- a/android/hardware/display/DisplayManager.java
+++ b/android/hardware/display/DisplayManager.java
@@ -28,7 +28,6 @@ import android.content.Context;
import android.graphics.Point;
import android.media.projection.MediaProjection;
import android.os.Handler;
-import android.os.UserHandle;
import android.util.SparseArray;
import android.view.Display;
import android.view.Surface;
@@ -536,6 +535,19 @@ public final class DisplayManager {
}
/**
+ * Set the level of color saturation to apply to the display.
+ * @param level The amount of saturation to apply, between 0 and 1 inclusive.
+ * 0 produces a grayscale image, 1 is normal.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
+ public void setSaturationLevel(float level) {
+ mGlobal.setSaturationLevel(level);
+ }
+
+ /**
* Creates a virtual display.
*
* @see #createVirtualDisplay(String, int, int, int, Surface, int,
@@ -615,6 +627,7 @@ public final class DisplayManager {
* @hide
*/
@SystemApi
+ @TestApi
public Point getStableDisplaySize() {
return mGlobal.getStableDisplaySize();
}
@@ -631,6 +644,18 @@ public final class DisplayManager {
}
/**
+ * Fetch {@link AmbientBrightnessDayStats}s.
+ *
+ * @hide until we make it a system api
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
+ public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ return mGlobal.getAmbientBrightnessStats();
+ }
+
+ /**
* Sets the global display brightness configuration.
*
* @hide
@@ -639,7 +664,7 @@ public final class DisplayManager {
@TestApi
@RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
public void setBrightnessConfiguration(BrightnessConfiguration c) {
- setBrightnessConfigurationForUser(c, UserHandle.myUserId(), mContext.getPackageName());
+ setBrightnessConfigurationForUser(c, mContext.getUserId(), mContext.getPackageName());
}
/**
@@ -656,6 +681,45 @@ public final class DisplayManager {
}
/**
+ * Gets the global display brightness configuration or the default curve if one hasn't been set.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+ public BrightnessConfiguration getBrightnessConfiguration() {
+ return getBrightnessConfigurationForUser(mContext.getUserId());
+ }
+
+ /**
+ * Gets the global display brightness configuration or the default curve if one hasn't been set
+ * for a specific user.
+ *
+ * Note this requires the INTERACT_ACROSS_USERS permission if getting the configuration for a
+ * user other than the one you're currently running as.
+ *
+ * @hide
+ */
+ public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+ return mGlobal.getBrightnessConfigurationForUser(userId);
+ }
+
+ /**
+ * Gets the default global display brightness configuration or null one hasn't
+ * been configured.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+ @Nullable
+ public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+ return mGlobal.getDefaultBrightnessConfiguration();
+ }
+
+ /**
* Temporarily sets the brightness of the display.
* <p>
* Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
diff --git a/android/hardware/display/DisplayManagerGlobal.java b/android/hardware/display/DisplayManagerGlobal.java
index 2d5f5e04..2d0ef2f2 100644
--- a/android/hardware/display/DisplayManagerGlobal.java
+++ b/android/hardware/display/DisplayManagerGlobal.java
@@ -384,6 +384,17 @@ public final class DisplayManagerGlobal {
}
}
+ /**
+ * Set the level of color saturation to apply to the display.
+ */
+ public void setSaturationLevel(float level) {
+ try {
+ mDm.setSaturationLevel(level);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
String name, int width, int height, int densityDpi, Surface surface, int flags,
VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
@@ -490,6 +501,32 @@ public final class DisplayManagerGlobal {
}
/**
+ * Gets the global brightness configuration for a given user or null if one hasn't been set.
+ *
+ * @hide
+ */
+ public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+ try {
+ return mDm.getBrightnessConfigurationForUser(userId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the default brightness configuration or null if one hasn't been configured.
+ *
+ * @hide
+ */
+ public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+ try {
+ return mDm.getDefaultBrightnessConfiguration();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Temporarily sets the brightness of the display.
* <p>
* Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
@@ -525,6 +562,21 @@ public final class DisplayManagerGlobal {
}
}
+ /**
+ * Retrieves ambient brightness stats.
+ */
+ public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ try {
+ ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
+ if (stats == null) {
+ return Collections.emptyList();
+ }
+ return stats.getList();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/android/hardware/display/DisplayManagerInternal.java b/android/hardware/display/DisplayManagerInternal.java
index 1cfad4f0..504f840a 100644
--- a/android/hardware/display/DisplayManagerInternal.java
+++ b/android/hardware/display/DisplayManagerInternal.java
@@ -23,6 +23,7 @@ import android.util.IntArray;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.SurfaceControl;
/**
* Display manager local system service interface.
@@ -115,7 +116,7 @@ public abstract class DisplayManagerInternal {
* Called by the window manager to perform traversals while holding a
* surface flinger transaction.
*/
- public abstract void performTraversalInTransactionFromWindowManager();
+ public abstract void performTraversal(SurfaceControl.Transaction t);
/**
* Tells the display manager about properties of the display that depend on the windows on it.
@@ -174,9 +175,9 @@ public abstract class DisplayManagerInternal {
public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
/**
- * Persist brightness slider events.
+ * Persist brightness slider events and ambient brightness stats.
*/
- public abstract void persistBrightnessSliderEvents();
+ public abstract void persistBrightnessTrackerState();
/**
* Notifies the display manager that resource overlays have changed.
diff --git a/android/hardware/display/WifiDisplay.java b/android/hardware/display/WifiDisplay.java
index af5a84e6..bb32c199 100644
--- a/android/hardware/display/WifiDisplay.java
+++ b/android/hardware/display/WifiDisplay.java
@@ -19,7 +19,7 @@ package android.hardware.display;
import android.os.Parcel;
import android.os.Parcelable;
-import libcore.util.Objects;
+import java.util.Objects;
/**
* Describes the properties of a Wifi display.
@@ -140,7 +140,7 @@ public final class WifiDisplay implements Parcelable {
return other != null
&& mDeviceAddress.equals(other.mDeviceAddress)
&& mDeviceName.equals(other.mDeviceName)
- && Objects.equal(mDeviceAlias, other.mDeviceAlias);
+ && Objects.equals(mDeviceAlias, other.mDeviceAlias);
}
/**
diff --git a/android/hardware/fingerprint/Fingerprint.java b/android/hardware/fingerprint/Fingerprint.java
index c3076347..c7ce8fad 100644
--- a/android/hardware/fingerprint/Fingerprint.java
+++ b/android/hardware/fingerprint/Fingerprint.java
@@ -15,6 +15,7 @@
*/
package android.hardware.fingerprint;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -22,7 +23,7 @@ import android.os.Parcelable;
* Container for fingerprint metadata.
* @hide
*/
-public final class Fingerprint implements Parcelable {
+public final class Fingerprint extends BiometricAuthenticator.BiometricIdentifier {
private CharSequence mName;
private int mGroupId;
private int mFingerId;
diff --git a/android/hardware/fingerprint/FingerprintDialog.java b/android/hardware/fingerprint/FingerprintDialog.java
deleted file mode 100644
index 6b7fab77..00000000
--- a/android/hardware/fingerprint/FingerprintDialog.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.fingerprint;
-
-import static android.Manifest.permission.USE_FINGERPRINT;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.text.TextUtils;
-
-import java.util.concurrent.Executor;
-
-/**
- * A class that manages a system-provided fingerprint dialog.
- */
-public class FingerprintDialog {
-
- /**
- * @hide
- */
- public static final String KEY_TITLE = "title";
- /**
- * @hide
- */
- public static final String KEY_SUBTITLE = "subtitle";
- /**
- * @hide
- */
- public static final String KEY_DESCRIPTION = "description";
- /**
- * @hide
- */
- public static final String KEY_POSITIVE_TEXT = "positive_text";
- /**
- * @hide
- */
- public static final String KEY_NEGATIVE_TEXT = "negative_text";
-
- /**
- * Error/help message will show for this amount of time.
- * For error messages, the dialog will also be dismissed after this amount of time.
- * Error messages will be propagated back to the application via AuthenticationCallback
- * after this amount of time.
- * @hide
- */
- public static final int HIDE_DIALOG_DELAY = 3000; // ms
- /**
- * @hide
- */
- public static final int DISMISSED_REASON_POSITIVE = 1;
-
- /**
- * @hide
- */
- public static final int DISMISSED_REASON_NEGATIVE = 2;
-
- /**
- * @hide
- */
- public static final int DISMISSED_REASON_USER_CANCEL = 3;
-
- private static class ButtonInfo {
- Executor executor;
- DialogInterface.OnClickListener listener;
- ButtonInfo(Executor ex, DialogInterface.OnClickListener l) {
- executor = ex;
- listener = l;
- }
- }
-
- /**
- * A builder that collects arguments, to be shown on the system-provided fingerprint dialog.
- **/
- public static class Builder {
- private final Bundle bundle;
- private ButtonInfo positiveButtonInfo;
- private ButtonInfo negativeButtonInfo;
-
- /**
- * Creates a builder for a fingerprint dialog.
- */
- public Builder() {
- bundle = new Bundle();
- }
-
- /**
- * Required: Set the title to display.
- * @param title
- * @return
- */
- public Builder setTitle(@NonNull CharSequence title) {
- bundle.putCharSequence(KEY_TITLE, title);
- return this;
- }
-
- /**
- * Optional: Set the subtitle to display.
- * @param subtitle
- * @return
- */
- public Builder setSubtitle(@NonNull CharSequence subtitle) {
- bundle.putCharSequence(KEY_SUBTITLE, subtitle);
- return this;
- }
-
- /**
- * Optional: Set the description to display.
- * @param description
- * @return
- */
- public Builder setDescription(@NonNull CharSequence description) {
- bundle.putCharSequence(KEY_DESCRIPTION, description);
- return this;
- }
-
- /**
- * Optional: Set the text for the positive button. If not set, the positive button
- * will not show.
- * @param text
- * @return
- * @hide
- */
- public Builder setPositiveButton(@NonNull CharSequence text,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull DialogInterface.OnClickListener listener) {
- if (TextUtils.isEmpty(text)) {
- throw new IllegalArgumentException("Text must be set and non-empty");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Executor must not be null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("Listener must not be null");
- }
- bundle.putCharSequence(KEY_POSITIVE_TEXT, text);
- positiveButtonInfo = new ButtonInfo(executor, listener);
- return this;
- }
-
- /**
- * Required: Set the text for the negative button.
- * @param text
- * @return
- */
- public Builder setNegativeButton(@NonNull CharSequence text,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull DialogInterface.OnClickListener listener) {
- if (TextUtils.isEmpty(text)) {
- throw new IllegalArgumentException("Text must be set and non-empty");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Executor must not be null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("Listener must not be null");
- }
- bundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
- negativeButtonInfo = new ButtonInfo(executor, listener);
- return this;
- }
-
- /**
- * Creates a {@link FingerprintDialog} with the arguments supplied to this builder.
- * @param context
- * @return a {@link FingerprintDialog}
- * @throws IllegalArgumentException if any of the required fields are not set.
- */
- public FingerprintDialog build(Context context) {
- final CharSequence title = bundle.getCharSequence(KEY_TITLE);
- final CharSequence negative = bundle.getCharSequence(KEY_NEGATIVE_TEXT);
-
- if (TextUtils.isEmpty(title)) {
- throw new IllegalArgumentException("Title must be set and non-empty");
- } else if (TextUtils.isEmpty(negative)) {
- throw new IllegalArgumentException("Negative text must be set and non-empty");
- }
- return new FingerprintDialog(context, bundle, positiveButtonInfo, negativeButtonInfo);
- }
- }
-
- private FingerprintManager mFingerprintManager;
- private Bundle mBundle;
- private ButtonInfo mPositiveButtonInfo;
- private ButtonInfo mNegativeButtonInfo;
-
- IFingerprintDialogReceiver mDialogReceiver = new IFingerprintDialogReceiver.Stub() {
- @Override
- public void onDialogDismissed(int reason) {
- // Check the reason and invoke OnClickListener(s) if necessary
- if (reason == DISMISSED_REASON_POSITIVE) {
- mPositiveButtonInfo.executor.execute(() -> {
- mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
- });
- } else if (reason == DISMISSED_REASON_NEGATIVE) {
- mNegativeButtonInfo.executor.execute(() -> {
- mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
- });
- }
- }
- };
-
- private FingerprintDialog(Context context, Bundle bundle,
- ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
- mBundle = bundle;
- mPositiveButtonInfo = positiveButtonInfo;
- mNegativeButtonInfo = negativeButtonInfo;
- mFingerprintManager = context.getSystemService(FingerprintManager.class);
- }
-
- /**
- * This call warms up the fingerprint hardware, displays a system-provided dialog,
- * and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
- * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
- * dismisses the system-provided dialog, at which point the crypto object becomes invalid.
- * This operation can be canceled by using the provided cancel object. The application will
- * receive authentication errors through {@link AuthenticationCallback}, and button events
- * through the corresponding callback set in
- * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
- * It is safe to reuse the {@link FingerprintDialog} object, and calling
- * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
- * while an existing authentication attempt is occurring will stop the previous client and
- * start a new authentication. The interrupted client will receive a cancelled notification
- * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
- *
- * @throws IllegalArgumentException if any of the arguments are null
- *
- * @param crypto object associated with the call
- * @param cancel an object that can be used to cancel authentication
- * @param executor an executor to handle callback events
- * @param callback an object to receive authentication events
- */
- @RequiresPermission(USE_FINGERPRINT)
- public void authenticate(@NonNull FingerprintManager.CryptoObject crypto,
- @NonNull CancellationSignal cancel,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull FingerprintManager.AuthenticationCallback callback) {
- mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
- callback);
- }
-
- /**
- * This call warms up the fingerprint hardware, displays a system-provided dialog,
- * and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
- * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
- * dismisses the system-provided dialog. This operation can be canceled by using the provided
- * cancel object. The application will receive authentication errors through
- * {@link AuthenticationCallback}, and button events through the corresponding callback set in
- * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
- * It is safe to reuse the {@link FingerprintDialog} object, and calling
- * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
- * while an existing authentication attempt is occurring will stop the previous client and
- * start a new authentication. The interrupted client will receive a cancelled notification
- * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
- *
- * @throws IllegalArgumentException if any of the arguments are null
- *
- * @param cancel an object that can be used to cancel authentication
- * @param executor an executor to handle callback events
- * @param callback an object to receive authentication events
- */
- @RequiresPermission(USE_FINGERPRINT)
- public void authenticate(@NonNull CancellationSignal cancel,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull FingerprintManager.AuthenticationCallback callback) {
- mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
- }
-}
diff --git a/android/hardware/fingerprint/FingerprintManager.java b/android/hardware/fingerprint/FingerprintManager.java
index 62d92c4a..a6c8c67d 100644
--- a/android/hardware/fingerprint/FingerprintManager.java
+++ b/android/hardware/fingerprint/FingerprintManager.java
@@ -18,15 +18,22 @@ package android.hardware.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_FINGERPRINT;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricDialog;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.IBiometricDialogReceiver;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -38,7 +45,6 @@ import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.util.Log;
import android.util.Slog;
@@ -51,9 +57,15 @@ import javax.crypto.Mac;
/**
* A class that coordinates access to the fingerprint hardware.
+ * @deprecated See {@link BiometricDialog} which shows a system-provided dialog upon starting
+ * authentication. In a world where devices may have different types of biometric authentication,
+ * it's much more realistic to have a system-provided authentication dialog since the method may
+ * vary by vendor/device.
*/
+@Deprecated
@SystemService(Context.FINGERPRINT_SERVICE)
-public class FingerprintManager {
+@RequiresFeature(PackageManager.FEATURE_FINGERPRINT)
+public class FingerprintManager implements BiometricFingerprintConstants {
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
@@ -64,147 +76,14 @@ public class FingerprintManager {
private static final int MSG_REMOVED = 105;
private static final int MSG_ENUMERATED = 106;
- //
- // Error messages from fingerprint hardware during initilization, enrollment, authentication or
- // removal. Must agree with the list in fingerprint.h
- //
-
- /**
- * The hardware is unavailable. Try again later.
- */
- public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
-
- /**
- * Error state returned when the sensor was unable to process the current image.
- */
- public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
-
- /**
- * Error state returned when the current request has been running too long. This is intended to
- * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
- * platform and sensor-specific, but is generally on the order of 30 seconds.
- */
- public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
-
- /**
- * Error state returned for operations like enrollment; the operation cannot be completed
- * because there's not enough storage remaining to complete the operation.
- */
- public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
-
- /**
- * The operation was canceled because the fingerprint sensor is unavailable. For example,
- * this may happen when the user is switched, the device is locked or another pending operation
- * prevents or disables it.
- */
- public static final int FINGERPRINT_ERROR_CANCELED = 5;
-
- /**
- * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
- * provided fingerprint id was incorrect.
- *
- * @hide
- */
- public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
-
- /**
- * The operation was canceled because the API is locked out due to too many attempts.
- * This occurs after 5 failed attempts, and lasts for 30 seconds.
- */
- public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
-
- /**
- * Hardware vendors may extend this list if there are conditions that do not fall under one of
- * the above categories. Vendors are responsible for providing error strings for these errors.
- * These messages are typically reserved for internal operations such as enrollment, but may be
- * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
- * expected to show the error message string if they happen, but are advised not to rely on the
- * message id since they will be device and vendor-specific
- */
- public static final int FINGERPRINT_ERROR_VENDOR = 8;
-
- /**
- * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
- * Fingerprint authentication is disabled until the user unlocks with strong authentication
- * (PIN/Pattern/Password)
- */
- public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
-
- /**
- * The user canceled the operation. Upon receiving this, applications should use alternate
- * authentication (e.g. a password). The application should also provide the means to return
- * to fingerprint authentication, such as a "use fingerprint" button.
- */
- public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
-
- /**
- * @hide
- */
- public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
-
- //
- // Image acquisition messages. Must agree with those in fingerprint.h
- //
-
- /**
- * The image acquired was good.
- */
- public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
-
- /**
- * Only a partial fingerprint image was detected. During enrollment, the user should be
- * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
- */
- public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
-
- /**
- * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
- * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
- */
- public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
-
- /**
- * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
- * For example, it's reasonable return this after multiple
- * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
- * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
- * when this is returned.
- */
- public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
-
- /**
- * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
- * linear array sensors that require a swipe motion.
- */
- public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
-
- /**
- * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
- * linear array sensors, this could also happen if the finger was moved during acquisition.
- * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
- * longer.
- */
- public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
-
- /**
- * Hardware vendors may extend this list if there are conditions that do not fall under one of
- * the above categories. Vendors are responsible for providing error strings for these errors.
- * @hide
- */
- public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
- /**
- * @hide
- */
- public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
-
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
- private AuthenticationCallback mAuthenticationCallback;
+ private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private EnumerateCallback mEnumerateCallback;
- private CryptoObject mCryptoObject;
+ private android.hardware.biometrics.CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
private Executor mExecutor;
@@ -217,9 +96,9 @@ public class FingerprintManager {
}
private class OnAuthenticationCancelListener implements OnCancelListener {
- private CryptoObject mCrypto;
+ private android.hardware.biometrics.CryptoObject mCrypto;
- public OnAuthenticationCancelListener(CryptoObject crypto) {
+ public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) {
mCrypto = crypto;
}
@@ -232,19 +111,20 @@ public class FingerprintManager {
/**
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * @deprecated See {@link android.hardware.biometrics.BiometricDialog.CryptoObject}
*/
- public static final class CryptoObject {
-
+ @Deprecated
+ public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
public CryptoObject(@NonNull Signature signature) {
- mCrypto = signature;
+ super(signature);
}
public CryptoObject(@NonNull Cipher cipher) {
- mCrypto = cipher;
+ super(cipher);
}
public CryptoObject(@NonNull Mac mac) {
- mCrypto = mac;
+ super(mac);
}
/**
@@ -252,7 +132,7 @@ public class FingerprintManager {
* @return {@link Signature} object or null if this doesn't contain one.
*/
public Signature getSignature() {
- return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ return super.getSignature();
}
/**
@@ -260,7 +140,7 @@ public class FingerprintManager {
* @return {@link Cipher} object or null if this doesn't contain one.
*/
public Cipher getCipher() {
- return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ return super.getCipher();
}
/**
@@ -268,25 +148,16 @@ public class FingerprintManager {
* @return {@link Mac} object or null if this doesn't contain one.
*/
public Mac getMac() {
- return mCrypto instanceof Mac ? (Mac) mCrypto : null;
- }
-
- /**
- * @hide
- * @return the opId associated with this object or 0 if none
- */
- public long getOpId() {
- return mCrypto != null ?
- AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+ return super.getMac();
}
-
- private final Object mCrypto;
- };
+ }
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
* CancellationSignal, int, AuthenticationCallback, Handler)}.
+ * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationResult}
*/
+ @Deprecated
public static class AuthenticationResult {
private Fingerprint mFingerprint;
private CryptoObject mCryptoObject;
@@ -333,14 +204,18 @@ public class FingerprintManager {
* FingerprintManager#authenticate(CryptoObject, CancellationSignal,
* int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
* fingerprint events.
+ * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationCallback}
*/
- public static abstract class AuthenticationCallback {
+ @Deprecated
+ public static abstract class AuthenticationCallback
+ extends BiometricAuthenticator.AuthenticationCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
* @param errorCode An integer identifying the error message
* @param errString A human-readable error string that can be shown in UI
*/
+ @Override
public void onAuthenticationError(int errorCode, CharSequence errString) { }
/**
@@ -350,6 +225,7 @@ public class FingerprintManager {
* @param helpCode An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
+ @Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
/**
@@ -361,6 +237,7 @@ public class FingerprintManager {
/**
* Called when a fingerprint is valid but not recognized.
*/
+ @Override
public void onAuthenticationFailed() { }
/**
@@ -369,7 +246,19 @@ public class FingerprintManager {
* @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants
* @hide
*/
+ @Override
public void onAuthenticationAcquired(int acquireInfo) {}
+
+ /**
+ * @hide
+ * @param result
+ */
+ @Override
+ public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+ onAuthenticationSucceeded(new AuthenticationResult(
+ (CryptoObject) result.getCryptoObject(),
+ (Fingerprint) result.getId(), result.getUserId()));
+ }
};
/**
@@ -489,11 +378,16 @@ public class FingerprintManager {
* by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
* facility</a>.
* @throws IllegalStateException if the crypto primitive is not initialized.
+ * @deprecated See {@link BiometricDialog#authenticate(CancellationSignal, Executor,
+ * BiometricDialog.AuthenticationCallback)} and {@link BiometricDialog#authenticate(
+ * BiometricDialog.CryptoObject, CancellationSignal, Executor,
+ * BiometricDialog.AuthenticationCallback)}
*/
- @RequiresPermission(USE_FINGERPRINT)
+ @Deprecated
+ @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
- authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
+ authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
}
/**
@@ -514,7 +408,7 @@ public class FingerprintManager {
* @param userId the user ID that the fingerprint hardware will authenticate for.
* @hide
*/
- @RequiresPermission(USE_FINGERPRINT)
+ @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
if (callback == null) {
@@ -550,16 +444,16 @@ public class FingerprintManager {
/**
* Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
- * CancellationSignal, Bundle, Executor, IFingerprintDialogReceiver, AuthenticationCallback)}
+ * CancellationSignal, Bundle, Executor, IBiometricDialogReceiver, AuthenticationCallback)}
* @param userId the user ID that the fingerprint hardware will authenticate for.
*/
private void authenticate(int userId,
- @Nullable CryptoObject crypto,
+ @Nullable android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
- @NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull IBiometricDialogReceiver receiver,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
mCryptoObject = crypto;
if (cancel.isCanceled()) {
Log.w(TAG, "authentication already canceled");
@@ -586,8 +480,8 @@ public class FingerprintManager {
}
/**
- * Private method, see {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
- * AuthenticationCallback)}
+ * Private method, see {@link BiometricDialog#authenticate(CancellationSignal, Executor,
+ * BiometricDialog.AuthenticationCallback)}
* @param cancel
* @param executor
* @param callback
@@ -597,8 +491,8 @@ public class FingerprintManager {
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
- @NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull IBiometricDialogReceiver receiver,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
@@ -614,24 +508,24 @@ public class FingerprintManager {
if (callback == null) {
throw new IllegalArgumentException("Must supply a calback");
}
- authenticate(UserHandle.myUserId(), null, cancel, bundle, executor, receiver, callback);
+ authenticate(mContext.getUserId(), null, cancel, bundle, executor, receiver, callback);
}
/**
- * Private method, see {@link FingerprintDialog#authenticate(CryptoObject, CancellationSignal,
- * Executor, AuthenticationCallback)}
+ * Private method, see {@link BiometricDialog#authenticate(BiometricDialog.CryptoObject,
+ * CancellationSignal, Executor, BiometricDialog.AuthenticationCallback)}
* @param crypto
* @param cancel
* @param executor
* @param callback
* @hide
*/
- public void authenticate(@NonNull CryptoObject crypto,
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
- @NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull IBiometricDialogReceiver receiver,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (crypto == null) {
throw new IllegalArgumentException("Must supply a crypto object");
}
@@ -648,9 +542,10 @@ public class FingerprintManager {
throw new IllegalArgumentException("Must supply a receiver");
}
if (callback == null) {
- throw new IllegalArgumentException("Must supply a calback");
+ throw new IllegalArgumentException("Must supply a callback");
}
- authenticate(UserHandle.myUserId(), crypto, cancel, bundle, executor, receiver, callback);
+ authenticate(mContext.getUserId(), crypto, cancel,
+ bundle, executor, receiver, callback);
}
/**
@@ -841,19 +736,22 @@ public class FingerprintManager {
*/
@RequiresPermission(USE_FINGERPRINT)
public List<Fingerprint> getEnrolledFingerprints() {
- return getEnrolledFingerprints(UserHandle.myUserId());
+ return getEnrolledFingerprints(mContext.getUserId());
}
/**
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
+ * @deprecated See {@link BiometricDialog} and
+ * {@link FingerprintManager#FINGERPRINT_ERROR_NO_FINGERPRINTS}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean hasEnrolledFingerprints() {
if (mService != null) try {
return mService.hasEnrolledFingerprints(
- UserHandle.myUserId(), mContext.getOpPackageName());
+ mContext.getUserId(), mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -879,7 +777,10 @@ public class FingerprintManager {
* Determine if fingerprint hardware is present and functional.
*
* @return true if hardware is present and functional, false otherwise.
+ * @deprecated See {@link BiometricDialog} and
+ * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean isHardwareDetected() {
if (mService != null) {
@@ -1049,8 +950,8 @@ public class FingerprintManager {
private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
- final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, fp, userId);
+ final BiometricAuthenticator.AuthenticationResult result =
+ new BiometricAuthenticator.AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -1126,7 +1027,7 @@ public class FingerprintManager {
}
}
- private void cancelAuthentication(CryptoObject cryptoObject) {
+ private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) {
if (mService != null) try {
mService.cancelAuthentication(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1160,6 +1061,12 @@ public class FingerprintManager {
case FINGERPRINT_ERROR_USER_CANCELED:
return mContext.getString(
com.android.internal.R.string.fingerprint_error_user_canceled);
+ case FINGERPRINT_ERROR_NO_FINGERPRINTS:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_no_fingerprints);
+ case FINGERPRINT_ERROR_HW_NOT_PRESENT:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_hw_not_present);
case FINGERPRINT_ERROR_VENDOR: {
String[] msgArray = mContext.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
@@ -1251,9 +1158,22 @@ public class FingerprintManager {
@Override // binder call
public void onError(long deviceId, int error, int vendorCode) {
if (mExecutor != null) {
- mExecutor.execute(() -> {
- sendErrorResult(deviceId, error, vendorCode);
- });
+ // BiometricDialog case
+ if (error == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED) {
+ // User tapped somewhere to cancel, the biometric dialog is already dismissed.
+ mExecutor.execute(() -> {
+ sendErrorResult(deviceId, error, vendorCode);
+ });
+ } else {
+ // User got an error that needs to be displayed on the dialog, post a delayed
+ // runnable on the FingerprintManager handler that sends the error message after
+ // FingerprintDialog.HIDE_DIALOG_DELAY to send the error to the application.
+ mHandler.postDelayed(() -> {
+ mExecutor.execute(() -> {
+ sendErrorResult(deviceId, error, vendorCode);
+ });
+ }, BiometricDialog.HIDE_DIALOG_DELAY);
+ }
} else {
mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
}
diff --git a/android/hardware/hdmi/HdmiControlManager.java b/android/hardware/hdmi/HdmiControlManager.java
index a772cbe4..e34423c0 100644
--- a/android/hardware/hdmi/HdmiControlManager.java
+++ b/android/hardware/hdmi/HdmiControlManager.java
@@ -17,11 +17,13 @@
package android.hardware.hdmi;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.os.RemoteException;
@@ -42,6 +44,7 @@ import android.util.Log;
*/
@SystemApi
@SystemService(Context.HDMI_CONTROL_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_HDMI_CEC)
public final class HdmiControlManager {
private static final String TAG = "HdmiControlManager";
diff --git a/android/hardware/input/InputManager.java b/android/hardware/input/InputManager.java
index 1de8882e..6ae7a146 100644
--- a/android/hardware/input/InputManager.java
+++ b/android/hardware/input/InputManager.java
@@ -17,7 +17,6 @@
package android.hardware.input;
import android.annotation.IntDef;
-import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
@@ -43,8 +42,6 @@ import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.os.SomeArgs;
@@ -703,52 +700,6 @@ public final class InputManager {
}
}
-
- /**
- * Gets the keyboard layout for the specified input device and IME subtype.
- *
- * @param identifier The identifier for the input device.
- * @param inputMethodInfo The input method.
- * @param inputMethodSubtype The input method subtype. {@code null} if this input method does
- * not support any subtype.
- *
- * @return The associated {@link KeyboardLayout}, or null if one has not been set.
- *
- * @hide
- */
- @Nullable
- public KeyboardLayout getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype) {
- try {
- return mIm.getKeyboardLayoutForInputDevice(
- identifier, inputMethodInfo, inputMethodSubtype);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- }
-
- /**
- * Sets the keyboard layout for the specified input device and IME subtype pair.
- *
- * @param identifier The identifier for the input device.
- * @param inputMethodInfo The input method with which to associate the keyboard layout.
- * @param inputMethodSubtype The input method subtype which which to associate the keyboard
- * layout. {@code null} if this input method does not support any subtype.
- * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to set
- *
- * @hide
- */
- public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype,
- String keyboardLayoutDescriptor) {
- try {
- mIm.setKeyboardLayoutForInputDevice(identifier, inputMethodInfo,
- inputMethodSubtype, keyboardLayoutDescriptor);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- }
-
/**
* Gets the TouchCalibration applied to the specified input device's coordinates.
*
@@ -1246,7 +1197,7 @@ public final class InputManager {
int repeat;
if (effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
- pattern = new long[] { 0, oneShot.getTiming() };
+ pattern = new long[] { 0, oneShot.getDuration() };
repeat = -1;
} else if (effect instanceof VibrationEffect.Waveform) {
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
diff --git a/android/hardware/input/InputManagerInternal.java b/android/hardware/input/InputManagerInternal.java
index 4ea0f552..eb7ea67e 100644
--- a/android/hardware/input/InputManagerInternal.java
+++ b/android/hardware/input/InputManagerInternal.java
@@ -16,11 +16,8 @@
package android.hardware.input;
-import android.annotation.Nullable;
import android.hardware.display.DisplayViewport;
import android.view.InputEvent;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
import java.util.List;
@@ -46,16 +43,6 @@ public abstract class InputManagerInternal {
public abstract void setInteractive(boolean interactive);
/**
- * Notifies that InputMethodManagerService switched the current input method subtype.
- *
- * @param userId user id that indicates who is using the specified input method and subtype.
- * @param inputMethodInfo {@code null} when no input method is selected.
- * @param subtype {@code null} when {@code inputMethodInfo} does has no subtype.
- */
- public abstract void onInputMethodSubtypeChanged(int userId,
- @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype);
-
- /**
* Toggles Caps Lock state for input device with specific id.
*
* @param deviceId The id of input device.
diff --git a/android/hardware/input/TouchCalibration.java b/android/hardware/input/TouchCalibration.java
index 15503ed0..025fad04 100644
--- a/android/hardware/input/TouchCalibration.java
+++ b/android/hardware/input/TouchCalibration.java
@@ -123,10 +123,4 @@ public class TouchCalibration implements Parcelable {
Float.floatToIntBits(mYScale) ^
Float.floatToIntBits(mYOffset);
}
-
- @Override
- public String toString() {
- return String.format("[%f, %f, %f, %f, %f, %f]",
- mXScale, mXYMix, mXOffset, mYXMix, mYScale, mYOffset);
- }
}
diff --git a/android/hardware/location/ContextHubMessage.java b/android/hardware/location/ContextHubMessage.java
index f85ce3ee..e1c69d71 100644
--- a/android/hardware/location/ContextHubMessage.java
+++ b/android/hardware/location/ContextHubMessage.java
@@ -33,7 +33,7 @@ import java.util.Arrays;
*/
@SystemApi
@Deprecated
-public class ContextHubMessage {
+public class ContextHubMessage implements Parcelable {
private static final int DEBUG_LOG_NUM_BYTES = 16;
private int mType;
private int mVersion;
diff --git a/android/hardware/location/NanoApp.java b/android/hardware/location/NanoApp.java
index b5c01ec2..ded1bb8c 100644
--- a/android/hardware/location/NanoApp.java
+++ b/android/hardware/location/NanoApp.java
@@ -36,7 +36,7 @@ import android.util.Log;
*/
@SystemApi
@Deprecated
-public class NanoApp {
+public class NanoApp implements Parcelable {
private final String TAG = "NanoApp";
private final String UNKNOWN = "Unknown";
diff --git a/android/hardware/location/NanoAppFilter.java b/android/hardware/location/NanoAppFilter.java
index 75a96ee8..4d8e7344 100644
--- a/android/hardware/location/NanoAppFilter.java
+++ b/android/hardware/location/NanoAppFilter.java
@@ -28,7 +28,7 @@ import android.os.Parcelable;
*/
@SystemApi
@Deprecated
-public class NanoAppFilter {
+public class NanoAppFilter implements Parcelable {
private static final String TAG = "NanoAppFilter";
diff --git a/android/hardware/location/NanoAppInstanceInfo.java b/android/hardware/location/NanoAppInstanceInfo.java
index f1926eaa..75fb9157 100644
--- a/android/hardware/location/NanoAppInstanceInfo.java
+++ b/android/hardware/location/NanoAppInstanceInfo.java
@@ -34,7 +34,7 @@ import libcore.util.EmptyArray;
*/
@SystemApi
@Deprecated
-public class NanoAppInstanceInfo {
+public class NanoAppInstanceInfo implements Parcelable {
private String mPublisher = "Unknown";
private String mName = "Unknown";
diff --git a/android/hardware/radio/ProgramList.java b/android/hardware/radio/ProgramList.java
index b2aa9ba5..e6f523c0 100644
--- a/android/hardware/radio/ProgramList.java
+++ b/android/hardware/radio/ProgramList.java
@@ -263,6 +263,17 @@ public final class ProgramList implements AutoCloseable {
/**
* @hide for framework use only
*/
+ public Filter() {
+ mIdentifierTypes = Collections.emptySet();
+ mIdentifiers = Collections.emptySet();
+ mIncludeCategories = false;
+ mExcludeModifications = false;
+ mVendorFilter = null;
+ }
+
+ /**
+ * @hide for framework use only
+ */
public Filter(@Nullable Map<String, String> vendorFilter) {
mIdentifierTypes = Collections.emptySet();
mIdentifiers = Collections.emptySet();
diff --git a/android/hardware/radio/ProgramSelector.java b/android/hardware/radio/ProgramSelector.java
index 0294a29b..2a878ebb 100644
--- a/android/hardware/radio/ProgramSelector.java
+++ b/android/hardware/radio/ProgramSelector.java
@@ -441,6 +441,15 @@ public final class ProgramSelector implements Parcelable {
*/
public static @NonNull ProgramSelector createAmFmSelector(
@RadioManager.Band int band, int frequencyKhz, int subChannel) {
+ if (band == RadioManager.BAND_INVALID) {
+ // 50MHz is a rough boundary between AM (<30MHz) and FM (>60MHz).
+ if (frequencyKhz < 50000) {
+ band = (subChannel <= 0) ? RadioManager.BAND_AM : RadioManager.BAND_AM_HD;
+ } else {
+ band = (subChannel <= 0) ? RadioManager.BAND_FM : RadioManager.BAND_FM_HD;
+ }
+ }
+
boolean isAm = (band == RadioManager.BAND_AM || band == RadioManager.BAND_AM_HD);
boolean isDigital = (band == RadioManager.BAND_AM_HD || band == RadioManager.BAND_FM_HD);
if (!isAm && !isDigital && band != RadioManager.BAND_FM) {
diff --git a/android/hardware/radio/RadioManager.java b/android/hardware/radio/RadioManager.java
index b00f6033..8263bb8d 100644
--- a/android/hardware/radio/RadioManager.java
+++ b/android/hardware/radio/RadioManager.java
@@ -21,10 +21,12 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
@@ -58,6 +60,7 @@ import java.util.stream.Collectors;
*/
@SystemApi
@SystemService(Context.RADIO_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_BROADCAST_RADIO)
public class RadioManager {
private static final String TAG = "BroadcastRadio.manager";
@@ -208,19 +211,23 @@ public class RadioManager {
private final String mSerial;
private final int mNumTuners;
private final int mNumAudioSources;
+ private final boolean mIsInitializationRequired;
private final boolean mIsCaptureSupported;
private final BandDescriptor[] mBands;
private final boolean mIsBgScanSupported;
private final Set<Integer> mSupportedProgramTypes;
private final Set<Integer> mSupportedIdentifierTypes;
+ @Nullable private final Map<String, Integer> mDabFrequencyTable;
@NonNull private final Map<String, String> mVendorInfo;
/** @hide */
public ModuleProperties(int id, String serviceName, int classId, String implementor,
String product, String version, String serial, int numTuners, int numAudioSources,
- boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
+ boolean isInitializationRequired, boolean isCaptureSupported,
+ BandDescriptor[] bands, boolean isBgScanSupported,
@ProgramSelector.ProgramType int[] supportedProgramTypes,
@ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
+ @Nullable Map<String, Integer> dabFrequencyTable,
Map<String, String> vendorInfo) {
mId = id;
mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
@@ -231,11 +238,19 @@ public class RadioManager {
mSerial = serial;
mNumTuners = numTuners;
mNumAudioSources = numAudioSources;
+ mIsInitializationRequired = isInitializationRequired;
mIsCaptureSupported = isCaptureSupported;
mBands = bands;
mIsBgScanSupported = isBgScanSupported;
mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
+ if (dabFrequencyTable != null) {
+ for (Map.Entry<String, Integer> entry : dabFrequencyTable.entrySet()) {
+ Objects.requireNonNull(entry.getKey());
+ Objects.requireNonNull(entry.getValue());
+ }
+ }
+ mDabFrequencyTable = dabFrequencyTable;
mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
}
@@ -317,6 +332,18 @@ public class RadioManager {
return mNumAudioSources;
}
+ /**
+ * Checks, if BandConfig initialization (after {@link RadioManager#openTuner})
+ * is required to be done before other operations or not.
+ *
+ * If it is, the client has to wait for {@link RadioTuner.Callback#onConfigurationChanged}
+ * callback before executing any other operations. Otherwise, such operation will fail
+ * returning {@link RadioManager#STATUS_INVALID_OPERATION} error code.
+ */
+ public boolean isInitializationRequired() {
+ return mIsInitializationRequired;
+ }
+
/** {@code true} if audio capture is possible from radio tuner output.
* This indicates if routing to audio devices not connected to the same HAL as the FM radio
* is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented.
@@ -363,6 +390,19 @@ public class RadioManager {
}
/**
+ * A frequency table for Digital Audio Broadcasting (DAB).
+ *
+ * The key is a channel name, i.e. 5A, 7B.
+ *
+ * The value is a frequency, in kHz.
+ *
+ * @return a frequency table, or {@code null} if the module doesn't support DAB
+ */
+ public @Nullable Map<String, Integer> getDabFrequencyTable() {
+ return mDabFrequencyTable;
+ }
+
+ /**
* A map of vendor-specific opaque strings, passed from HAL without changes.
* Format of these strings can vary across vendors.
*
@@ -394,6 +434,7 @@ public class RadioManager {
mSerial = in.readString();
mNumTuners = in.readInt();
mNumAudioSources = in.readInt();
+ mIsInitializationRequired = in.readInt() == 1;
mIsCaptureSupported = in.readInt() == 1;
Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
mBands = new BandDescriptor[tmp.length];
@@ -403,6 +444,7 @@ public class RadioManager {
mIsBgScanSupported = in.readInt() == 1;
mSupportedProgramTypes = arrayToSet(in.createIntArray());
mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
+ mDabFrequencyTable = Utils.readStringIntMap(in);
mVendorInfo = Utils.readStringMap(in);
}
@@ -428,11 +470,13 @@ public class RadioManager {
dest.writeString(mSerial);
dest.writeInt(mNumTuners);
dest.writeInt(mNumAudioSources);
+ dest.writeInt(mIsInitializationRequired ? 1 : 0);
dest.writeInt(mIsCaptureSupported ? 1 : 0);
dest.writeParcelableArray(mBands, flags);
dest.writeInt(mIsBgScanSupported ? 1 : 0);
dest.writeIntArray(setToArray(mSupportedProgramTypes));
dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
+ Utils.writeStringIntMap(dest, mDabFrequencyTable);
Utils.writeStringMap(dest, mVendorInfo);
}
@@ -449,6 +493,7 @@ public class RadioManager {
+ ", mVersion=" + mVersion + ", mSerial=" + mSerial
+ ", mNumTuners=" + mNumTuners
+ ", mNumAudioSources=" + mNumAudioSources
+ + ", mIsInitializationRequired=" + mIsInitializationRequired
+ ", mIsCaptureSupported=" + mIsCaptureSupported
+ ", mIsBgScanSupported=" + mIsBgScanSupported
+ ", mBands=" + Arrays.toString(mBands) + "]";
@@ -456,67 +501,32 @@ public class RadioManager {
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + mId;
- result = prime * result + mServiceName.hashCode();
- result = prime * result + mClassId;
- result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode());
- result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode());
- result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());
- result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode());
- result = prime * result + mNumTuners;
- result = prime * result + mNumAudioSources;
- result = prime * result + (mIsCaptureSupported ? 1 : 0);
- result = prime * result + Arrays.hashCode(mBands);
- result = prime * result + (mIsBgScanSupported ? 1 : 0);
- result = prime * result + mVendorInfo.hashCode();
- return result;
+ return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
+ mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired,
+ mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
}
@Override
public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (!(obj instanceof ModuleProperties))
- return false;
+ if (this == obj) return true;
+ if (!(obj instanceof ModuleProperties)) return false;
ModuleProperties other = (ModuleProperties) obj;
- if (mId != other.getId())
- return false;
+
+ if (mId != other.getId()) return false;
if (!TextUtils.equals(mServiceName, other.mServiceName)) return false;
- if (mClassId != other.getClassId())
- return false;
- if (mImplementor == null) {
- if (other.getImplementor() != null)
- return false;
- } else if (!mImplementor.equals(other.getImplementor()))
- return false;
- if (mProduct == null) {
- if (other.getProduct() != null)
- return false;
- } else if (!mProduct.equals(other.getProduct()))
- return false;
- if (mVersion == null) {
- if (other.getVersion() != null)
- return false;
- } else if (!mVersion.equals(other.getVersion()))
- return false;
- if (mSerial == null) {
- if (other.getSerial() != null)
- return false;
- } else if (!mSerial.equals(other.getSerial()))
- return false;
- if (mNumTuners != other.getNumTuners())
- return false;
- if (mNumAudioSources != other.getNumAudioSources())
- return false;
- if (mIsCaptureSupported != other.isCaptureSupported())
- return false;
- if (!Arrays.equals(mBands, other.getBands()))
- return false;
- if (mIsBgScanSupported != other.isBackgroundScanningSupported())
- return false;
- if (!mVendorInfo.equals(other.mVendorInfo)) return false;
+ if (mClassId != other.mClassId) return false;
+ if (!Objects.equals(mImplementor, other.mImplementor)) return false;
+ if (!Objects.equals(mProduct, other.mProduct)) return false;
+ if (!Objects.equals(mVersion, other.mVersion)) return false;
+ if (!Objects.equals(mSerial, other.mSerial)) return false;
+ if (mNumTuners != other.mNumTuners) return false;
+ if (mNumAudioSources != other.mNumAudioSources) return false;
+ if (mIsInitializationRequired != other.mIsInitializationRequired) return false;
+ if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
+ if (!Objects.equals(mBands, other.mBands)) return false;
+ if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
+ if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false;
+ if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
return true;
}
}
diff --git a/android/hardware/radio/RadioMetadata.java b/android/hardware/radio/RadioMetadata.java
index 3cc4b566..6e510607 100644
--- a/android/hardware/radio/RadioMetadata.java
+++ b/android/hardware/radio/RadioMetadata.java
@@ -96,6 +96,48 @@ public final class RadioMetadata implements Parcelable {
*/
public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK";
+ /**
+ * Technology-independent program name (station name).
+ */
+ public static final String METADATA_KEY_PROGRAM_NAME =
+ "android.hardware.radio.metadata.PROGRAM_NAME";
+
+ /**
+ * DAB ensemble name.
+ */
+ public static final String METADATA_KEY_DAB_ENSEMBLE_NAME =
+ "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME";
+
+ /**
+ * DAB ensemble name - short version (up to 8 characters).
+ */
+ public static final String METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT =
+ "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME_SHORT";
+
+ /**
+ * DAB service name.
+ */
+ public static final String METADATA_KEY_DAB_SERVICE_NAME =
+ "android.hardware.radio.metadata.DAB_SERVICE_NAME";
+
+ /**
+ * DAB service name - short version (up to 8 characters).
+ */
+ public static final String METADATA_KEY_DAB_SERVICE_NAME_SHORT =
+ "android.hardware.radio.metadata.DAB_SERVICE_NAME_SHORT";
+
+ /**
+ * DAB component name.
+ */
+ public static final String METADATA_KEY_DAB_COMPONENT_NAME =
+ "android.hardware.radio.metadata.DAB_COMPONENT_NAME";
+
+ /**
+ * DAB component name.
+ */
+ public static final String METADATA_KEY_DAB_COMPONENT_NAME_SHORT =
+ "android.hardware.radio.metadata.DAB_COMPONENT_NAME_SHORT";
+
private static final int METADATA_TYPE_INVALID = -1;
private static final int METADATA_TYPE_INT = 0;
@@ -119,6 +161,13 @@ public final class RadioMetadata implements Parcelable {
METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_PROGRAM_NAME, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME_SHORT, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME, METADATA_TYPE_TEXT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME_SHORT, METADATA_TYPE_TEXT);
}
// keep in sync with: system/media/radio/include/system/radio_metadata.h
diff --git a/android/hardware/radio/RadioTuner.java b/android/hardware/radio/RadioTuner.java
index ed20c4aa..0edd0553 100644
--- a/android/hardware/radio/RadioTuner.java
+++ b/android/hardware/radio/RadioTuner.java
@@ -64,7 +64,9 @@ public abstract class RadioTuner {
* <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native
* service fails, </li>
* </ul>
+ * @deprecated Only applicable for HAL 1.x.
*/
+ @Deprecated
public abstract int setConfiguration(RadioManager.BandConfig config);
/**
@@ -80,7 +82,10 @@ public abstract class RadioTuner {
* <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native
* service fails, </li>
* </ul>
+ *
+ * @deprecated Only applicable for HAL 1.x.
*/
+ @Deprecated
public abstract int getConfiguration(RadioManager.BandConfig[] config);
@@ -228,7 +233,9 @@ public abstract class RadioTuner {
* <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native
* service fails, </li>
* </ul>
+ * @deprecated Use {@link onProgramInfoChanged} callback instead.
*/
+ @Deprecated
public abstract int getProgramInformation(RadioManager.ProgramInfo[] info);
/**
@@ -427,7 +434,10 @@ public abstract class RadioTuner {
* Get current antenna connection state for current configuration.
* Only valid if a configuration has been applied.
* @return {@code true} if the antenna is connected, {@code false} otherwise.
+ *
+ * @deprecated Use {@link onAntennaState} callback instead
*/
+ @Deprecated
public abstract boolean isAntennaConnected();
/**
@@ -446,20 +456,41 @@ public abstract class RadioTuner {
public abstract boolean hasControl();
/** Indicates a failure of radio IC or driver.
- * The application must close and re open the tuner */
+ * The application must close and re open the tuner
+ * @deprecated See {@link onError} callback.
+ */
+ @Deprecated
public static final int ERROR_HARDWARE_FAILURE = 0;
/** Indicates a failure of the radio service.
- * The application must close and re open the tuner */
+ * The application must close and re open the tuner
+ * @deprecated See {@link onError} callback.
+ */
+ @Deprecated
public static final int ERROR_SERVER_DIED = 1;
- /** A pending seek or tune operation was cancelled */
+ /** A pending seek or tune operation was cancelled
+ * @deprecated See {@link onError} callback.
+ */
+ @Deprecated
public static final int ERROR_CANCELLED = 2;
- /** A pending seek or tune operation timed out */
+ /** A pending seek or tune operation timed out
+ * @deprecated See {@link onError} callback.
+ */
+ @Deprecated
public static final int ERROR_SCAN_TIMEOUT = 3;
- /** The requested configuration could not be applied */
+ /** The requested configuration could not be applied
+ * @deprecated See {@link onError} callback.
+ */
+ @Deprecated
public static final int ERROR_CONFIG = 4;
- /** Background scan was interrupted due to hardware becoming temporarily unavailable. */
+ /** Background scan was interrupted due to hardware becoming temporarily unavailable.
+ * @deprecated See {@link onError} callback.
+ */
+ @Deprecated
public static final int ERROR_BACKGROUND_SCAN_UNAVAILABLE = 5;
- /** Background scan failed due to other error, ie. HW failure. */
+ /** Background scan failed due to other error, ie. HW failure.
+ * @deprecated See {@link onError} callback.
+ */
+ @Deprecated
public static final int ERROR_BACKGROUND_SCAN_FAILED = 6;
/**
@@ -473,13 +504,29 @@ public abstract class RadioTuner {
* status is one of {@link #ERROR_HARDWARE_FAILURE}, {@link #ERROR_SERVER_DIED},
* {@link #ERROR_CANCELLED}, {@link #ERROR_SCAN_TIMEOUT},
* {@link #ERROR_CONFIG}
+ *
+ * @deprecated Use {@link onTuneFailed} for tune, scan and step;
+ * other use cases (configuration, background scan) are already deprecated.
*/
public void onError(int status) {}
+
+ /**
+ * Called when tune, scan or step operation fails.
+ *
+ * @param result cause of the failure
+ * @param selector ProgramSelector argument of tune that failed;
+ * null for scan and step.
+ */
+ public void onTuneFailed(int result, @Nullable ProgramSelector selector) {}
+
/**
* onConfigurationChanged() is called upon successful completion of
* {@link RadioManager#openTuner(int, RadioManager.BandConfig, boolean, Callback, Handler)}
* or {@link RadioTuner#setConfiguration(RadioManager.BandConfig)}
+ *
+ * @deprecated Only applicable for HAL 1.x.
*/
+ @Deprecated
public void onConfigurationChanged(RadioManager.BandConfig config) {}
/**
diff --git a/android/hardware/radio/TunerAdapter.java b/android/hardware/radio/TunerAdapter.java
index 91944bfd..be2846f8 100644
--- a/android/hardware/radio/TunerAdapter.java
+++ b/android/hardware/radio/TunerAdapter.java
@@ -60,6 +60,7 @@ class TunerAdapter extends RadioTuner {
mLegacyListProxy.close();
mLegacyListProxy = null;
}
+ mCallback.close();
}
try {
mTuner.close();
@@ -202,15 +203,17 @@ class TunerAdapter extends RadioTuner {
@Override
public int getProgramInformation(RadioManager.ProgramInfo[] info) {
if (info == null || info.length != 1) {
- throw new IllegalArgumentException("The argument must be an array of length 1");
+ Log.e(TAG, "The argument must be an array of length 1");
+ return RadioManager.STATUS_BAD_VALUE;
}
- try {
- info[0] = mTuner.getProgramInformation();
- return RadioManager.STATUS_OK;
- } catch (RemoteException e) {
- Log.e(TAG, "service died", e);
- return RadioManager.STATUS_DEAD_OBJECT;
+
+ RadioManager.ProgramInfo current = mCallback.getCurrentProgramInformation();
+ if (current == null) {
+ Log.w(TAG, "Didn't get program info yet");
+ return RadioManager.STATUS_INVALID_OPERATION;
}
+ info[0] = current;
+ return RadioManager.STATUS_OK;
}
@Override
@@ -276,6 +279,7 @@ class TunerAdapter extends RadioTuner {
try {
mTuner.startProgramListUpdates(filter);
} catch (UnsupportedOperationException ex) {
+ Log.i(TAG, "Program list is not supported with this hardware");
return null;
} catch (RemoteException ex) {
mCallback.setProgramListObserver(null, () -> { });
@@ -288,12 +292,20 @@ class TunerAdapter extends RadioTuner {
@Override
public boolean isAnalogForced() {
- return isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG);
+ try {
+ return isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG);
+ } catch (UnsupportedOperationException ex) {
+ throw new IllegalStateException(ex);
+ }
}
@Override
public void setAnalogForced(boolean isForced) {
- setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, isForced);
+ try {
+ setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, isForced);
+ } catch (UnsupportedOperationException ex) {
+ throw new IllegalStateException(ex);
+ }
}
@Override
@@ -343,11 +355,7 @@ class TunerAdapter extends RadioTuner {
@Override
public boolean isAntennaConnected() {
- try {
- return mTuner.isAntennaConnected();
- } catch (RemoteException e) {
- throw new RuntimeException("service died", e);
- }
+ return mCallback.isAntennaConnected();
}
@Override
diff --git a/android/hardware/radio/TunerCallbackAdapter.java b/android/hardware/radio/TunerCallbackAdapter.java
index b299ffe0..0fb93e53 100644
--- a/android/hardware/radio/TunerCallbackAdapter.java
+++ b/android/hardware/radio/TunerCallbackAdapter.java
@@ -37,8 +37,12 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
@NonNull private final Handler mHandler;
@Nullable ProgramList mProgramList;
- @Nullable List<RadioManager.ProgramInfo> mLastCompleteList; // for legacy getProgramList call
+
+ // cache for deprecated methods
+ boolean mIsAntennaConnected = true;
+ @Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
private boolean mDelayedCompleteCallback = false;
+ @Nullable RadioManager.ProgramInfo mCurrentProgramInfo;
TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
mCallback = callback;
@@ -49,6 +53,12 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
}
}
+ void close() {
+ synchronized (mLock) {
+ if (mProgramList != null) mProgramList.close();
+ }
+ }
+
void setProgramListObserver(@Nullable ProgramList programList,
@NonNull ProgramList.OnCloseListener closeListener) {
Objects.requireNonNull(closeListener);
@@ -92,12 +102,46 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
}
}
+ @Nullable RadioManager.ProgramInfo getCurrentProgramInformation() {
+ synchronized (mLock) {
+ return mCurrentProgramInfo;
+ }
+ }
+
+ boolean isAntennaConnected() {
+ return mIsAntennaConnected;
+ }
+
@Override
public void onError(int status) {
mHandler.post(() -> mCallback.onError(status));
}
@Override
+ public void onTuneFailed(int status, @Nullable ProgramSelector selector) {
+ mHandler.post(() -> mCallback.onTuneFailed(status, selector));
+
+ int errorCode;
+ switch (status) {
+ case RadioManager.STATUS_PERMISSION_DENIED:
+ case RadioManager.STATUS_DEAD_OBJECT:
+ errorCode = RadioTuner.ERROR_SERVER_DIED;
+ break;
+ case RadioManager.STATUS_ERROR:
+ case RadioManager.STATUS_NO_INIT:
+ case RadioManager.STATUS_BAD_VALUE:
+ case RadioManager.STATUS_INVALID_OPERATION:
+ Log.i(TAG, "Got an error with no mapping to the legacy API (" + status
+ + "), doing a best-effort conversion to ERROR_SCAN_TIMEOUT");
+ // fall through
+ case RadioManager.STATUS_TIMED_OUT:
+ default:
+ errorCode = RadioTuner.ERROR_SCAN_TIMEOUT;
+ }
+ mHandler.post(() -> mCallback.onError(errorCode));
+ }
+
+ @Override
public void onConfigurationChanged(RadioManager.BandConfig config) {
mHandler.post(() -> mCallback.onConfigurationChanged(config));
}
@@ -109,6 +153,10 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
return;
}
+ synchronized (mLock) {
+ mCurrentProgramInfo = info;
+ }
+
mHandler.post(() -> {
mCallback.onProgramInfoChanged(info);
@@ -129,6 +177,7 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
@Override
public void onAntennaState(boolean connected) {
+ mIsAntennaConnected = connected;
mHandler.post(() -> mCallback.onAntennaState(connected));
}
diff --git a/android/hardware/radio/Utils.java b/android/hardware/radio/Utils.java
index f1b58974..9887f782 100644
--- a/android/hardware/radio/Utils.java
+++ b/android/hardware/radio/Utils.java
@@ -56,6 +56,29 @@ final class Utils {
return map;
}
+ static void writeStringIntMap(@NonNull Parcel dest, @Nullable Map<String, Integer> map) {
+ if (map == null) {
+ dest.writeInt(0);
+ return;
+ }
+ dest.writeInt(map.size());
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeInt(entry.getValue());
+ }
+ }
+
+ static @NonNull Map<String, Integer> readStringIntMap(@NonNull Parcel in) {
+ int size = in.readInt();
+ Map<String, Integer> map = new HashMap<>();
+ while (size-- > 0) {
+ String key = in.readString();
+ int value = in.readInt();
+ map.put(key, value);
+ }
+ return map;
+ }
+
static <T extends Parcelable> void writeSet(@NonNull Parcel dest, @Nullable Set<T> set) {
if (set == null) {
dest.writeInt(0);
diff --git a/android/hardware/soundtrigger/SoundTrigger.java b/android/hardware/soundtrigger/SoundTrigger.java
index b635088c..dde8a332 100644
--- a/android/hardware/soundtrigger/SoundTrigger.java
+++ b/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,6 +16,14 @@
package android.hardware.soundtrigger;
+import static android.system.OsConstants.EINVAL;
+import static android.system.OsConstants.ENODEV;
+import static android.system.OsConstants.ENOSYS;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.EPIPE;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.media.AudioFormat;
import android.os.Handler;
import android.os.Parcel;
@@ -25,22 +33,33 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
-import static android.system.OsConstants.*;
-
/**
* The SoundTrigger class provides access via JNI to the native service managing
* the sound trigger HAL.
*
* @hide
*/
+@SystemApi
public class SoundTrigger {
+ private SoundTrigger() {
+ }
+
+ /**
+ * Status code used when the operation succeeded
+ */
public static final int STATUS_OK = 0;
+ /** @hide */
public static final int STATUS_ERROR = Integer.MIN_VALUE;
+ /** @hide */
public static final int STATUS_PERMISSION_DENIED = -EPERM;
+ /** @hide */
public static final int STATUS_NO_INIT = -ENODEV;
+ /** @hide */
public static final int STATUS_BAD_VALUE = -EINVAL;
+ /** @hide */
public static final int STATUS_DEAD_OBJECT = -EPIPE;
+ /** @hide */
public static final int STATUS_INVALID_OPERATION = -ENOSYS;
/*****************************************************************************
@@ -48,6 +67,8 @@ public class SoundTrigger {
* managed by the native sound trigger service. Each module has a unique
* ID used to target any API call to this paricular module. Module
* properties are returned by listModules() method.
+ *
+ * @hide
****************************************************************************/
public static class ModuleProperties implements Parcelable {
/** Unique module ID provided by the native service */
@@ -187,6 +208,8 @@ public class SoundTrigger {
* implementation to detect a particular sound pattern.
* A specialized version {@link KeyphraseSoundModel} is defined for key phrase
* sound models.
+ *
+ * @hide
****************************************************************************/
public static class SoundModel {
/** Undefined sound model type */
@@ -261,6 +284,8 @@ public class SoundTrigger {
/*****************************************************************************
* A Keyphrase describes a key phrase that can be detected by a
* {@link KeyphraseSoundModel}
+ *
+ * @hide
****************************************************************************/
public static class Keyphrase implements Parcelable {
/** Unique identifier for this keyphrase */
@@ -382,6 +407,8 @@ public class SoundTrigger {
* A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
* It contains data needed by the hardware to detect a certain number of key phrases
* and the list of corresponding {@link Keyphrase} descriptors.
+ *
+ * @hide
****************************************************************************/
public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
/** Key phrases in this sound model */
@@ -468,6 +495,8 @@ public class SoundTrigger {
/*****************************************************************************
* A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
* patterns.
+ *
+ * @hide
****************************************************************************/
public static class GenericSoundModel extends SoundModel implements Parcelable {
@@ -524,52 +553,115 @@ public class SoundTrigger {
/**
* Modes for key phrase recognition
*/
- /** Simple recognition of the key phrase */
+
+ /**
+ * Simple recognition of the key phrase
+ *
+ * @hide
+ */
public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
- /** Trigger only if one user is identified */
+ /**
+ * Trigger only if one user is identified
+ *
+ * @hide
+ */
public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
- /** Trigger only if one user is authenticated */
+ /**
+ * Trigger only if one user is authenticated
+ *
+ * @hide
+ */
public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
/**
* Status codes for {@link RecognitionEvent}
*/
- /** Recognition success */
+ /**
+ * Recognition success
+ *
+ * @hide
+ */
public static final int RECOGNITION_STATUS_SUCCESS = 0;
- /** Recognition aborted (e.g. capture preempted by anotehr use case */
+ /**
+ * Recognition aborted (e.g. capture preempted by anotehr use case
+ *
+ * @hide
+ */
public static final int RECOGNITION_STATUS_ABORT = 1;
- /** Recognition failure */
+ /**
+ * Recognition failure
+ *
+ * @hide
+ */
public static final int RECOGNITION_STATUS_FAILURE = 2;
/**
* A RecognitionEvent is provided by the
- * {@link StatusListener#onRecognition(RecognitionEvent)}
+ * {@code StatusListener#onRecognition(RecognitionEvent)}
* callback upon recognition success or failure.
*/
- public static class RecognitionEvent implements Parcelable {
- /** Recognition status e.g {@link #RECOGNITION_STATUS_SUCCESS} */
+ public static class RecognitionEvent {
+ /**
+ * Recognition status e.g RECOGNITION_STATUS_SUCCESS
+ *
+ * @hide
+ */
public final int status;
- /** Sound Model corresponding to this event callback */
+ /**
+ *
+ * Sound Model corresponding to this event callback
+ *
+ * @hide
+ */
public final int soundModelHandle;
- /** True if it is possible to capture audio from this utterance buffered by the hardware */
+ /**
+ * True if it is possible to capture audio from this utterance buffered by the hardware
+ *
+ * @hide
+ */
public final boolean captureAvailable;
- /** Audio session ID to be used when capturing the utterance with an AudioRecord
- * if captureAvailable() is true. */
+ /**
+ * Audio session ID to be used when capturing the utterance with an AudioRecord
+ * if captureAvailable() is true.
+ *
+ * @hide
+ */
public final int captureSession;
- /** Delay in ms between end of model detection and start of audio available for capture.
- * A negative value is possible (e.g. if keyphrase is also available for capture) */
+ /**
+ * Delay in ms between end of model detection and start of audio available for capture.
+ * A negative value is possible (e.g. if keyphrase is also available for capture)
+ *
+ * @hide
+ */
public final int captureDelayMs;
- /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
+ /**
+ * Duration in ms of audio captured before the start of the trigger. 0 if none.
+ *
+ * @hide
+ */
public final int capturePreambleMs;
- /** True if the trigger (key phrase capture is present in binary data */
+ /**
+ * True if the trigger (key phrase capture is present in binary data
+ *
+ * @hide
+ */
public final boolean triggerInData;
- /** Audio format of either the trigger in event data or to use for capture of the
- * rest of the utterance */
- public AudioFormat captureFormat;
- /** Opaque data for use by system applications who know about voice engine internals,
- * typically during enrollment. */
+ /**
+ * Audio format of either the trigger in event data or to use for capture of the
+ * rest of the utterance
+ *
+ * @hide
+ */
+ public final AudioFormat captureFormat;
+ /**
+ * Opaque data for use by system applications who know about voice engine internals,
+ * typically during enrollment.
+ *
+ * @hide
+ */
public final byte[] data;
+ /** @hide */
public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
int captureSession, int captureDelayMs, int capturePreambleMs,
boolean triggerInData, AudioFormat captureFormat, byte[] data) {
@@ -584,6 +676,46 @@ public class SoundTrigger {
this.data = data;
}
+ /**
+ * Check if is possible to capture audio from this utterance buffered by the hardware.
+ *
+ * @return {@code true} iff a capturing is possible
+ */
+ public boolean isCaptureAvailable() {
+ return captureAvailable;
+ }
+
+ /**
+ * Get the audio format of either the trigger in event data or to use for capture of the
+ * rest of the utterance
+ *
+ * @return the audio format
+ */
+ @Nullable public AudioFormat getCaptureFormat() {
+ return captureFormat;
+ }
+
+ /**
+ * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord}
+ * if {@link #isCaptureAvailable()} is true.
+ *
+ * @return The id of the capture session
+ */
+ public int getCaptureSession() {
+ return captureSession;
+ }
+
+ /**
+ * Get the opaque data for use by system applications who know about voice engine
+ * internals, typically during enrollment.
+ *
+ * @return The data of the event
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /** @hide */
public static final Parcelable.Creator<RecognitionEvent> CREATOR
= new Parcelable.Creator<RecognitionEvent>() {
public RecognitionEvent createFromParcel(Parcel in) {
@@ -595,6 +727,7 @@ public class SoundTrigger {
}
};
+ /** @hide */
protected static RecognitionEvent fromParcel(Parcel in) {
int status = in.readInt();
int soundModelHandle = in.readInt();
@@ -619,12 +752,12 @@ public class SoundTrigger {
captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data);
}
- @Override
+ /** @hide */
public int describeContents() {
return 0;
}
- @Override
+ /** @hide */
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(status);
dest.writeInt(soundModelHandle);
@@ -726,6 +859,8 @@ public class SoundTrigger {
* A RecognitionConfig is provided to
* {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
* recognition request.
+ *
+ * @hide
*/
public static class RecognitionConfig implements Parcelable {
/** True if the DSP should capture the trigger sound and make it available for further
@@ -744,7 +879,7 @@ public class SoundTrigger {
public final byte[] data;
public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
- KeyphraseRecognitionExtra keyphrases[], byte[] data) {
+ KeyphraseRecognitionExtra[] keyphrases, byte[] data) {
this.captureRequested = captureRequested;
this.allowMultipleTriggers = allowMultipleTriggers;
this.keyphrases = keyphrases;
@@ -799,6 +934,8 @@ public class SoundTrigger {
* When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
* should trigger a recognition.
* - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
+ *
+ * @hide
*/
public static class ConfidenceLevel implements Parcelable {
public final int userId;
@@ -872,6 +1009,8 @@ public class SoundTrigger {
/**
* Additional data conveyed by a {@link KeyphraseRecognitionEvent}
* for a key phrase detection.
+ *
+ * @hide
*/
public static class KeyphraseRecognitionExtra implements Parcelable {
/** The keyphrase ID */
@@ -970,8 +1109,10 @@ public class SoundTrigger {
/**
* Specialized {@link RecognitionEvent} for a key phrase detection.
+ *
+ * @hide
*/
- public static class KeyphraseRecognitionEvent extends RecognitionEvent {
+ public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
/** Indicates if the key phrase is present in the buffered audio available for capture */
public final KeyphraseRecognitionExtra[] keyphraseExtras;
@@ -1091,8 +1232,10 @@ public class SoundTrigger {
/**
* Sub-class of RecognitionEvent specifically for sound-trigger based sound
* models(non-keyphrase). Currently does not contain any additional fields.
+ *
+ * @hide
*/
- public static class GenericRecognitionEvent extends RecognitionEvent {
+ public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
public GenericRecognitionEvent(int status, int soundModelHandle,
boolean captureAvailable, int captureSession, int captureDelayMs,
int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
@@ -1140,13 +1283,19 @@ public class SoundTrigger {
/**
* Status codes for {@link SoundModelEvent}
*/
- /** Sound Model was updated */
+ /**
+ * Sound Model was updated
+ *
+ * @hide
+ */
public static final int SOUNDMODEL_STATUS_UPDATED = 0;
/**
* A SoundModelEvent is provided by the
* {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
* callback when a sound model has been updated by the implementation
+ *
+ * @hide
*/
public static class SoundModelEvent implements Parcelable {
/** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
@@ -1231,9 +1380,17 @@ public class SoundTrigger {
* Native service state. {@link StatusListener#onServiceStateChange(int)}
*/
// Keep in sync with system/core/include/system/sound_trigger.h
- /** Sound trigger service is enabled */
+ /**
+ * Sound trigger service is enabled
+ *
+ * @hide
+ */
public static final int SERVICE_STATE_ENABLED = 0;
- /** Sound trigger service is disabled */
+ /**
+ * Sound trigger service is disabled
+ *
+ * @hide
+ */
public static final int SERVICE_STATE_DISABLED = 1;
/**
@@ -1245,6 +1402,8 @@ public class SoundTrigger {
* - {@link #STATUS_NO_INIT} if the native service cannot be reached
* - {@link #STATUS_BAD_VALUE} if modules is null
* - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
+ *
+ * @hide
*/
public static native int listModules(ArrayList <ModuleProperties> modules);
@@ -1256,6 +1415,8 @@ public class SoundTrigger {
* @param handler the Handler that will receive the callabcks. Can be null if default handler
* is OK.
* @return a valid sound module in case of success or null in case of error.
+ *
+ * @hide
*/
public static SoundTriggerModule attachModule(int moduleId,
StatusListener listener,
@@ -1270,6 +1431,8 @@ public class SoundTrigger {
/**
* Interface provided by the client application when attaching to a {@link SoundTriggerModule}
* to received recognition and error notifications.
+ *
+ * @hide
*/
public static interface StatusListener {
/**
diff --git a/android/hardware/usb/AccessoryFilter.java b/android/hardware/usb/AccessoryFilter.java
index d9b7c5be..00070fe3 100644
--- a/android/hardware/usb/AccessoryFilter.java
+++ b/android/hardware/usb/AccessoryFilter.java
@@ -16,6 +16,11 @@
package android.hardware.usb;
+import android.annotation.NonNull;
+import android.service.usb.UsbAccessoryFilterProto;
+
+import com.android.internal.util.dump.DualDumpOutputStream;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -142,4 +147,17 @@ public class AccessoryFilter {
"\", mModel=\"" + mModel +
"\", mVersion=\"" + mVersion + "\"]";
}
+
+ /**
+ * Write a description of the filter to a dump stream.
+ */
+ public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("manufacturer", UsbAccessoryFilterProto.MANUFACTURER, mManufacturer);
+ dump.write("model", UsbAccessoryFilterProto.MODEL, mModel);
+ dump.write("version", UsbAccessoryFilterProto.VERSION, mVersion);
+
+ dump.end(token);
+ }
}
diff --git a/android/hardware/usb/DeviceFilter.java b/android/hardware/usb/DeviceFilter.java
index 439c6297..6f1aff71 100644
--- a/android/hardware/usb/DeviceFilter.java
+++ b/android/hardware/usb/DeviceFilter.java
@@ -16,8 +16,12 @@
package android.hardware.usb;
+import android.annotation.NonNull;
+import android.service.usb.UsbDeviceFilterProto;
import android.util.Slog;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -310,4 +314,22 @@ public class DeviceFilter {
",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
"]";
}
+
+ /**
+ * Write a description of the filter to a dump stream.
+ */
+ public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("vendor_id", UsbDeviceFilterProto.VENDOR_ID, mVendorId);
+ dump.write("product_id", UsbDeviceFilterProto.PRODUCT_ID, mProductId);
+ dump.write("class", UsbDeviceFilterProto.CLASS, mClass);
+ dump.write("subclass", UsbDeviceFilterProto.SUBCLASS, mSubclass);
+ dump.write("protocol", UsbDeviceFilterProto.PROTOCOL, mProtocol);
+ dump.write("manufacturer_name", UsbDeviceFilterProto.MANUFACTURER_NAME, mManufacturerName);
+ dump.write("product_name", UsbDeviceFilterProto.PRODUCT_NAME, mProductName);
+ dump.write("serial_number", UsbDeviceFilterProto.SERIAL_NUMBER, mSerialNumber);
+
+ dump.end(token);
+ }
}
diff --git a/android/hardware/usb/UsbConfiguration.java b/android/hardware/usb/UsbConfiguration.java
index a1715708..6ce42019 100644
--- a/android/hardware/usb/UsbConfiguration.java
+++ b/android/hardware/usb/UsbConfiguration.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+
import com.android.internal.util.Preconditions;
/**
@@ -106,6 +107,17 @@ public class UsbConfiguration implements Parcelable {
}
/**
+ * Returns the attributes of this configuration
+ *
+ * @return the configuration's attributes
+ *
+ * @hide
+ */
+ public int getAttributes() {
+ return mAttributes;
+ }
+
+ /**
* Returns the configuration's max power consumption, in milliamps.
*
* @return the configuration's max power
diff --git a/android/hardware/usb/UsbConstants.java b/android/hardware/usb/UsbConstants.java
index 0e8d47ca..215e9d5f 100644
--- a/android/hardware/usb/UsbConstants.java
+++ b/android/hardware/usb/UsbConstants.java
@@ -16,6 +16,8 @@
package android.hardware.usb;
+import android.service.ServiceProtoEnums;
+
/**
* Contains constants for the USB protocol.
* These constants correspond to definitions in linux/usb/ch9.h in the linux kernel.
@@ -35,12 +37,12 @@ public final class UsbConstants {
* Used to signify direction of data for a {@link UsbEndpoint} is OUT (host to device)
* @see UsbEndpoint#getDirection
*/
- public static final int USB_DIR_OUT = 0;
+ public static final int USB_DIR_OUT = ServiceProtoEnums.USB_ENDPOINT_DIR_OUT; // 0
/**
* Used to signify direction of data for a {@link UsbEndpoint} is IN (device to host)
* @see UsbEndpoint#getDirection
*/
- public static final int USB_DIR_IN = 0x80;
+ public static final int USB_DIR_IN = ServiceProtoEnums.USB_ENDPOINT_DIR_IN; // 0x80
/**
* Bitmask used for extracting the {@link UsbEndpoint} number its address field.
@@ -63,22 +65,26 @@ public final class UsbConstants {
* Control endpoint type (endpoint zero)
* @see UsbEndpoint#getType
*/
- public static final int USB_ENDPOINT_XFER_CONTROL = 0;
+ public static final int USB_ENDPOINT_XFER_CONTROL =
+ ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_CONTROL; // 0
/**
* Isochronous endpoint type (currently not supported)
* @see UsbEndpoint#getType
*/
- public static final int USB_ENDPOINT_XFER_ISOC = 1;
+ public static final int USB_ENDPOINT_XFER_ISOC =
+ ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_ISOC; // 1
/**
* Bulk endpoint type
* @see UsbEndpoint#getType
*/
- public static final int USB_ENDPOINT_XFER_BULK = 2;
+ public static final int USB_ENDPOINT_XFER_BULK =
+ ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_BULK; // 2
/**
* Interrupt endpoint type
* @see UsbEndpoint#getType
*/
- public static final int USB_ENDPOINT_XFER_INT = 3;
+ public static final int USB_ENDPOINT_XFER_INT =
+ ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_INT; // 3
/**
diff --git a/android/hardware/usb/UsbDeviceConnection.java b/android/hardware/usb/UsbDeviceConnection.java
index 5b15c0d2..9e5174ad 100644
--- a/android/hardware/usb/UsbDeviceConnection.java
+++ b/android/hardware/usb/UsbDeviceConnection.java
@@ -222,7 +222,10 @@ public class UsbDeviceConnection {
* @param endpoint the endpoint for this transaction
* @param buffer buffer for data to send or receive; can be {@code null} to wait for next
* transaction without reading data
- * @param length the length of the data to send or receive
+ * @param length the length of the data to send or receive. Before
+ * {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes
+ * would be truncated down to 16384. In API {@value Build.VERSION_CODES#P}
+ * and after, any value of length is valid.
* @param timeout in milliseconds, 0 is infinite
* @return length of data transferred (or zero) for success,
* or negative value for failure
@@ -239,7 +242,10 @@ public class UsbDeviceConnection {
* @param endpoint the endpoint for this transaction
* @param buffer buffer for data to send or receive
* @param offset the index of the first byte in the buffer to send or receive
- * @param length the length of the data to send or receive
+ * @param length the length of the data to send or receive. Before
+ * {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes
+ * would be truncated down to 16384. In API {@value Build.VERSION_CODES#P}
+ * and after, any value of length is valid.
* @param timeout in milliseconds, 0 is infinite
* @return length of data transferred (or zero) for success,
* or negative value for failure
@@ -247,6 +253,10 @@ public class UsbDeviceConnection {
public int bulkTransfer(UsbEndpoint endpoint,
byte[] buffer, int offset, int length, int timeout) {
checkBounds(buffer, offset, length);
+ if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+ && length > UsbRequest.MAX_USBFS_BUFFER_SIZE) {
+ length = UsbRequest.MAX_USBFS_BUFFER_SIZE;
+ }
return native_bulk_request(endpoint.getAddress(), buffer, offset, length, timeout);
}
diff --git a/android/hardware/usb/UsbManager.java b/android/hardware/usb/UsbManager.java
index 7617c2bd..46142e35 100644
--- a/android/hardware/usb/UsbManager.java
+++ b/android/hardware/usb/UsbManager.java
@@ -19,6 +19,7 @@ package android.hardware.usb;
import android.Manifest;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -27,7 +28,9 @@ import android.annotation.SystemService;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -37,6 +40,8 @@ import android.util.Log;
import com.android.internal.util.Preconditions;
import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
/**
* This class allows you to access the state of USB and communicate with USB devices.
@@ -70,7 +75,7 @@ public class UsbManager {
* MTP function is enabled
* <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
* PTP function is enabled
- * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
+ * <li> {@link #USB_FUNCTION_ACCESSORY} boolean extra indicating whether the
* accessory function is enabled
* <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
* audio source function is enabled
@@ -187,17 +192,8 @@ public class UsbManager {
public static final String USB_DATA_UNLOCKED = "unlocked";
/**
- * Boolean extra indicating whether the intent represents a change in the usb
- * configuration (as opposed to a state update).
- *
- * {@hide}
- */
- public static final String USB_CONFIG_CHANGED = "config_changed";
-
- /**
* A placeholder indicating that no USB function is being specified.
- * Used to distinguish between selecting no function vs. the default function in
- * {@link #setCurrentFunction(String)}.
+ * Used for compatibility with old init scripts to indicate no functions vs. charging function.
*
* {@hide}
*/
@@ -298,6 +294,69 @@ public class UsbManager {
*/
public static final String EXTRA_PERMISSION_GRANTED = "permission";
+ /**
+ * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_NONE = 0;
+
+ /**
+ * Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_MTP = GadgetFunction.MTP;
+
+ /**
+ * Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_PTP = GadgetFunction.PTP;
+
+ /**
+ * Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS;
+
+ /**
+ * Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
+
+ /**
+ * Code for the accessory usb function.
+ * {@hide}
+ */
+ public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
+
+ /**
+ * Code for the audio source usb function.
+ * {@hide}
+ */
+ public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
+
+ /**
+ * Code for the adb usb function.
+ * {@hide}
+ */
+ public static final long FUNCTION_ADB = GadgetFunction.ADB;
+
+ private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
+ | FUNCTION_MIDI;
+
+ private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
+
+ static {
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MTP, FUNCTION_MTP);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_PTP, FUNCTION_PTP);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_RNDIS, FUNCTION_RNDIS);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MIDI, FUNCTION_MIDI);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
+ }
+
private final Context mContext;
private final IUsbManager mService;
@@ -317,6 +376,7 @@ public class UsbManager {
*
* @return HashMap containing all connected USB devices.
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_HOST)
public HashMap<String,UsbDevice> getDeviceList() {
HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
if (mService == null) {
@@ -341,6 +401,7 @@ public class UsbManager {
* @param device the device to open
* @return a {@link UsbDeviceConnection}, or {@code null} if open failed
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_HOST)
public UsbDeviceConnection openDevice(UsbDevice device) {
try {
String deviceName = device.getDeviceName();
@@ -365,6 +426,7 @@ public class UsbManager {
*
* @return list of USB accessories, or null if none are attached.
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
public UsbAccessory[] getAccessoryList() {
if (mService == null) {
return null;
@@ -384,9 +446,14 @@ public class UsbManager {
/**
* Opens a file descriptor for reading and writing data to the USB accessory.
*
+ * <p>If data is read from the {@link java.io.InputStream} created from this file descriptor all
+ * data of a USB transfer should be read at once. If only a partial request is read the rest of
+ * the transfer is dropped.
+ *
* @param accessory the USB accessory to open
- * @return file descriptor, or null if the accessor could not be opened.
+ * @return file descriptor, or null if the accessory could not be opened.
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
try {
return mService.openAccessory(accessory);
@@ -396,6 +463,25 @@ public class UsbManager {
}
/**
+ * Gets the functionfs control file descriptor for the given function, with
+ * the usb descriptors and strings already written. The file descriptor is used
+ * by the function implementation to handle events and control requests.
+ *
+ * @param function to get control fd for. Currently {@link #FUNCTION_MTP} and
+ * {@link #FUNCTION_PTP} are supported.
+ * @return A ParcelFileDescriptor holding the valid fd, or null if the fd was not found.
+ *
+ * {@hide}
+ */
+ public ParcelFileDescriptor getControlFd(long function) {
+ try {
+ return mService.getControlFd(function);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns true if the caller has permission to access the device.
* Permission might have been granted temporarily via
* {@link #requestPermission(UsbDevice, PendingIntent)} or
@@ -407,6 +493,7 @@ public class UsbManager {
* @param device to check permissions for
* @return true if caller has permission
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_HOST)
public boolean hasPermission(UsbDevice device) {
if (mService == null) {
return false;
@@ -427,6 +514,7 @@ public class UsbManager {
* @param accessory to check permissions for
* @return true if caller has permission
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
public boolean hasPermission(UsbAccessory accessory) {
if (mService == null) {
return false;
@@ -460,6 +548,7 @@ public class UsbManager {
* @param device to request permissions for
* @param pi PendingIntent for returning result
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_HOST)
public void requestPermission(UsbDevice device, PendingIntent pi) {
try {
mService.requestDevicePermission(device, mContext.getPackageName(), pi);
@@ -486,6 +575,7 @@ public class UsbManager {
* @param accessory to request permissions for
* @param pi PendingIntent for returning result
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
public void requestPermission(UsbAccessory accessory, PendingIntent pi) {
try {
mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi);
@@ -548,15 +638,14 @@ public class UsbManager {
* services offered by the device.
* </p>
*
+ * @deprecated use getCurrentFunctions() instead.
* @param function name of the USB function
* @return true if the USB function is enabled
*
* {@hide}
*/
+ @Deprecated
public boolean isFunctionEnabled(String function) {
- if (mService == null) {
- return false;
- }
try {
return mService.isFunctionEnabled(function);
} catch (RemoteException e) {
@@ -565,7 +654,7 @@ public class UsbManager {
}
/**
- * Sets the current USB function when in device mode.
+ * Sets the current USB functions when in device mode.
* <p>
* USB functions represent interfaces which are published to the host to access
* services offered by the device.
@@ -574,27 +663,59 @@ public class UsbManager {
* automatically activate additional functions such as {@link #USB_FUNCTION_ADB}
* or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states.
* </p><p>
- * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE},
- * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
- * or {@link #USB_FUNCTION_RNDIS}.
- * </p><p>
- * Also sets whether USB data (for example, MTP exposed pictures) should be made available
- * on the USB connection when in device mode. Unlocking usb data should only be done with
- * user involvement, since exposing pictures or other data could leak sensitive
- * user information.
+ * An argument of 0 indicates that the device is charging, and can pick any
+ * appropriate function for that purpose.
* </p><p>
* Note: This function is asynchronous and may fail silently without applying
* the requested changes.
* </p>
*
- * @param function name of the USB function, or null to restore the default function
- * @param usbDataUnlocked whether user data is accessible
+ * @param functions the USB function(s) to set, as a bitwise mask.
+ * Must satisfy {@link UsbManager#areSettableFunctions}
*
* {@hide}
*/
- public void setCurrentFunction(String function, boolean usbDataUnlocked) {
+ public void setCurrentFunctions(long functions) {
try {
- mService.setCurrentFunction(function, usbDataUnlocked);
+ mService.setCurrentFunctions(functions);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the current USB functions when in device mode.
+ *
+ * @deprecated use setCurrentFunctions(long) instead.
+ * @param functions the USB function(s) to set.
+ * @param usbDataUnlocked unused
+
+ * {@hide}
+ */
+ @Deprecated
+ public void setCurrentFunction(String functions, boolean usbDataUnlocked) {
+ try {
+ mService.setCurrentFunction(functions, usbDataUnlocked);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current USB functions in device mode.
+ * <p>
+ * This function returns the state of primary USB functions and can return a
+ * mask containing any usb function(s) except for ADB.
+ * </p>
+ *
+ * @return The currently enabled functions, in a bitwise mask.
+ * A zero mask indicates that the current function is the charging function.
+ *
+ * {@hide}
+ */
+ public long getCurrentFunctions() {
+ try {
+ return mService.getCurrentFunctions();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -604,23 +725,37 @@ public class UsbManager {
* Sets the screen unlocked functions, which are persisted and set as the current functions
* whenever the screen is unlocked.
* <p>
- * The allowed values are: {@link #USB_FUNCTION_NONE},
- * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
- * or {@link #USB_FUNCTION_RNDIS}.
- * {@link #USB_FUNCTION_NONE} has the effect of switching off this feature, so functions
+ * A zero mask has the effect of switching off this feature, so functions
* no longer change on screen unlock.
* </p><p>
* Note: When the screen is on, this method will apply given functions as current functions,
* which is asynchronous and may fail silently without applying the requested changes.
* </p>
*
- * @param function function to set as default
+ * @param functions functions to set, in a bitwise mask.
+ * Must satisfy {@link UsbManager#areSettableFunctions}
+ *
+ * {@hide}
+ */
+ public void setScreenUnlockedFunctions(long functions) {
+ try {
+ mService.setScreenUnlockedFunctions(functions);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the current screen unlocked functions.
+ *
+ * @return The currently set screen enabled functions.
+ * A zero mask indicates that the screen unlocked functions feature is not enabled.
*
* {@hide}
*/
- public void setScreenUnlockedFunctions(String function) {
+ public long getScreenUnlockedFunctions() {
try {
- mService.setScreenUnlockedFunctions(function);
+ return mService.getScreenUnlockedFunctions();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -719,51 +854,71 @@ public class UsbManager {
}
}
- /** @hide */
- public static String addFunction(String functions, String function) {
- if (USB_FUNCTION_NONE.equals(functions)) {
- return function;
- }
- if (!containsFunction(functions, function)) {
- if (functions.length() > 0) {
- functions += ",";
- }
- functions += function;
- }
- return functions;
+ /**
+ * Returns whether the given functions are valid inputs to UsbManager.
+ * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
+ *
+ * @return Whether the mask is settable.
+ *
+ * {@hide}
+ */
+ public static boolean areSettableFunctions(long functions) {
+ return functions == FUNCTION_NONE
+ || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1);
}
- /** @hide */
- public static String removeFunction(String functions, String function) {
- String[] split = functions.split(",");
- for (int i = 0; i < split.length; i++) {
- if (function.equals(split[i])) {
- split[i] = null;
- }
+ /**
+ * Converts the given function mask to string. Maintains ordering with respect to init scripts.
+ *
+ * @return String representation of given mask
+ *
+ * {@hide}
+ */
+ public static String usbFunctionsToString(long functions) {
+ StringJoiner joiner = new StringJoiner(",");
+ if ((functions & FUNCTION_MTP) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_MTP);
}
- if (split.length == 1 && split[0] == null) {
- return USB_FUNCTION_NONE;
+ if ((functions & FUNCTION_PTP) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_PTP);
}
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < split.length; i++) {
- String s = split[i];
- if (s != null) {
- if (builder.length() > 0) {
- builder.append(",");
- }
- builder.append(s);
- }
+ if ((functions & FUNCTION_RNDIS) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_RNDIS);
+ }
+ if ((functions & FUNCTION_MIDI) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_MIDI);
}
- return builder.toString();
+ if ((functions & FUNCTION_ACCESSORY) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_ACCESSORY);
+ }
+ if ((functions & FUNCTION_AUDIO_SOURCE) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
+ }
+ if ((functions & FUNCTION_ADB) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_ADB);
+ }
+ return joiner.toString();
}
- /** @hide */
- public static boolean containsFunction(String functions, String function) {
- int index = functions.indexOf(function);
- if (index < 0) return false;
- if (index > 0 && functions.charAt(index - 1) != ',') return false;
- int charAfter = index + function.length();
- if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
- return true;
+ /**
+ * Parses a string of usb functions that are comma separated.
+ *
+ * @return A mask of all valid functions in the string
+ *
+ * {@hide}
+ */
+ public static long usbFunctionsFromString(String functions) {
+ if (functions == null || functions.equals(USB_FUNCTION_NONE)) {
+ return FUNCTION_NONE;
+ }
+ long ret = 0;
+ for (String function : functions.split(",")) {
+ if (FUNCTION_NAME_TO_CODE.containsKey(function)) {
+ ret |= FUNCTION_NAME_TO_CODE.get(function);
+ } else if (function.length() > 0) {
+ throw new IllegalArgumentException("Invalid usb function " + functions);
+ }
+ }
+ return ret;
}
}
diff --git a/android/hardware/usb/UsbRequest.java b/android/hardware/usb/UsbRequest.java
index 2e8f8e12..f59c87ee 100644
--- a/android/hardware/usb/UsbRequest.java
+++ b/android/hardware/usb/UsbRequest.java
@@ -17,6 +17,7 @@
package android.hardware.usb;
import android.annotation.Nullable;
+import android.os.Build;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -43,7 +44,7 @@ public class UsbRequest {
private static final String TAG = "UsbRequest";
// From drivers/usb/core/devio.c
- private static final int MAX_USBFS_BUFFER_SIZE = 16384;
+ static final int MAX_USBFS_BUFFER_SIZE = 16384;
// used by the JNI code
private long mNativeContext;
@@ -175,7 +176,9 @@ public class UsbRequest {
* capacity will be ignored. Once the request
* {@link UsbDeviceConnection#requestWait() is processed} the position will be set
* to the number of bytes read/written.
- * @param length number of bytes to read or write.
+ * @param length number of bytes to read or write. Before {@value Build.VERSION_CODES#P}, a
+ * value larger than 16384 bytes would be truncated down to 16384. In API
+ * {@value Build.VERSION_CODES#P} and after, any value of length is valid.
*
* @return true if the queueing operation succeeded
*
@@ -186,6 +189,11 @@ public class UsbRequest {
boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
boolean result;
+ if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+ && length > MAX_USBFS_BUFFER_SIZE) {
+ length = MAX_USBFS_BUFFER_SIZE;
+ }
+
synchronized (mLock) {
// save our buffer for when the request has completed
mBuffer = buffer;
@@ -222,7 +230,10 @@ public class UsbRequest {
* of the buffer is undefined until the request is returned by
* {@link UsbDeviceConnection#requestWait}. If the request failed the buffer
* will be unchanged; if the request succeeded the position of the buffer is
- * incremented by the number of bytes sent/received.
+ * incremented by the number of bytes sent/received. Before
+ * {@value Build.VERSION_CODES#P}, a buffer of length larger than 16384 bytes
+ * would throw IllegalArgumentException. In API {@value Build.VERSION_CODES#P}
+ * and after, any size buffer is valid.
*
* @return true if the queueing operation succeeded
*/
@@ -244,9 +255,12 @@ public class UsbRequest {
mIsUsingNewQueue = true;
wasQueued = native_queue(null, 0, 0);
} else {
- // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
- Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
- "number of remaining bytes");
+ if (mConnection.getContext().getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.P) {
+ // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
+ Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
+ "number of remaining bytes");
+ }
// Can not receive into read-only buffers.
Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be "