Camera: Disallow concurrent extension sessions
Support for concurrent extension processing sessions is not yet mandated. To avoid inconsistent behavior across devices, explicitly document and disallow concurrent extension sessions at the framework level. Bug: 187341271 Test: Manual using test application, Camera CTS Change-Id: Ibff194c58bbb37e884c27151a2a27fb21e3db928
This commit is contained in:
@@ -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