Merge "Camera: Disallow concurrent extension sessions" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-06-28 18:31:16 +00:00
committed by Android (Google) Code Review
8 changed files with 250 additions and 27 deletions

View File

@@ -25,6 +25,7 @@ import android.graphics.ImageFormat;
import android.hardware.camera2.extension.IAdvancedExtenderImpl; import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService; import android.hardware.camera2.extension.ICameraExtensionsProxyService;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl; import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl; import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.LatencyRange; import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.SizeList; 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() { public boolean areAdvancedExtensionsSupported() {
return mSupportsAdvancedExtensions; return mSupportsAdvancedExtensions;
} }
@@ -409,6 +431,20 @@ public final class CameraExtensionCharacteristics {
CameraExtensionManagerGlobal.get().unregisterClient(clientId); 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 * @hide
*/ */

View File

@@ -195,8 +195,9 @@ public abstract class CameraExtensionSession implements AutoCloseable {
* This method is called if the session cannot be configured as requested. * 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, * <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 * too many outputs are requested at once or when trying to initialize multiple
* unrecoverable error during configuration.</p> * 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 * <p>The session is considered to be closed, and all methods called on it after this
* callback is invoked will throw an IllegalStateException.</p> * callback is invoked will throw an IllegalStateException.</p>

View File

@@ -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 * <p>The set of combinations may include camera devices that may be in use by other camera API
* clients.</p> * 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 * <p>The set of combinations doesn't contain physical cameras that can only be used as
* part of a logical multi-camera device.</p> * part of a logical multi-camera device.</p>
* *

View File

@@ -18,6 +18,7 @@ package android.hardware.camera2.extension;
import android.hardware.camera2.extension.IAdvancedExtenderImpl; import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.IPreviewExtenderImpl; import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl; import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
/** @hide */ /** @hide */
interface ICameraExtensionsProxyService interface ICameraExtensionsProxyService
@@ -25,6 +26,8 @@ interface ICameraExtensionsProxyService
long registerClient(); long registerClient();
void unregisterClient(long clientId); void unregisterClient(long clientId);
boolean advancedExtensionsSupported(); boolean advancedExtensionsSupported();
void initializeSession(in IInitializeSessionCallback cb);
void releaseSession();
@nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType); @nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType);
@nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType); @nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType);
@nullable IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType); @nullable IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType);

View File

@@ -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();
}

View File

@@ -35,9 +35,11 @@ import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.extension.CameraOutputConfig; import android.hardware.camera2.extension.CameraOutputConfig;
import android.hardware.camera2.extension.CameraSessionConfig; import android.hardware.camera2.extension.CameraSessionConfig;
import android.hardware.camera2.extension.CaptureStageImpl;
import android.hardware.camera2.extension.IAdvancedExtenderImpl; import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICaptureCallback; import android.hardware.camera2.extension.ICaptureCallback;
import android.hardware.camera2.extension.IImageProcessorImpl; import android.hardware.camera2.extension.IImageProcessorImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IRequestCallback; import android.hardware.camera2.extension.IRequestCallback;
import android.hardware.camera2.extension.IRequestProcessorImpl; import android.hardware.camera2.extension.IRequestProcessorImpl;
import android.hardware.camera2.extension.ISessionProcessorImpl; import android.hardware.camera2.extension.ISessionProcessorImpl;
@@ -89,6 +91,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
private Surface mClientCaptureSurface; private Surface mClientCaptureSurface;
private CameraCaptureSession mCaptureSession = null; private CameraCaptureSession mCaptureSession = null;
private ISessionProcessorImpl mSessionProcessor = null; private ISessionProcessorImpl mSessionProcessor = null;
private final InitializeSessionHandler mInitializeHandler;
private boolean mInitialized; private boolean mInitialized;
@@ -181,6 +184,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
mHandlerThread.start(); mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()); mHandler = new Handler(mHandlerThread.getLooper());
mInitialized = false; mInitialized = false;
mInitializeHandler = new InitializeSessionHandler();
} }
/** /**
@@ -444,7 +448,6 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
public void release() { public void release() {
synchronized (mInterfaceLock) { synchronized (mInterfaceLock) {
mInitialized = false;
mHandlerThread.quitSafely(); mHandlerThread.quitSafely();
if (mSessionProcessor != null) { if (mSessionProcessor != null) {
@@ -459,7 +462,11 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
if (mExtensionClientId >= 0) { if (mExtensionClientId >= 0) {
CameraExtensionCharacteristics.unregisterClient(mExtensionClientId); CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
if (mInitialized) {
CameraExtensionCharacteristics.releaseSession();
} }
}
mInitialized = false;
for (ImageReader reader : mReaderMap.values()) { for (ImageReader reader : mReaderMap.values()) {
reader.close(); reader.close();
@@ -512,9 +519,24 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
@Override @Override
public void onConfigured(@NonNull CameraCaptureSession session) { public void onConfigured(@NonNull CameraCaptureSession session) {
boolean status = true;
synchronized (mInterfaceLock) { synchronized (mInterfaceLock) {
mCaptureSession = session; 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 { try {
mSessionProcessor.onCaptureSessionStart(mRequestProcessor); mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
mInitialized = true; mInitialized = true;
@@ -522,7 +544,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
Log.e(TAG, "Failed to start capture session," Log.e(TAG, "Failed to start capture session,"
+ " extension service does not respond!"); + " extension service does not respond!");
status = false; status = false;
session.close(); mCaptureSession.close();
} }
} }
@@ -538,6 +560,15 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
notifyConfigurationFailure(); 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 { private final class RequestCallbackHandler extends ICaptureCallback.Stub {

View File

@@ -34,6 +34,7 @@ import android.hardware.camera2.extension.CaptureBundle;
import android.hardware.camera2.extension.CaptureStageImpl; import android.hardware.camera2.extension.CaptureStageImpl;
import android.hardware.camera2.extension.ICaptureProcessorImpl; import android.hardware.camera2.extension.ICaptureProcessorImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl; import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl; import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl; import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.ParcelImage; import android.hardware.camera2.extension.ParcelImage;
@@ -48,6 +49,8 @@ import android.media.ImageWriter;
import android.os.Binder; import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IInterface;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.RemoteException; import android.os.RemoteException;
import android.annotation.NonNull; import android.annotation.NonNull;
@@ -80,6 +83,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private final HandlerThread mHandlerThread; private final HandlerThread mHandlerThread;
private final StateCallback mCallbacks; private final StateCallback mCallbacks;
private final List<Size> mSupportedPreviewSizes; private final List<Size> mSupportedPreviewSizes;
private final InitializeSessionHandler mInitializeHandler;
private CameraCaptureSession mCaptureSession = null; private CameraCaptureSession mCaptureSession = null;
private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface; private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface;
@@ -216,6 +220,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
mHandlerThread.start(); mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()); mHandler = new Handler(mHandlerThread.getLooper());
mInitialized = false; mInitialized = false;
mInitializeHandler = new InitializeSessionHandler();
} }
private void initializeRepeatingRequestPipeline() throws RemoteException { private void initializeRepeatingRequestPipeline() throws RemoteException {
@@ -621,7 +626,6 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
public void release() { public void release() {
synchronized (mInterfaceLock) { synchronized (mInterfaceLock) {
mInternalRepeatingRequestEnabled = false; mInternalRepeatingRequestEnabled = false;
mInitialized = false;
mHandlerThread.quitSafely(); mHandlerThread.quitSafely();
try { try {
@@ -634,7 +638,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
if (mExtensionClientId >= 0) { if (mExtensionClientId >= 0) {
CameraExtensionCharacteristics.unregisterClient(mExtensionClientId); CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
if (mInitialized) {
CameraExtensionCharacteristics.releaseSession();
} }
}
mInitialized = false;
if (mRepeatingRequestImageCallback != null) { if (mRepeatingRequestImageCallback != null) {
mRepeatingRequestImageCallback.close(); mRepeatingRequestImageCallback.close();
@@ -739,17 +747,34 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
@Override @Override
public void onConfigured(@NonNull CameraCaptureSession session) { public void onConfigured(@NonNull CameraCaptureSession session) {
boolean status = true;
synchronized (mInterfaceLock) { synchronized (mInterfaceLock) {
mCaptureSession = session; mCaptureSession = session;
try {
CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
} catch (RemoteException e) {
Log.e(TAG, "Failed to initialize session! Extension service does"
+ " not respond!");
notifyConfigurationFailure();
}
}
}
}
ArrayList<CaptureStageImpl> initialRequestList = compileInitialRequestList(); private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
@Override
public void onSuccess() {
boolean status = true;
ArrayList<CaptureStageImpl> initialRequestList =
compileInitialRequestList();
if (!initialRequestList.isEmpty()) { if (!initialRequestList.isEmpty()) {
try { try {
setInitialCaptureRequest(initialRequestList, setInitialCaptureRequest(initialRequestList,
new InitialRequestHandler(mRepeatingRequestImageCallback)); new InitialRequestHandler(
mRepeatingRequestImageCallback));
} catch (CameraAccessException e) { } catch (CameraAccessException e) {
Log.e(TAG, "Failed to initialize the initial capture request!"); Log.e(TAG,
"Failed to initialize the initial capture "
+ "request!");
status = false; status = false;
} }
} else { } else {
@@ -758,17 +783,27 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
new RepeatingRequestHandler(null, null, null, new RepeatingRequestHandler(null, null, null,
mRepeatingRequestImageCallback)); mRepeatingRequestImageCallback));
} catch (CameraAccessException | RemoteException e) { } catch (CameraAccessException | RemoteException e) {
Log.e(TAG, "Failed to initialize internal repeating request!"); Log.e(TAG,
"Failed to initialize internal repeating "
+ "request!");
status = false; status = false;
} }
} }
}
if (!status) { if (!status) {
notifyConfigurationFailure(); 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 { private class BurstRequestHandler extends CameraCaptureSession.CaptureCallback {

View File

@@ -18,6 +18,7 @@ package com.android.cameraextensions;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.GraphicBuffer; import android.graphics.GraphicBuffer;
import android.graphics.Rect; import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException; 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.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl; import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IImageProcessorImpl; import android.hardware.camera2.extension.IImageProcessorImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.ISessionProcessorImpl; import android.hardware.camera2.extension.ISessionProcessorImpl;
import android.hardware.camera2.extension.LatencyRange; import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.OutputConfigId; import android.hardware.camera2.extension.OutputConfigId;
@@ -57,6 +59,7 @@ import android.hardware.HardwareBuffer;
import android.hardware.camera2.impl.PhysicalCaptureResultInfo; import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
import android.media.Image; import android.media.Image;
import android.media.ImageReader; import android.media.ImageReader;
import android.os.Binder;
import android.os.ConditionVariable; import android.os.ConditionVariable;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerExecutor; import android.os.HandlerExecutor;
@@ -176,6 +179,7 @@ public class CameraExtensionsProxyService extends Service {
private long mCurrentClientCount = 0; private long mCurrentClientCount = 0;
private ArraySet<Long> mActiveClients = new ArraySet<>(); private ArraySet<Long> mActiveClients = new ArraySet<>();
private IInitializeSessionCallback mInitializeCb = null;
// Singleton instance // Singleton instance
private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER = 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); 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 * @hide
*/ */
@@ -538,6 +596,39 @@ public class CameraExtensionsProxyService extends Service {
CameraExtensionsProxyService.unregisterClient(clientId); 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 @Override
public boolean advancedExtensionsSupported() { public boolean advancedExtensionsSupported() {
return ADVANCED_API_SUPPORTED; return ADVANCED_API_SUPPORTED;