diff options
author | Igor Murashkin <iam@google.com> | 2013-06-25 20:27:06 +0000 |
---|---|---|
committer | Igor Murashkin <iam@google.com> | 2013-06-26 13:19:45 -0700 |
commit | 70725500dcf3b666b43d50563d64705aab58d2d3 (patch) | |
tree | ad2d6206c590e11c4b86e871c138f2aabd822956 /core/java/android/hardware | |
parent | e363fbb2647aeb5ef4c87160d84c6b9ae8d45598 (diff) | |
download | frameworks_base-70725500dcf3b666b43d50563d64705aab58d2d3.zip frameworks_base-70725500dcf3b666b43d50563d64705aab58d2d3.tar.gz frameworks_base-70725500dcf3b666b43d50563d64705aab58d2d3.tar.bz2 |
Initial camera device implementation
* Working streaming preview requests only
* Almost everything else returns empty objects that don't do anything
Bug: 9213377
Change-Id: Ie6f02a7c0952b0f5ebc41905425b15cae221f7d3
Diffstat (limited to 'core/java/android/hardware')
15 files changed, 690 insertions, 69 deletions
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl index b8fbfdb..2d0c099 100644 --- a/core/java/android/hardware/ICameraService.aidl +++ b/core/java/android/hardware/ICameraService.aidl @@ -20,6 +20,8 @@ import android.hardware.ICamera; import android.hardware.ICameraClient; import android.hardware.IProCameraUser; import android.hardware.IProCameraCallbacks; +import android.hardware.photography.ICameraDeviceUser; +import android.hardware.photography.ICameraDeviceCallbacks; import android.hardware.ICameraServiceListener; import android.hardware.CameraInfo; @@ -43,6 +45,10 @@ interface ICameraService String clientPackageName, int clientUid); + ICameraDeviceUser connectDevice(ICameraDeviceCallbacks callbacks, int cameraId, + String clientPackageName, + int clientUid); + int addListener(ICameraServiceListener listener); int removeListener(ICameraServiceListener listener); } diff --git a/core/java/android/hardware/photography/CameraDevice.java b/core/java/android/hardware/photography/CameraDevice.java index e94e3a1..5fb14dc 100644 --- a/core/java/android/hardware/photography/CameraDevice.java +++ b/core/java/android/hardware/photography/CameraDevice.java @@ -16,11 +16,6 @@ package android.hardware.photography; -import android.graphics.ImageFormat; -import android.os.IBinder; -import android.renderscript.Allocation; -import android.renderscript.RenderScript; -import android.util.Log; import android.view.Surface; import java.lang.AutoCloseable; @@ -49,7 +44,7 @@ import java.util.List; * @see CameraManager#openCamera * @see android.Manifest.permission#CAMERA */ -public final class CameraDevice implements AutoCloseable { +public interface CameraDevice extends AutoCloseable { /** * Create a request suitable for a camera preview window. Specifically, this @@ -103,8 +98,6 @@ public final class CameraDevice implements AutoCloseable { */ public static final int TEMPLATE_MANUAL = 5; - private static final String TAG = "CameraDevice"; - /** * Get the static properties for this camera. These are identical to the * properties returned by {@link CameraManager#getCameraProperties}. @@ -115,10 +108,7 @@ public final class CameraDevice implements AutoCloseable { * * @see CameraManager#getCameraProperties */ - public CameraProperties getProperties() throws CameraAccessException { - return null; - } - + public CameraProperties getProperties() throws CameraAccessException; /** * <p>Set up a new output set of Surfaces for the camera device.</p> * @@ -200,8 +190,7 @@ public final class CameraDevice implements AutoCloseable { * @throws IllegalStateException if the camera device is not idle, or has * encountered a fatal error */ - public void configureOutputs(List<Surface> outputs) { - } + public void configureOutputs(List<Surface> outputs) throws CameraAccessException; /** * <p>Create a {@link CaptureRequest} initialized with template for a target @@ -227,9 +216,7 @@ public final class CameraDevice implements AutoCloseable { * @see #TEMPLATE_MANUAL */ public CaptureRequest createCaptureRequest(int templateType) - throws CameraAccessException { - return null; - } + throws CameraAccessException; /** * <p>Submit a request for an image to be captured by this CameraDevice.</p> @@ -261,8 +248,7 @@ public final class CameraDevice implements AutoCloseable { * @see #setRepeatingBurst */ public void capture(CaptureRequest request, CaptureListener listener) - throws CameraAccessException { - } + throws CameraAccessException; /** * <p>Submit a list of requests to be captured in sequence as a burst. The @@ -293,8 +279,7 @@ public final class CameraDevice implements AutoCloseable { * @see #setRepeatingBurst */ public void captureBurst(List<CaptureRequest> requests, - CaptureListener listener) throws CameraAccessException { - } + CaptureListener listener) throws CameraAccessException; /** * <p>Request endlessly repeating capture of images by this @@ -336,8 +321,7 @@ public final class CameraDevice implements AutoCloseable { * @see #setRepeatingBurst */ public void setRepeatingRequest(CaptureRequest request, CaptureListener listener) - throws CameraAccessException { - } + throws CameraAccessException; /** * <p>Request endlessly repeating capture of a sequence of images by this @@ -381,8 +365,7 @@ public final class CameraDevice implements AutoCloseable { * @see #setRepeatingRequest */ public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener) - throws CameraAccessException { - } + throws CameraAccessException; /** * <p>Cancel any ongoing repeating capture set by either @@ -408,8 +391,7 @@ public final class CameraDevice implements AutoCloseable { * device has encountered a fatal error, or if there is an active repeating * request or burst. */ - public void stopRepeating() throws CameraAccessException { - } + public void stopRepeating() throws CameraAccessException; /** * <p>Wait until all the submitted requests have finished processing</p> @@ -434,8 +416,7 @@ public final class CameraDevice implements AutoCloseable { * device has encountered a fatal error, or if there is an active repeating * request or burst. */ - public void waitUntilIdle() throws CameraAccessException { - } + public void waitUntilIdle() throws CameraAccessException; /** * Set the error listener object to call when an asynchronous error @@ -447,17 +428,17 @@ public final class CameraDevice implements AutoCloseable { * notifications to. Setting this to null will stop notifications about * asynchronous errors. */ - public void setErrorListener(ErrorListener listener) { - } + public void setErrorListener(ErrorListener listener); /** * Close the connection to this camera device. After this call, all calls to * the camera device interface will throw a {@link IllegalStateException}, * except for calls to close(). + * @throws Exception */ @Override - public void close() { - } + public void close() throws Exception; + // TODO: We should decide on the behavior of in-flight requests should be on close. /** * A listener for receiving metadata about completed image captures. The @@ -556,12 +537,4 @@ public final class CameraDevice implements AutoCloseable { */ public void onCameraDeviceError(CameraDevice camera, int error); } - - /** - * @hide - */ - public CameraDevice(IBinder binder) { - Log.e(TAG, "CameraDevice constructor not implemented yet"); - } - } diff --git a/core/java/android/hardware/photography/CameraManager.java b/core/java/android/hardware/photography/CameraManager.java index 115f151..c1c9435 100644 --- a/core/java/android/hardware/photography/CameraManager.java +++ b/core/java/android/hardware/photography/CameraManager.java @@ -94,7 +94,13 @@ public final class CameraManager { */ public String[] getDeviceIdList() throws CameraAccessException { synchronized (mLock) { - return (String[]) getOrCreateDeviceIdListLocked().toArray(); + try { + return getOrCreateDeviceIdListLocked().toArray(new String[0]); + } catch(CameraAccessException e) { + // this should almost never happen, except if mediaserver crashes + throw new IllegalStateException( + "Failed to query camera service for device ID list", e); + } } } @@ -179,17 +185,32 @@ public final class CameraManager { public CameraDevice openCamera(String cameraId) throws CameraAccessException { try { - IProCameraUser cameraUser; synchronized (mLock) { - // TODO: Use ICameraDevice or some such instead of this... - cameraUser = mCameraService.connectPro(null, + + ICameraDeviceUser cameraUser; + + android.hardware.photography.impl.CameraDevice device = + new android.hardware.photography.impl.CameraDevice(cameraId); + + cameraUser = mCameraService.connectDevice(device.getCallbacks(), Integer.parseInt(cameraId), mContext.getPackageName(), USE_CALLING_UID); + // TODO: change ICameraService#connectDevice to return status_t + if (cameraUser == null) { + // TEMPORARY CODE. + // catch-all exception since we aren't yet getting the actual error code + throw new IllegalStateException("Failed to open camera device"); + } + + // TODO: factor out listener to be non-nested, then move setter to constructor + device.setRemoteDevice(cameraUser); + + return device; + } - return new CameraDevice(cameraUser.asBinder()); } catch (NumberFormatException e) { throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: " + cameraId); @@ -306,7 +327,7 @@ public final class CameraManager { @Override public void onStatusChanged(int status, int cameraId) throws RemoteException { - synchronized(CameraManager.this) { + synchronized(CameraManager.this.mLock) { Log.v(TAG, String.format("Camera id %d has status changed to 0x%x", cameraId, status)); diff --git a/core/java/android/hardware/photography/CameraMetadata.aidl b/core/java/android/hardware/photography/CameraMetadata.aidl new file mode 100644 index 0000000..b4dc9ac --- /dev/null +++ b/core/java/android/hardware/photography/CameraMetadata.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2013 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.photography; + +/** @hide */ +parcelable CameraMetadata; diff --git a/core/java/android/hardware/photography/CameraMetadata.java b/core/java/android/hardware/photography/CameraMetadata.java index 385a1b9..5488952 100644 --- a/core/java/android/hardware/photography/CameraMetadata.java +++ b/core/java/android/hardware/photography/CameraMetadata.java @@ -18,6 +18,8 @@ package android.hardware.photography; import android.os.Parcelable; import android.os.Parcel; +import android.util.Log; + import java.util.HashMap; import java.util.Map; @@ -32,27 +34,34 @@ import java.util.Map; * @see CameraManager * @see CameraProperties **/ -public class CameraMetadata implements Parcelable { +public class CameraMetadata implements Parcelable, AutoCloseable { public CameraMetadata() { mMetadataMap = new HashMap<Key<?>, Object>(); - } - - private CameraMetadata(Parcel in) { + mMetadataPtr = nativeAllocate(); + if (mMetadataPtr == 0) { + throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); + } } public static final Parcelable.Creator<CameraMetadata> CREATOR = new Parcelable.Creator<CameraMetadata>() { + @Override public CameraMetadata createFromParcel(Parcel in) { - return new CameraMetadata(in); + CameraMetadata metadata = new CameraMetadata(); + metadata.readFromParcel(in); + return metadata; } + @Override public CameraMetadata[] newArray(int size) { return new CameraMetadata[size]; } }; + private static final String TAG = "CameraMetadataJV"; + /** * Set a camera metadata field to a value. The field definitions can be * found in {@link CameraProperties}, {@link CaptureResult}, and @@ -63,6 +72,8 @@ public class CameraMetadata implements Parcelable { * type to the key. */ public <T> void set(Key<T> key, T value) { + Log.e(TAG, "Not fully implemented yet"); + mMetadataMap.put(key, value); } @@ -76,6 +87,8 @@ public class CameraMetadata implements Parcelable { */ @SuppressWarnings("unchecked") public <T> T get(Key<T> key) { + Log.e(TAG, "Not fully implemented yet"); + return (T) mMetadataMap.get(key); } @@ -86,7 +99,15 @@ public class CameraMetadata implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + nativeWriteToParcel(dest); + } + /** + * Expand this object from a Parcel. + * @param in The Parcel from which the object should be read + */ + public void readFromParcel(Parcel in) { + nativeReadFromParcel(in); } public static class Key<T> { @@ -125,5 +146,88 @@ public class CameraMetadata implements Parcelable { private final String mName; } - private Map<Key<?>, Object> mMetadataMap; + private final Map<Key<?>, Object> mMetadataMap; + + /** + * @hide + */ + private long mMetadataPtr; // native CameraMetadata* + + private native long nativeAllocate(); + private native synchronized void nativeWriteToParcel(Parcel dest); + private native synchronized void nativeReadFromParcel(Parcel source); + private native synchronized void nativeSwap(CameraMetadata other) throws NullPointerException; + private native synchronized void nativeClose(); + private native synchronized boolean nativeIsEmpty(); + private native synchronized int nativeGetEntryCount(); + private static native void nativeClassInit(); + + /** + * <p>Perform a 0-copy swap of the internal metadata with another object.</p> + * + * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p> + * + * @param other metadata to swap with + * @throws NullPointerException if other was null + * @hide + */ + public void swap(CameraMetadata other) { + nativeSwap(other); + } + + /** + * @hide + */ + public int getEntryCount() { + return nativeGetEntryCount(); + } + + /** + * Does this metadata contain at least 1 entry? + * + * @hide + */ + public boolean isEmpty() { + return nativeIsEmpty(); + } + + /** + * <p>Closes this object, and releases all native resources associated with it.</p> + * + * <p>Calling any other public method after this will result in an IllegalStateException + * being thrown.</p> + */ + @Override + public void close() throws Exception { + // this sets mMetadataPtr to 0 + nativeClose(); + mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final + } + + /** + * Whether or not {@link #close} has already been called (at least once) on this object. + * @hide + */ + public boolean isClosed() { + synchronized (this) { + return mMetadataPtr == 0; + } + } + + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * We use a class initializer to allow the native code to cache some field offsets + */ + static { + System.loadLibrary("media_jni"); + nativeClassInit(); + } }
\ No newline at end of file diff --git a/core/java/android/hardware/photography/CameraProperties.java b/core/java/android/hardware/photography/CameraProperties.java index 1bfd712..ad42285 100644 --- a/core/java/android/hardware/photography/CameraProperties.java +++ b/core/java/android/hardware/photography/CameraProperties.java @@ -39,7 +39,7 @@ public final class CameraProperties extends CameraMetadata { * {@link #INFO_IDENTIFIER} can be used to distinguish between multiple * removable cameras of the same model. */ - public static final Key INFO_MODEL = + public static final Key<String> INFO_MODEL = new Key<String>("android.info.model"); /** @@ -48,7 +48,7 @@ public final class CameraProperties extends CameraMetadata { * same model and manufacturer. For non-removable cameras, the * identifier is equal to the the device's id. */ - public static final Key INFO_IDENTIFIER = + public static final Key<String> INFO_IDENTIFIER = new Key<String>("android.info.identifier"); /** @@ -58,7 +58,7 @@ public final class CameraProperties extends CameraMetadata { * to be disconnected during use. Use the {@link #INFO_IDENTIFIER} field to * determine if this camera is a match for a camera device seen earlier.</p> */ - public static final Key INFO_REMOVABLE = + public static final Key<Boolean> INFO_REMOVABLE = new Key<Boolean>("android.info.isRemovable"); /** @@ -99,7 +99,7 @@ public final class CameraProperties extends CameraMetadata { * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL */ - public static final Key INFO_SUPPORTED_HARDWARE_LEVEL = + public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL = new Key<Integer>("android.info.supportedHardwareLevel"); /** @@ -164,7 +164,7 @@ public final class CameraProperties extends CameraMetadata { * {@link android.graphics.ImageFormat#YUV_420_888} are guaranteed to be * supported.</p> */ - public static final Key SCALER_AVAILABLE_FORMATS = + public static final Key<Integer[]> SCALER_AVAILABLE_FORMATS = new Key<Integer[]>("android.scaler.availableFormats"); /** @@ -173,7 +173,7 @@ public final class CameraProperties extends CameraMetadata { * target, the ImageReader must be configured to use one of these sizes * when using format {@link android.graphics.ImageFormat#JPEG}.</p> */ - public static final Key SCALER_AVAILABLE_JPEG_SIZES = + public static final Key<Size[]> SCALER_AVAILABLE_JPEG_SIZES = new Key<Size[]>("android.scaler.availableJpegSizes"); /** @@ -193,7 +193,7 @@ public final class CameraProperties extends CameraMetadata { * when using format {@link android.graphics.ImageFormat#YUV_420_888}.</p> * */ - public static final Key SCALER_AVAILABLE_PROCESSED_SIZES = + public static final Key<Size[]> SCALER_AVAILABLE_PROCESSED_SIZES = new Key<Size[]>("android.scaler.availableProcessedSizes"); /** @@ -206,7 +206,7 @@ public final class CameraProperties extends CameraMetadata { * target, the ImageReader must be configured to use one of these sizes * when using image format {@link android.graphics.ImageFormat#RAW_SENSOR}.</p> */ - public static final Key SCALER_AVAILABLE_RAW_SIZES = + public static final Key<Size[]> SCALER_AVAILABLE_RAW_SIZES = new Key<Size[]>("android.scaler.availableRawSizes"); /** @@ -229,7 +229,7 @@ public final class CameraProperties extends CameraMetadata { * a coordinate system based on the active array dimensions, with (0,0) * being the top-left corner of the active array.</p> */ - public static final Key SENSOR_ACTIVE_ARRAY_SIZE = + public static final Key<Rect> SENSOR_ACTIVE_ARRAY_SIZE = new Key<Rect>("android.sensor.activeArraySize"); /** @@ -239,7 +239,7 @@ public final class CameraProperties extends CameraMetadata { * this. If raw sensor capture is supported by this device, this is one of * the supported capture sizes.</p> */ - public static final Key SENSOR_PIXEL_ARRAY_SIZE = + public static final Key<Size> SENSOR_PIXEL_ARRAY_SIZE = new Key<Size>("android.sensor.activeArraySize"); // TODO: Many more of these. diff --git a/core/java/android/hardware/photography/CaptureRequest.aidl b/core/java/android/hardware/photography/CaptureRequest.aidl new file mode 100644 index 0000000..64fb6f2 --- /dev/null +++ b/core/java/android/hardware/photography/CaptureRequest.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2013 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.photography; + +/** @hide */ +parcelable CaptureRequest; diff --git a/core/java/android/hardware/photography/CaptureRequest.java b/core/java/android/hardware/photography/CaptureRequest.java index a54c743..ac2041b 100644 --- a/core/java/android/hardware/photography/CaptureRequest.java +++ b/core/java/android/hardware/photography/CaptureRequest.java @@ -16,9 +16,11 @@ package android.hardware.photography; +import android.os.Parcel; +import android.os.Parcelable; import android.view.Surface; -import java.util.List; +import java.util.HashSet; /** @@ -47,24 +49,30 @@ import java.util.List; * @see CameraDevice#setRepeatingRequest * @see CameraDevice#createRequest */ -public final class CaptureRequest extends CameraMetadata { +public final class CaptureRequest extends CameraMetadata implements Parcelable { + + private final Object mLock = new Object(); + private final HashSet<Surface> mSurfaceSet = new HashSet<Surface>(); /** * The exposure time for this capture, in nanoseconds. */ - public static final Key SENSOR_EXPOSURE_TIME = + public static final Key<Long> SENSOR_EXPOSURE_TIME = new Key<Long>("android.sensor.exposureTime"); /** * The sensor sensitivity (gain) setting for this camera. * This is represented as an ISO sensitivity value */ - public static final Key SENSOR_SENSITIVITY = + public static final Key<Integer> SENSOR_SENSITIVITY = new Key<Integer>("android.sensor.sensitivity"); // Many more settings - CaptureRequest() { + /** + * @hide + */ + public CaptureRequest() { } /** @@ -72,14 +80,76 @@ public final class CaptureRequest extends CameraMetadata { * * <p>The Surface added must be one of the surfaces included in the last * call to {@link CameraDevice#configureOutputs}.</p> + * + * <p>Adding a target more than once has no effect.</p> + * + * @param outputTarget surface to use as an output target for this request */ public void addTarget(Surface outputTarget) { + synchronized (mLock) { + mSurfaceSet.add(outputTarget); + } } /** * <p>Remove a surface from the list of targets for this request.</p> + * + * <p>Removing a target that is not currently added has no effect.</p> + * + * @param outputTarget surface to use as an output target for this request */ public void removeTarget(Surface outputTarget) { + synchronized (mLock) { + mSurfaceSet.remove(outputTarget); + } + } + + public static final Parcelable.Creator<CaptureRequest> CREATOR = + new Parcelable.Creator<CaptureRequest>() { + @Override + public CaptureRequest createFromParcel(Parcel in) { + CaptureRequest request = new CaptureRequest(); + request.readFromParcel(in); + return request; + } + + @Override + public CaptureRequest[] newArray(int size) { + return new CaptureRequest[size]; + } + }; + + /** + * Expand this object from a Parcel. + * @param in The parcel from which the object should be read + */ + @Override + public void readFromParcel(Parcel in) { + synchronized (mLock) { + super.readFromParcel(in); + + mSurfaceSet.clear(); + + Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader()); + + if (parcelableArray == null) { + return; + } + + for (Parcelable p : parcelableArray) { + Surface s = (Surface) p; + mSurfaceSet.add(s); + } + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + synchronized (mLock) { + super.writeToParcel(dest, flags); + + dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags); + } } }
\ No newline at end of file diff --git a/core/java/android/hardware/photography/CaptureResult.java b/core/java/android/hardware/photography/CaptureResult.java index dd36f1d..b502c4c 100644 --- a/core/java/android/hardware/photography/CaptureResult.java +++ b/core/java/android/hardware/photography/CaptureResult.java @@ -101,7 +101,10 @@ public final class CaptureResult extends CameraMetadata { // TODO: Many many more - CaptureResult() { + /** + * @hide + */ + public CaptureResult() { } /** diff --git a/core/java/android/hardware/photography/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/photography/ICameraDeviceCallbacks.aidl new file mode 100644 index 0000000..c506800 --- /dev/null +++ b/core/java/android/hardware/photography/ICameraDeviceCallbacks.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 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.photography; + +import android.hardware.photography.CameraMetadata; + +/** @hide */ +interface ICameraDeviceCallbacks +{ + /** + * Keep up-to-date with frameworks/av/include/camera/photography/ICameraDeviceCallbacks.h + */ + + void notifyCallback(int msgType, int ext1, int ext2); + void onResultReceived(int frameId, in CameraMetadata result); +} diff --git a/core/java/android/hardware/photography/ICameraDeviceUser.aidl b/core/java/android/hardware/photography/ICameraDeviceUser.aidl new file mode 100644 index 0000000..d1310fb --- /dev/null +++ b/core/java/android/hardware/photography/ICameraDeviceUser.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 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.photography; + +import android.view.Surface; +import android.hardware.photography.CameraMetadata; +import android.hardware.photography.CaptureRequest; + +/** @hide */ +interface ICameraDeviceUser +{ + /** + * Keep up-to-date with frameworks/av/include/camera/photography/ICameraDeviceUser.h + */ + void disconnect(); + + // ints here are status_t + + // non-negative value is the requestId. negative value is status_t + int submitRequest(in CaptureRequest request, boolean streaming); + + int cancelRequest(int requestId); + + int deleteStream(int streamId); + + // non-negative value is the stream ID. negative value is status_t + int createStream(int width, int height, int format, in Surface surface); + + int createDefaultRequest(int templateId, out CameraMetadata request); +} diff --git a/core/java/android/hardware/photography/impl/CameraDevice.java b/core/java/android/hardware/photography/impl/CameraDevice.java new file mode 100644 index 0000000..b1e3f6a --- /dev/null +++ b/core/java/android/hardware/photography/impl/CameraDevice.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2013 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.photography.impl; + +import android.hardware.photography.CameraMetadata; +import android.hardware.photography.CaptureResult; +import android.hardware.photography.ICameraDeviceUser; +import android.hardware.photography.ICameraDeviceCallbacks; +import android.hardware.photography.CameraAccessException; +import android.hardware.photography.CameraProperties; +import android.hardware.photography.CaptureRequest; +import android.hardware.photography.utils.CameraRuntimeException; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.Surface; + +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +/** + * HAL2.1+ implementation of CameraDevice Use CameraManager#open to instantiate + */ +public class CameraDevice implements android.hardware.photography.CameraDevice { + + private final String TAG; + + // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) + private ICameraDeviceUser mRemoteDevice; + + private final Object mLock = new Object(); + private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); + + // XX: Make this a WeakReference<CaptureListener> ? + private final HashMap<Integer, CaptureListenerHolder> mCaptureListenerMap = + new HashMap<Integer, CaptureListenerHolder>(); + + private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>(); + + private final String mCameraId; + + public CameraDevice(String cameraId) { + mCameraId = cameraId; + TAG = String.format("CameraDevice-%s-JV", mCameraId); + } + + public CameraDeviceCallbacks getCallbacks() { + return mCallbacks; + } + + /** + * @hide + */ + public void setRemoteDevice(ICameraDeviceUser remoteDevice) { + mRemoteDevice = remoteDevice; + } + + @Override + public CameraProperties getProperties() throws CameraAccessException { + // TODO + Log.v(TAG, "TODO: Implement getProperties"); + return new CameraProperties(); + } + + @Override + public void configureOutputs(List<Surface> outputs) throws CameraAccessException { + synchronized (mLock) { + // TODO: delete outputs that aren't in this list that were configured previously + for (Surface s : outputs) { + try { + // TODO: remove width,height,format since we are ignoring + // it. + mRemoteDevice.createStream(0, 0, 0, s); + } catch (CameraRuntimeException e) { + throw e.asChecked(); + } catch (RemoteException e) { + // impossible + return; + } + } + } + } + + @Override + public CaptureRequest createCaptureRequest(int templateType) throws CameraAccessException { + + synchronized (mLock) { + + CameraMetadata templatedRequest = new CameraMetadata(); + + try { + mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest); + } catch (CameraRuntimeException e) { + throw e.asChecked(); + } catch (RemoteException e) { + // impossible + return null; + } + + CaptureRequest request = new CaptureRequest(); + + // XX: could also change binder signature but that's more work than + // just using swap. + request.swap(templatedRequest); + + return request; + + } + } + + @Override + public void capture(CaptureRequest request, CaptureListener listener) + throws CameraAccessException { + submitCaptureRequest(request, listener, /*streaming*/false); + } + + @Override + public void captureBurst(List<CaptureRequest> requests, CaptureListener listener) + throws CameraAccessException { + // TODO + throw new UnsupportedOperationException("Burst capture implemented yet"); + + } + + private void submitCaptureRequest(CaptureRequest request, CaptureListener listener, + boolean repeating) throws CameraAccessException { + + synchronized (mLock) { + + int requestId; + + try { + requestId = mRemoteDevice.submitRequest(request, repeating); + } catch (CameraRuntimeException e) { + throw e.asChecked(); + } catch (RemoteException e) { + // impossible + return; + } + + mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request, + repeating)); + + if (repeating) { + mRepeatingRequestIdStack.add(requestId); + } + + } + } + + @Override + public void setRepeatingRequest(CaptureRequest request, CaptureListener listener) + throws CameraAccessException { + submitCaptureRequest(request, listener, /*streaming*/true); + } + + @Override + public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener) + throws CameraAccessException { + // TODO + throw new UnsupportedOperationException("Burst capture implemented yet"); + } + + @Override + public void stopRepeating() throws CameraAccessException { + + synchronized (mLock) { + + while (!mRepeatingRequestIdStack.isEmpty()) { + int requestId = mRepeatingRequestIdStack.pop(); + + try { + mRemoteDevice.cancelRequest(requestId); + } catch (CameraRuntimeException e) { + throw e.asChecked(); + } catch (RemoteException e) { + // impossible + return; + } + } + } + } + + @Override + public void waitUntilIdle() throws CameraAccessException { + // TODO: implement + } + + @Override + public void setErrorListener(ErrorListener listener) { + // TODO Auto-generated method stub + + } + + @Override + public void close() throws Exception { + + // TODO: every method should throw IllegalStateException after close has been called + + synchronized (mLock) { + + try { + mRemoteDevice.disconnect(); + } catch (CameraRuntimeException e) { + throw e.asChecked(); + } catch (RemoteException e) { + // impossible + } + + mRemoteDevice = null; + + } + } + + @Override + protected void finalize() throws Throwable { + try { + close(); + } catch (CameraRuntimeException e) { + Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage()); + } + finally { + super.finalize(); + } + } + + static class CaptureListenerHolder { + + private final boolean mRepeating; + private final CaptureListener mListener; + private final CaptureRequest mRequest; + + CaptureListenerHolder(CaptureListener listener, CaptureRequest request, boolean repeating) { + mRepeating = repeating; + mRequest = request; + mListener = listener; + } + + public boolean isRepeating() { + return mRepeating; + } + + public CaptureListener getListener() { + return mListener; + } + + public CaptureRequest getRequest() { + return mRequest; + } + } + + // TODO: unit tests + public class CameraDeviceCallbacks extends Binder implements ICameraDeviceCallbacks { + + @Override + public IBinder asBinder() { + return this; + } + + // TODO: consider rename to onMessageReceived + @Override + public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException { + Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2); + // TODO implement rest + } + + @Override + public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException { + Log.d(TAG, "Received result for frameId " + frameId); + + CaptureListenerHolder holder; + + synchronized (mLock) { + // TODO: move this whole map into this class to make it more testable, + // exposing the methods necessary like subscribeToRequest, unsubscribe.. + // TODO: make class static class + + holder = CameraDevice.this.mCaptureListenerMap.get(frameId); + + // Clean up listener once we no longer expect to see it. + + // TODO: how to handle repeating listeners? + // we probably want cancelRequest to return # of times it already enqueued and + // keep a counter. + if (holder != null && !holder.isRepeating()) { + CameraDevice.this.mCaptureListenerMap.remove(frameId); + } + } + + if (holder == null) { + Log.e(TAG, "Result had no listener holder associated with it, dropping result"); + return; + } + + CaptureResult resultAsCapture = new CaptureResult(); + resultAsCapture.swap(result); + + if (holder.getListener() != null) { + holder.getListener().onCaptureComplete(CameraDevice.this, holder.getRequest(), + resultAsCapture); + } + } + + } + +} diff --git a/core/java/android/hardware/photography/impl/package.html b/core/java/android/hardware/photography/impl/package.html new file mode 100644 index 0000000..783d0a1 --- /dev/null +++ b/core/java/android/hardware/photography/impl/package.html @@ -0,0 +1,3 @@ +<body> +{@hide} +</body> diff --git a/core/java/android/hardware/photography/utils/CameraBinderDecorator.java b/core/java/android/hardware/photography/utils/CameraBinderDecorator.java index 99e7c78..1a44b97f 100644 --- a/core/java/android/hardware/photography/utils/CameraBinderDecorator.java +++ b/core/java/android/hardware/photography/utils/CameraBinderDecorator.java @@ -67,6 +67,8 @@ public class CameraBinderDecorator { case PERMISSION_DENIED: throw new SecurityException("Lacking privileges to access camera service"); case ALREADY_EXISTS: + // This should be handled at the call site. Typically this isn't bad, + // just means we tried to do an operation that already completed. return; case BAD_VALUE: throw new IllegalArgumentException("Bad argument passed to camera service"); diff --git a/core/java/android/hardware/photography/utils/package.html b/core/java/android/hardware/photography/utils/package.html new file mode 100644 index 0000000..783d0a1 --- /dev/null +++ b/core/java/android/hardware/photography/utils/package.html @@ -0,0 +1,3 @@ +<body> +{@hide} +</body> |