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.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
*/

View File

@@ -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>

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
* 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>
*

View File

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

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.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 {

View File

@@ -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 {

View File

@@ -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;