Merge "Camera: Disallow concurrent extension sessions" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
efb3fafeff
@@ -25,6 +25,7 @@ import android.graphics.ImageFormat;
|
||||
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
|
||||
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
import android.hardware.camera2.extension.IInitializeSessionCallback;
|
||||
import android.hardware.camera2.extension.IPreviewExtenderImpl;
|
||||
import android.hardware.camera2.extension.LatencyRange;
|
||||
import android.hardware.camera2.extension.SizeList;
|
||||
@@ -357,6 +358,27 @@ public final class CameraExtensionCharacteristics {
|
||||
}
|
||||
}
|
||||
|
||||
public void initializeSession(IInitializeSessionCallback cb) throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
if (mProxy != null) {
|
||||
mProxy.initializeSession(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseSession() {
|
||||
synchronized (mLock) {
|
||||
if (mProxy != null) {
|
||||
try {
|
||||
mProxy.releaseSession();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to release session! Extension service does"
|
||||
+ " not respond!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean areAdvancedExtensionsSupported() {
|
||||
return mSupportsAdvancedExtensions;
|
||||
}
|
||||
@@ -409,6 +431,20 @@ public final class CameraExtensionCharacteristics {
|
||||
CameraExtensionManagerGlobal.get().unregisterClient(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static void initializeSession(IInitializeSessionCallback cb) throws RemoteException {
|
||||
CameraExtensionManagerGlobal.get().initializeSession(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static void releaseSession() {
|
||||
CameraExtensionManagerGlobal.get().releaseSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -195,8 +195,9 @@ public abstract class CameraExtensionSession implements AutoCloseable {
|
||||
* This method is called if the session cannot be configured as requested.
|
||||
*
|
||||
* <p>This can happen if the set of requested outputs contains unsupported sizes,
|
||||
* too many outputs are requested at once or the camera device encounters an
|
||||
* unrecoverable error during configuration.</p>
|
||||
* too many outputs are requested at once or when trying to initialize multiple
|
||||
* concurrent extension sessions from two (or more) separate camera devices
|
||||
* or the camera device encounters an unrecoverable error during configuration.</p>
|
||||
*
|
||||
* <p>The session is considered to be closed, and all methods called on it after this
|
||||
* callback is invoked will throw an IllegalStateException.</p>
|
||||
|
||||
@@ -162,6 +162,9 @@ public final class CameraManager {
|
||||
* <p>The set of combinations may include camera devices that may be in use by other camera API
|
||||
* clients.</p>
|
||||
*
|
||||
* <p>Concurrent camera extension sessions {@link CameraExtensionSession} are not currently
|
||||
* supported.</p>
|
||||
*
|
||||
* <p>The set of combinations doesn't contain physical cameras that can only be used as
|
||||
* part of a logical multi-camera device.</p>
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.hardware.camera2.extension;
|
||||
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
|
||||
import android.hardware.camera2.extension.IPreviewExtenderImpl;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
import android.hardware.camera2.extension.IInitializeSessionCallback;
|
||||
|
||||
/** @hide */
|
||||
interface ICameraExtensionsProxyService
|
||||
@@ -25,6 +26,8 @@ interface ICameraExtensionsProxyService
|
||||
long registerClient();
|
||||
void unregisterClient(long clientId);
|
||||
boolean advancedExtensionsSupported();
|
||||
void initializeSession(in IInitializeSessionCallback cb);
|
||||
void releaseSession();
|
||||
@nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType);
|
||||
@nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType);
|
||||
@nullable IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2021, 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.extension;
|
||||
|
||||
/** @hide */
|
||||
interface IInitializeSessionCallback
|
||||
{
|
||||
void onSuccess();
|
||||
void onFailure();
|
||||
}
|
||||
@@ -35,9 +35,11 @@ import android.hardware.camera2.CaptureResult;
|
||||
import android.hardware.camera2.TotalCaptureResult;
|
||||
import android.hardware.camera2.extension.CameraOutputConfig;
|
||||
import android.hardware.camera2.extension.CameraSessionConfig;
|
||||
import android.hardware.camera2.extension.CaptureStageImpl;
|
||||
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
|
||||
import android.hardware.camera2.extension.ICaptureCallback;
|
||||
import android.hardware.camera2.extension.IImageProcessorImpl;
|
||||
import android.hardware.camera2.extension.IInitializeSessionCallback;
|
||||
import android.hardware.camera2.extension.IRequestCallback;
|
||||
import android.hardware.camera2.extension.IRequestProcessorImpl;
|
||||
import android.hardware.camera2.extension.ISessionProcessorImpl;
|
||||
@@ -89,6 +91,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
|
||||
private Surface mClientCaptureSurface;
|
||||
private CameraCaptureSession mCaptureSession = null;
|
||||
private ISessionProcessorImpl mSessionProcessor = null;
|
||||
private final InitializeSessionHandler mInitializeHandler;
|
||||
|
||||
private boolean mInitialized;
|
||||
|
||||
@@ -181,6 +184,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
|
||||
mHandlerThread.start();
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
mInitialized = false;
|
||||
mInitializeHandler = new InitializeSessionHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,7 +448,6 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
|
||||
|
||||
public void release() {
|
||||
synchronized (mInterfaceLock) {
|
||||
mInitialized = false;
|
||||
mHandlerThread.quitSafely();
|
||||
|
||||
if (mSessionProcessor != null) {
|
||||
@@ -459,7 +462,11 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
|
||||
|
||||
if (mExtensionClientId >= 0) {
|
||||
CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
|
||||
if (mInitialized) {
|
||||
CameraExtensionCharacteristics.releaseSession();
|
||||
}
|
||||
}
|
||||
mInitialized = false;
|
||||
|
||||
for (ImageReader reader : mReaderMap.values()) {
|
||||
reader.close();
|
||||
@@ -512,9 +519,24 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
|
||||
|
||||
@Override
|
||||
public void onConfigured(@NonNull CameraCaptureSession session) {
|
||||
boolean status = true;
|
||||
synchronized (mInterfaceLock) {
|
||||
mCaptureSession = session;
|
||||
try {
|
||||
CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to initialize session! Extension service does"
|
||||
+ " not respond!");
|
||||
notifyConfigurationFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
boolean status = true;
|
||||
synchronized (mInterfaceLock) {
|
||||
try {
|
||||
mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
|
||||
mInitialized = true;
|
||||
@@ -522,7 +544,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
|
||||
Log.e(TAG, "Failed to start capture session,"
|
||||
+ " extension service does not respond!");
|
||||
status = false;
|
||||
session.close();
|
||||
mCaptureSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,6 +560,15 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
|
||||
notifyConfigurationFailure();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
mCaptureSession.close();
|
||||
Log.e(TAG, "Failed to initialize proxy service session!"
|
||||
+ " This can happen when trying to configure multiple "
|
||||
+ "concurrent extension sessions!");
|
||||
notifyConfigurationFailure();
|
||||
}
|
||||
}
|
||||
|
||||
private final class RequestCallbackHandler extends ICaptureCallback.Stub {
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.hardware.camera2.extension.CaptureBundle;
|
||||
import android.hardware.camera2.extension.CaptureStageImpl;
|
||||
import android.hardware.camera2.extension.ICaptureProcessorImpl;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
import android.hardware.camera2.extension.IInitializeSessionCallback;
|
||||
import android.hardware.camera2.extension.IPreviewExtenderImpl;
|
||||
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
|
||||
import android.hardware.camera2.extension.ParcelImage;
|
||||
@@ -48,6 +49,8 @@ import android.media.ImageWriter;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.annotation.NonNull;
|
||||
@@ -80,6 +83,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
private final HandlerThread mHandlerThread;
|
||||
private final StateCallback mCallbacks;
|
||||
private final List<Size> mSupportedPreviewSizes;
|
||||
private final InitializeSessionHandler mInitializeHandler;
|
||||
|
||||
private CameraCaptureSession mCaptureSession = null;
|
||||
private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface;
|
||||
@@ -216,6 +220,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
mHandlerThread.start();
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
mInitialized = false;
|
||||
mInitializeHandler = new InitializeSessionHandler();
|
||||
}
|
||||
|
||||
private void initializeRepeatingRequestPipeline() throws RemoteException {
|
||||
@@ -621,7 +626,6 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
public void release() {
|
||||
synchronized (mInterfaceLock) {
|
||||
mInternalRepeatingRequestEnabled = false;
|
||||
mInitialized = false;
|
||||
mHandlerThread.quitSafely();
|
||||
|
||||
try {
|
||||
@@ -634,7 +638,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
|
||||
if (mExtensionClientId >= 0) {
|
||||
CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
|
||||
if (mInitialized) {
|
||||
CameraExtensionCharacteristics.releaseSession();
|
||||
}
|
||||
}
|
||||
mInitialized = false;
|
||||
|
||||
if (mRepeatingRequestImageCallback != null) {
|
||||
mRepeatingRequestImageCallback.close();
|
||||
@@ -739,36 +747,63 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
|
||||
@Override
|
||||
public void onConfigured(@NonNull CameraCaptureSession session) {
|
||||
boolean status = true;
|
||||
synchronized (mInterfaceLock) {
|
||||
mCaptureSession = session;
|
||||
|
||||
ArrayList<CaptureStageImpl> initialRequestList = compileInitialRequestList();
|
||||
if (!initialRequestList.isEmpty()) {
|
||||
try {
|
||||
setInitialCaptureRequest(initialRequestList,
|
||||
new InitialRequestHandler(mRepeatingRequestImageCallback));
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG, "Failed to initialize the initial capture request!");
|
||||
status = false;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
setRepeatingRequest(mPreviewExtender.getCaptureStage(),
|
||||
new RepeatingRequestHandler(null, null, null,
|
||||
mRepeatingRequestImageCallback));
|
||||
} catch (CameraAccessException | RemoteException e) {
|
||||
Log.e(TAG, "Failed to initialize internal repeating request!");
|
||||
status = false;
|
||||
}
|
||||
|
||||
try {
|
||||
CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to initialize session! Extension service does"
|
||||
+ " not respond!");
|
||||
notifyConfigurationFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
boolean status = true;
|
||||
ArrayList<CaptureStageImpl> initialRequestList =
|
||||
compileInitialRequestList();
|
||||
if (!initialRequestList.isEmpty()) {
|
||||
try {
|
||||
setInitialCaptureRequest(initialRequestList,
|
||||
new InitialRequestHandler(
|
||||
mRepeatingRequestImageCallback));
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG,
|
||||
"Failed to initialize the initial capture "
|
||||
+ "request!");
|
||||
status = false;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
setRepeatingRequest(mPreviewExtender.getCaptureStage(),
|
||||
new RepeatingRequestHandler(null, null, null,
|
||||
mRepeatingRequestImageCallback));
|
||||
} catch (CameraAccessException | RemoteException e) {
|
||||
Log.e(TAG,
|
||||
"Failed to initialize internal repeating "
|
||||
+ "request!");
|
||||
status = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
notifyConfigurationFailure();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
mCaptureSession.close();
|
||||
Log.e(TAG, "Failed to initialize proxy service session!"
|
||||
+ " This can happen when trying to configure multiple "
|
||||
+ "concurrent extension sessions!");
|
||||
notifyConfigurationFailure();
|
||||
}
|
||||
}
|
||||
|
||||
private class BurstRequestHandler extends CameraCaptureSession.CaptureCallback {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.cameraextensions;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
@@ -42,6 +43,7 @@ import android.hardware.camera2.extension.IRequestProcessorImpl;
|
||||
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
import android.hardware.camera2.extension.IImageProcessorImpl;
|
||||
import android.hardware.camera2.extension.IInitializeSessionCallback;
|
||||
import android.hardware.camera2.extension.ISessionProcessorImpl;
|
||||
import android.hardware.camera2.extension.LatencyRange;
|
||||
import android.hardware.camera2.extension.OutputConfigId;
|
||||
@@ -57,6 +59,7 @@ import android.hardware.HardwareBuffer;
|
||||
import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
|
||||
import android.media.Image;
|
||||
import android.media.ImageReader;
|
||||
import android.os.Binder;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
@@ -176,6 +179,7 @@ public class CameraExtensionsProxyService extends Service {
|
||||
|
||||
private long mCurrentClientCount = 0;
|
||||
private ArraySet<Long> mActiveClients = new ArraySet<>();
|
||||
private IInitializeSessionCallback mInitializeCb = null;
|
||||
|
||||
// Singleton instance
|
||||
private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
|
||||
@@ -328,6 +332,40 @@ public class CameraExtensionsProxyService extends Service {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
|
||||
@Override
|
||||
public void binderDied() {
|
||||
synchronized (mLock) {
|
||||
mInitializeCb = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public boolean initializeSession(IInitializeSessionCallback cb) {
|
||||
synchronized (mLock) {
|
||||
if (mInitializeCb == null) {
|
||||
mInitializeCb = cb;
|
||||
try {
|
||||
mInitializeCb.asBinder().linkToDeath(mDeathRecipient, 0);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void releaseSession() {
|
||||
synchronized (mLock) {
|
||||
if (mInitializeCb != null) {
|
||||
mInitializeCb.asBinder().unlinkToDeath(mDeathRecipient, 0);
|
||||
mInitializeCb = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,6 +388,26 @@ public class CameraExtensionsProxyService extends Service {
|
||||
CameraExtensionManagerGlobal.get().unregisterClient(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static boolean initializeSession(IInitializeSessionCallback cb) {
|
||||
if (!EXTENSIONS_PRESENT) {
|
||||
return false;
|
||||
}
|
||||
return CameraExtensionManagerGlobal.get().initializeSession(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static void releaseSession() {
|
||||
if (!EXTENSIONS_PRESENT) {
|
||||
return;
|
||||
}
|
||||
CameraExtensionManagerGlobal.get().releaseSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -538,6 +596,39 @@ public class CameraExtensionsProxyService extends Service {
|
||||
CameraExtensionsProxyService.unregisterClient(clientId);
|
||||
}
|
||||
|
||||
private boolean checkCameraPermission() {
|
||||
int allowed = CameraExtensionsProxyService.this.checkPermission(
|
||||
android.Manifest.permission.CAMERA, Binder.getCallingPid(),
|
||||
Binder.getCallingUid());
|
||||
return (PackageManager.PERMISSION_GRANTED == allowed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeSession(IInitializeSessionCallback cb) {
|
||||
try {
|
||||
if (!checkCameraPermission()) {
|
||||
Log.i(TAG, "Camera permission required for initializing capture session");
|
||||
cb.onFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
if (CameraExtensionsProxyService.initializeSession(cb)) {
|
||||
cb.onSuccess();
|
||||
} else {
|
||||
cb.onFailure();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Client doesn't respond!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSession() {
|
||||
if (checkCameraPermission()) {
|
||||
CameraExtensionsProxyService.releaseSession();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advancedExtensionsSupported() {
|
||||
return ADVANCED_API_SUPPORTED;
|
||||
|
||||
Reference in New Issue
Block a user