From b2456b4058803abe865f1109666bae824268f79e Mon Sep 17 00:00:00 2001 From: Kwangkyu Park Date: Thu, 8 Jun 2023 02:23:15 +0900 Subject: [PATCH] Camera: release extension when extension app crash When extension use app crashed, extension proxy service does not clean up the SessionProcessorImpl or ExtenderImpl. This could cause the resource leak as all resources allocated is expected to be released from the onDeInit or deInitSession. This patch will attached DeathRecipient to the SessionProcessorImpl and ExtenderImpl. And, it will handle relevant release sequence. Bug: 286198063 Test: Manual test with the extension test app that intentionally crashes Change-Id: I9053bc9a51e65c33309eea78db538dfc9962affc --- .../CameraExtensionCharacteristics.java | 114 +++++----- .../ICameraExtensionsProxyService.aidl | 6 +- .../extension/IImageCaptureExtenderImpl.aidl | 6 +- .../extension/IPreviewExtenderImpl.aidl | 6 +- .../extension/ISessionProcessorImpl.aidl | 6 +- .../CameraAdvancedExtensionSessionImpl.java | 37 ++-- .../impl/CameraExtensionSessionImpl.java | 34 +-- .../CameraExtensionsProxyService.java | 199 +++++++++++++----- 8 files changed, 266 insertions(+), 142 deletions(-) diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index d48e20e128b9f..6baf91d720c3d 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -34,6 +34,7 @@ import android.hardware.camera2.impl.CameraExtensionUtils; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.params.ExtensionSessionConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; +import android.os.Binder; import android.os.ConditionVariable; import android.os.IBinder; import android.os.RemoteException; @@ -48,7 +49,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -346,28 +346,29 @@ public final class CameraExtensionCharacteristics { } } - public long registerClient(Context ctx) { + public boolean registerClient(Context ctx, IBinder token) { synchronized (mLock) { connectToProxyLocked(ctx); - if (mProxy != null) { - try { - return mProxy.registerClient(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to initialize extension! Extension service does " - + " not respond!"); - return -1; - } - } else { - return -1; + if (mProxy == null) { + return false; } + + try { + return mProxy.registerClient(token); + } catch (RemoteException e) { + Log.e(TAG, "Failed to initialize extension! Extension service does " + + " not respond!"); + } + + return false; } } - public void unregisterClient(long clientId) { + public void unregisterClient(IBinder token) { synchronized (mLock) { if (mProxy != null) { try { - mProxy.unregisterClient(clientId); + mProxy.unregisterClient(token); } catch (RemoteException e) { Log.e(TAG, "Failed to de-initialize extension! Extension service does" + " not respond!"); @@ -438,15 +439,15 @@ public final class CameraExtensionCharacteristics { /** * @hide */ - public static long registerClient(Context ctx) { - return CameraExtensionManagerGlobal.get().registerClient(ctx); + public static boolean registerClient(Context ctx, IBinder token) { + return CameraExtensionManagerGlobal.get().registerClient(ctx, token); } /** * @hide */ - public static void unregisterClient(long clientId) { - CameraExtensionManagerGlobal.get().unregisterClient(clientId); + public static void unregisterClient(IBinder token) { + CameraExtensionManagerGlobal.get().unregisterClient(token); } /** @@ -564,8 +565,9 @@ public final class CameraExtensionCharacteristics { */ public @NonNull List getSupportedExtensions() { ArrayList ret = new ArrayList<>(); - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#getSupportedExtensions:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { return Collections.unmodifiableList(ret); } @@ -576,7 +578,7 @@ public final class CameraExtensionCharacteristics { } } } finally { - unregisterClient(clientId); + unregisterClient(token); } return Collections.unmodifiableList(ret); @@ -599,8 +601,9 @@ public final class CameraExtensionCharacteristics { * supported device-specific extension */ public boolean isPostviewAvailable(@Extension int extension) { - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#isPostviewAvailable:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { throw new IllegalArgumentException("Unsupported extensions"); } @@ -623,7 +626,7 @@ public final class CameraExtensionCharacteristics { Log.e(TAG, "Failed to query the extension for postview availability! Extension " + "service does not respond!"); } finally { - unregisterClient(clientId); + unregisterClient(token); } return false; @@ -656,9 +659,9 @@ public final class CameraExtensionCharacteristics { @NonNull public List getPostviewSupportedSizes(@Extension int extension, @NonNull Size captureSize, int format) { - - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#getPostviewSupportedSizes:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { throw new IllegalArgumentException("Unsupported extensions"); } @@ -719,7 +722,7 @@ public final class CameraExtensionCharacteristics { + "service does not respond!"); return Collections.emptyList(); } finally { - unregisterClient(clientId); + unregisterClient(token); } } @@ -756,8 +759,9 @@ public final class CameraExtensionCharacteristics { // TODO: Revisit this code once the Extension preview processor output format // ambiguity is resolved in b/169799538. - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { throw new IllegalArgumentException("Unsupported extensions"); } @@ -787,7 +791,7 @@ public final class CameraExtensionCharacteristics { + " not respond!"); return new ArrayList<>(); } finally { - unregisterClient(clientId); + unregisterClient(token); } } @@ -814,8 +818,9 @@ public final class CameraExtensionCharacteristics { public @NonNull List getExtensionSupportedSizes(@Extension int extension, int format) { try { - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { throw new IllegalArgumentException("Unsupported extensions"); } @@ -867,7 +872,7 @@ public final class CameraExtensionCharacteristics { } } } finally { - unregisterClient(clientId); + unregisterClient(token); } } catch (RemoteException e) { Log.e(TAG, "Failed to query the extension supported sizes! Extension service does" @@ -888,7 +893,6 @@ public final class CameraExtensionCharacteristics { * @param format device-specific extension output format * @return the range of estimated minimal and maximal capture latency in milliseconds * or null if no capture latency info can be provided - * * @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG} / * {@link ImageFormat#YUV_420_888}; or unsupported extension. */ @@ -903,8 +907,9 @@ public final class CameraExtensionCharacteristics { throw new IllegalArgumentException("Unsupported format: " + format); } - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#getEstimatedCaptureLatencyRangeMillis:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { throw new IllegalArgumentException("Unsupported extensions"); } @@ -952,7 +957,7 @@ public final class CameraExtensionCharacteristics { Log.e(TAG, "Failed to query the extension capture latency! Extension service does" + " not respond!"); } finally { - unregisterClient(clientId); + unregisterClient(token); } return null; @@ -968,8 +973,9 @@ public final class CameraExtensionCharacteristics { * @throws IllegalArgumentException in case of an unsupported extension. */ public boolean isCaptureProcessProgressAvailable(@Extension int extension) { - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#isCaptureProcessProgressAvailable:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { throw new IllegalArgumentException("Unsupported extensions"); } @@ -992,7 +998,7 @@ public final class CameraExtensionCharacteristics { Log.e(TAG, "Failed to query the extension progress callbacks! Extension service does" + " not respond!"); } finally { - unregisterClient(clientId); + unregisterClient(token); } return false; @@ -1013,8 +1019,9 @@ public final class CameraExtensionCharacteristics { */ @NonNull public Set getAvailableCaptureRequestKeys(@Extension int extension) { - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#getAvailableCaptureRequestKeys:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { throw new IllegalArgumentException("Unsupported extensions"); } @@ -1033,10 +1040,11 @@ public final class CameraExtensionCharacteristics { } else { Pair extenders = initializeExtension(extension); - extenders.second.onInit(mCameraId, mCharacteristicsMapNative.get(mCameraId)); + extenders.second.onInit(token, mCameraId, + mCharacteristicsMapNative.get(mCameraId)); extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys(); - extenders.second.onDeInit(); + extenders.second.onDeInit(token); } if (captureRequestMeta != null) { @@ -1067,7 +1075,7 @@ public final class CameraExtensionCharacteristics { } catch (RemoteException e) { throw new IllegalStateException("Failed to query the available capture request keys!"); } finally { - unregisterClient(clientId); + unregisterClient(token); } return Collections.unmodifiableSet(ret); @@ -1092,8 +1100,9 @@ public final class CameraExtensionCharacteristics { */ @NonNull public Set getAvailableCaptureResultKeys(@Extension int extension) { - long clientId = registerClient(mContext); - if (clientId < 0) { + final IBinder token = new Binder(TAG + "#getAvailableCaptureResultKeys:" + mCameraId); + boolean success = registerClient(mContext, token); + if (!success) { throw new IllegalArgumentException("Unsupported extensions"); } @@ -1111,10 +1120,11 @@ public final class CameraExtensionCharacteristics { } else { Pair extenders = initializeExtension(extension); - extenders.second.onInit(mCameraId, mCharacteristicsMapNative.get(mCameraId)); + extenders.second.onInit(token, mCameraId, + mCharacteristicsMapNative.get(mCameraId)); extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); captureResultMeta = extenders.second.getAvailableCaptureResultKeys(); - extenders.second.onDeInit(); + extenders.second.onDeInit(token); } if (captureResultMeta != null) { @@ -1126,7 +1136,7 @@ public final class CameraExtensionCharacteristics { } CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta); Object crKey = CaptureResult.Key.class; - Class> crKeyTyped = (Class>)crKey; + Class> crKeyTyped = (Class>) crKey; ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped, resultKeys, /*includeSynthetic*/ true)); @@ -1145,7 +1155,7 @@ public final class CameraExtensionCharacteristics { } catch (RemoteException e) { throw new IllegalStateException("Failed to query the available capture result keys!"); } finally { - unregisterClient(clientId); + unregisterClient(token); } return Collections.unmodifiableSet(ret); diff --git a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl index b52c650086f4a..3b7d801afd859 100644 --- a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl +++ b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl @@ -20,11 +20,13 @@ import android.hardware.camera2.extension.IPreviewExtenderImpl; import android.hardware.camera2.extension.IImageCaptureExtenderImpl; import android.hardware.camera2.extension.IInitializeSessionCallback; +import android.os.IBinder; + /** @hide */ interface ICameraExtensionsProxyService { - long registerClient(); - void unregisterClient(long clientId); + boolean registerClient(in IBinder token); + void unregisterClient(in IBinder token); boolean advancedExtensionsSupported(); void initializeSession(in IInitializeSessionCallback cb); void releaseSession(); diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl index 754f8f6625c9f..5a2241820534f 100644 --- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl +++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl @@ -24,11 +24,13 @@ import android.hardware.camera2.extension.LatencyRange; import android.hardware.camera2.extension.Size; import android.hardware.camera2.extension.SizeList; +import android.os.IBinder; + /** @hide */ interface IImageCaptureExtenderImpl { - void onInit(in String cameraId, in CameraMetadataNative cameraCharacteristics); - void onDeInit(); + void onInit(in IBinder token, in String cameraId, in CameraMetadataNative cameraCharacteristics); + void onDeInit(in IBinder token); @nullable CaptureStageImpl onPresetSession(); @nullable CaptureStageImpl onEnableSession(); @nullable CaptureStageImpl onDisableSession(); diff --git a/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl index 01046d01233cc..9ea8a74070492 100644 --- a/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl +++ b/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl @@ -22,11 +22,13 @@ import android.hardware.camera2.extension.IPreviewImageProcessorImpl; import android.hardware.camera2.extension.IRequestUpdateProcessorImpl; import android.hardware.camera2.extension.SizeList; +import android.os.IBinder; + /** @hide */ interface IPreviewExtenderImpl { - void onInit(in String cameraId, in CameraMetadataNative cameraCharacteristics); - void onDeInit(); + void onInit(in IBinder token, in String cameraId, in CameraMetadataNative cameraCharacteristics); + void onDeInit(in IBinder token); @nullable CaptureStageImpl onPresetSession(); @nullable CaptureStageImpl onEnableSession(); @nullable CaptureStageImpl onDisableSession(); diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl index 13b93a8c5e922..0581ec08a131a 100644 --- a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl +++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl @@ -25,13 +25,15 @@ import android.hardware.camera2.extension.LatencyPair; import android.hardware.camera2.extension.LatencyRange; import android.hardware.camera2.extension.OutputSurface; +import android.os.IBinder; + /** @hide */ interface ISessionProcessorImpl { - CameraSessionConfig initSession(in String cameraId, + CameraSessionConfig initSession(in IBinder token, in String cameraId, in Map charsMap, in OutputSurface previewSurface, in OutputSurface imageCaptureSurface, in OutputSurface postviewSurface); - void deInitSession(); + void deInitSession(in IBinder token); void onCaptureSessionStart(IRequestProcessorImpl requestProcessor); void onCaptureSessionEnd(); int startRepeating(in ICaptureCallback callback); diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index 7e3acdbb3ed54..ae700a0a3c417 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -60,6 +60,7 @@ import android.media.ImageReader; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.util.Size; @@ -79,7 +80,6 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private final Executor mExecutor; private CameraDevice mCameraDevice; private final Map mCharacteristicsMap; - private final long mExtensionClientId; private final Handler mHandler; private final HandlerThread mHandlerThread; private final CameraExtensionSession.StateCallback mCallbacks; @@ -90,6 +90,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private final HashMap mReaderMap = new HashMap<>(); private RequestProcessor mRequestProcessor = new RequestProcessor(); private final int mSessionId; + private final IBinder mToken; private Surface mClientRepeatingRequestSurface; private Surface mClientCaptureSurface; @@ -114,8 +115,9 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes @NonNull Map characteristicsMap, @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId) throws CameraAccessException, RemoteException { - long clientId = CameraExtensionCharacteristics.registerClient(ctx); - if (clientId < 0) { + final IBinder token = new Binder(TAG + " : " + sessionId); + boolean success = CameraExtensionCharacteristics.registerClient(ctx, token); + if (!success) { throw new UnsupportedOperationException("Unsupported extension!"); } @@ -202,11 +204,10 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension( config.getExtension()); extender.init(cameraId, characteristicsMapNative); - - CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId, - extender, cameraDevice, characteristicsMapNative, repeatingRequestSurface, + CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(extender, + cameraDevice, characteristicsMapNative, repeatingRequestSurface, burstCaptureSurface, postviewSurface, config.getStateCallback(), - config.getExecutor(), sessionId); + config.getExecutor(), sessionId, token); ret.mStatsAggregator.setClientName(ctx.getOpPackageName()); ret.mStatsAggregator.setExtensionType(config.getExtension()); @@ -216,15 +217,13 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes return ret; } - private CameraAdvancedExtensionSessionImpl(long extensionClientId, - @NonNull IAdvancedExtenderImpl extender, + private CameraAdvancedExtensionSessionImpl(@NonNull IAdvancedExtenderImpl extender, @NonNull CameraDeviceImpl cameraDevice, Map characteristicsMap, @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface, @Nullable Surface postviewSurface, @NonNull StateCallback callback, @NonNull Executor executor, - int sessionId) { - mExtensionClientId = extensionClientId; + int sessionId, @NonNull IBinder token) { mAdvancedExtender = extender; mCameraDevice = cameraDevice; mCharacteristicsMap = characteristicsMap; @@ -240,6 +239,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mSessionClosed = false; mInitializeHandler = new InitializeSessionHandler(); mSessionId = sessionId; + mToken = token; mInterfaceLock = cameraDevice.mInterfaceLock; mStatsAggregator = new ExtensionSessionStatsAggregator(mCameraDevice.getId(), @@ -260,7 +260,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes OutputSurface postviewSurface = initializeParcelable(mClientPostviewSurface); mSessionProcessor = mAdvancedExtender.getSessionProcessor(); - CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(), + CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mToken, + mCameraDevice.getId(), mCharacteristicsMap, previewSurface, captureSurface, postviewSurface); List outputConfigs = sessionConfig.outputConfigs; ArrayList outputList = new ArrayList<>(); @@ -569,7 +570,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes if (!mSessionClosed) { mSessionProcessor.onCaptureSessionEnd(); } - mSessionProcessor.deInitSession(); + mSessionProcessor.deInitSession(mToken); } catch (RemoteException e) { Log.e(TAG, "Failed to de-initialize session processor, extension service" + " does not respond!") ; @@ -577,12 +578,10 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mSessionProcessor = null; } - if (mExtensionClientId >= 0) { - CameraExtensionCharacteristics.unregisterClient(mExtensionClientId); - if (mInitialized || (mCaptureSession != null)) { - notifyClose = true; - CameraExtensionCharacteristics.releaseSession(); - } + CameraExtensionCharacteristics.unregisterClient(mToken); + if (mInitialized || (mCaptureSession != null)) { + notifyClose = true; + CameraExtensionCharacteristics.releaseSession(); } mInitialized = false; diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 9e95d4968d310..1db4808b64309 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -56,6 +56,7 @@ import android.media.ImageWriter; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.util.LongSparseArray; @@ -79,7 +80,6 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private final Executor mExecutor; private final CameraDevice mCameraDevice; - private final long mExtensionClientId; private final IImageCaptureExtenderImpl mImageExtender; private final IPreviewExtenderImpl mPreviewExtender; private final Handler mHandler; @@ -91,6 +91,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private final Set mSupportedRequestKeys; private final Set mSupportedResultKeys; private final ExtensionSessionStatsAggregator mStatsAggregator; + private final IBinder mToken; private boolean mCaptureResultsSupported; private CameraCaptureSession mCaptureSession = null; @@ -136,8 +137,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { @NonNull ExtensionSessionConfiguration config, int sessionId) throws CameraAccessException, RemoteException { - long clientId = CameraExtensionCharacteristics.registerClient(ctx); - if (clientId < 0) { + final IBinder token = new Binder(TAG + " : " + sessionId); + boolean success = CameraExtensionCharacteristics.registerClient(ctx, token); + if (!success) { throw new UnsupportedOperationException("Unsupported extension!"); } @@ -225,15 +227,16 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } extenders.first.init(cameraId, characteristicsMap.get(cameraId).getNativeMetadata()); - extenders.first.onInit(cameraId, characteristicsMap.get(cameraId).getNativeMetadata()); + extenders.first.onInit(token, cameraId, + characteristicsMap.get(cameraId).getNativeMetadata()); extenders.second.init(cameraId, characteristicsMap.get(cameraId).getNativeMetadata()); - extenders.second.onInit(cameraId, characteristicsMap.get(cameraId).getNativeMetadata()); + extenders.second.onInit(token, cameraId, + characteristicsMap.get(cameraId).getNativeMetadata()); CameraExtensionSessionImpl session = new CameraExtensionSessionImpl( extenders.second, extenders.first, supportedPreviewSizes, - clientId, cameraDevice, repeatingRequestSurface, burstCaptureSurface, @@ -241,6 +244,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { config.getStateCallback(), config.getExecutor(), sessionId, + token, extensionChars.getAvailableCaptureRequestKeys(config.getExtension()), extensionChars.getAvailableCaptureResultKeys(config.getExtension())); @@ -255,7 +259,6 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender, @NonNull IPreviewExtenderImpl previewExtender, @NonNull List previewSizes, - long extensionClientId, @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice, @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface, @@ -263,9 +266,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { @NonNull StateCallback callback, @NonNull Executor executor, int sessionId, + @NonNull IBinder token, @NonNull Set requestKeys, @Nullable Set resultKeys) { - mExtensionClientId = extensionClientId; mImageExtender = imageExtender; mPreviewExtender = previewExtender; mCameraDevice = cameraDevice; @@ -282,6 +285,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mSessionClosed = false; mInitializeHandler = new InitializeSessionHandler(); mSessionId = sessionId; + mToken = token; mSupportedRequestKeys = requestKeys; mSupportedResultKeys = resultKeys; mCaptureResultsSupported = !resultKeys.isEmpty(); @@ -867,19 +871,17 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mPreviewExtender.onDisableSession(); mImageExtender.onDisableSession(); } - mPreviewExtender.onDeInit(); - mImageExtender.onDeInit(); + mPreviewExtender.onDeInit(mToken); + mImageExtender.onDeInit(mToken); } catch (RemoteException e) { Log.e(TAG, "Failed to release extensions! Extension service does not" + " respond!"); } - if (mExtensionClientId >= 0) { - CameraExtensionCharacteristics.unregisterClient(mExtensionClientId); - if (mInitialized || (mCaptureSession != null)) { - notifyClose = true; - CameraExtensionCharacteristics.releaseSession(); - } + CameraExtensionCharacteristics.unregisterClient(mToken); + if (mInitialized || (mCaptureSession != null)) { + notifyClose = true; + CameraExtensionCharacteristics.releaseSession(); } mInitialized = false; diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index c2ebddf00fb42..502ee4db80c83 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -75,6 +75,7 @@ import android.util.Range; import android.util.Size; import android.view.Surface; +import androidx.annotation.NonNull; import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl; import androidx.camera.extensions.impl.AutoPreviewExtenderImpl; import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl; @@ -204,7 +205,7 @@ public class CameraExtensionsProxyService extends Service { * A per-process global camera extension manager instance, to track and * initialize/release extensions depending on client activity. */ - private static final class CameraExtensionManagerGlobal { + private static final class CameraExtensionManagerGlobal implements IBinder.DeathRecipient { private static final String TAG = "CameraExtensionManagerGlobal"; private final int EXTENSION_DELAY_MS = 1000; @@ -212,8 +213,9 @@ public class CameraExtensionsProxyService extends Service { private final HandlerThread mHandlerThread; private final Object mLock = new Object(); - private long mCurrentClientCount = 0; - private ArraySet mActiveClients = new ArraySet<>(); + private ArraySet mActiveClients = new ArraySet<>(); + private HashMap> mClientDeathRecipient = + new HashMap<>(); private IInitializeSessionCallback mInitializeCb = null; // Singleton instance @@ -314,8 +316,20 @@ public class CameraExtensionsProxyService extends Service { return GLOBAL_CAMERA_MANAGER; } - public long registerClient(Context ctx) { + public boolean registerClient(Context ctx, IBinder token) { synchronized (mLock) { + if (mActiveClients.contains(token)) { + Log.e(TAG, "Failed to register existing client!"); + return false; + } + + try { + token.linkToDeath(this, 0); + } catch (RemoteException e) { + Log.e(TAG, "Failed to link to binder token!"); + return false; + } + if (INIT_API_SUPPORTED) { if (mActiveClients.isEmpty()) { InitializerFuture status = new InitializerFuture(); @@ -327,47 +341,80 @@ public class CameraExtensionsProxyService extends Service { TimeUnit.MILLISECONDS); } catch (TimeoutException e) { Log.e(TAG, "Timed out while initializing camera extensions!"); - return -1; + return false; } if (!initSuccess) { Log.e(TAG, "Failed while initializing camera extensions!"); - return -1; + return false; } } } - long ret = mCurrentClientCount; - mCurrentClientCount++; - if (mCurrentClientCount < 0) { - mCurrentClientCount = 0; - } - mActiveClients.add(ret); + mActiveClients.add(token); + mClientDeathRecipient.put(token, new ArraySet<>()); - return ret; + return true; } } - public void unregisterClient(long clientId) { + public void unregisterClient(IBinder token) { synchronized (mLock) { - if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() && - INIT_API_SUPPORTED) { - InitializerFuture status = new InitializerFuture(); - InitializerImpl.deinit(new ReleaseHandler(status), - new HandlerExecutor(mHandler)); - boolean releaseSuccess; - try { - releaseSuccess = status.get(EXTENSION_DELAY_MS, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - Log.e(TAG, "Timed out while releasing camera extensions!"); - return; - } - if (!releaseSuccess) { - Log.e(TAG, "Failed while releasing camera extensions!"); + if (mActiveClients.remove(token)) { + token.unlinkToDeath(this, 0); + mClientDeathRecipient.remove(token); + if (mActiveClients.isEmpty() && INIT_API_SUPPORTED) { + InitializerFuture status = new InitializerFuture(); + InitializerImpl.deinit(new ReleaseHandler(status), + new HandlerExecutor(mHandler)); + boolean releaseSuccess; + try { + releaseSuccess = status.get(EXTENSION_DELAY_MS, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + Log.e(TAG, "Timed out while releasing camera extensions!"); + return; + } + if (!releaseSuccess) { + Log.e(TAG, "Failed while releasing camera extensions!"); + } } } } } + @Override + public void binderDied() { + // Do nothing, handled below + } + + @Override + public void binderDied(@NonNull IBinder who) { + synchronized (mLock) { + if (mClientDeathRecipient.containsKey(who)) { + mClientDeathRecipient.get(who).stream().forEach( + recipient -> recipient.binderDied(who)); + } + unregisterClient(who); + } + } + + public void registerDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) { + synchronized (mLock) { + if (mClientDeathRecipient.containsKey(token)) { + ArraySet recipients = mClientDeathRecipient.get(token); + recipients.add(recipient); + } + } + } + + public void unregisterDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) { + synchronized (mLock) { + if (mClientDeathRecipient.containsKey(token)) { + ArraySet recipients = mClientDeathRecipient.get(token); + recipients.remove(recipient); + } + } + } + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { @@ -406,21 +453,35 @@ public class CameraExtensionsProxyService extends Service { /** * @hide */ - private static long registerClient(Context ctx) { + private static boolean registerClient(Context ctx, IBinder token) { if (!EXTENSIONS_PRESENT) { - return -1; + return false; } - return CameraExtensionManagerGlobal.get().registerClient(ctx); + return CameraExtensionManagerGlobal.get().registerClient(ctx, token); } /** * @hide */ - public static void unregisterClient(long clientId) { + public static void unregisterClient(IBinder token) { if (!EXTENSIONS_PRESENT) { return; } - CameraExtensionManagerGlobal.get().unregisterClient(clientId); + CameraExtensionManagerGlobal.get().unregisterClient(token); + } + + /** + * @hide + */ + private static void registerDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) { + CameraExtensionManagerGlobal.get().registerDeathRecipient(token, recipient); + } + + /** + * @hide + */ + private static void unregisterDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) { + CameraExtensionManagerGlobal.get().unregisterDeathRecipient(token, recipient); } /** @@ -649,13 +710,14 @@ public class CameraExtensionsProxyService extends Service { private class CameraExtensionsProxyServiceStub extends ICameraExtensionsProxyService.Stub { @Override - public long registerClient() { - return CameraExtensionsProxyService.registerClient(CameraExtensionsProxyService.this); + public boolean registerClient(IBinder token) { + return CameraExtensionsProxyService.registerClient(CameraExtensionsProxyService.this, + token); } @Override - public void unregisterClient(long clientId) { - CameraExtensionsProxyService.unregisterClient(clientId); + public void unregisterClient(IBinder token) { + CameraExtensionsProxyService.unregisterClient(token); } private boolean checkCameraPermission() { @@ -1192,16 +1254,18 @@ public class CameraExtensionsProxyService extends Service { } } - private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub { + private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub implements + IBinder.DeathRecipient { private final SessionProcessorImpl mSessionProcessor; private String mCameraId = null; + private IBinder mToken; public SessionProcessorImplStub(SessionProcessorImpl sessionProcessor) { mSessionProcessor = sessionProcessor; } @Override - public CameraSessionConfig initSession(String cameraId, + public CameraSessionConfig initSession(IBinder token, String cameraId, Map charsMapNative, OutputSurface previewSurface, OutputSurface imageCaptureSurface, OutputSurface postviewSurface) { OutputSurfaceImplStub outputPreviewSurfaceImpl = @@ -1253,12 +1317,14 @@ public class CameraExtensionsProxyService extends Service { ret.sessionParameter = initializeParcelableMetadata( sessionConfig.getSessionParameters(), cameraId); mCameraId = cameraId; - + mToken = token; + CameraExtensionsProxyService.registerDeathRecipient(mToken, this); return ret; } @Override - public void deInitSession() { + public void deInitSession(IBinder token) { + CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this); mSessionProcessor.deInitSession(); } @@ -1330,6 +1396,11 @@ public class CameraExtensionsProxyService extends Service { return null; } + + @Override + public void binderDied() { + mSessionProcessor.deInitSession(); + } } private class OutputSurfaceConfigurationImplStub implements OutputSurfaceConfigurationImpl { @@ -1395,24 +1466,31 @@ public class CameraExtensionsProxyService extends Service { } } - private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub { + private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub implements + IBinder.DeathRecipient { private final PreviewExtenderImpl mPreviewExtender; private String mCameraId = null; + private boolean mSessionEnabled; + private IBinder mToken; public PreviewExtenderImplStub(PreviewExtenderImpl previewExtender) { mPreviewExtender = previewExtender; } @Override - public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) { + public void onInit(IBinder token, String cameraId, + CameraMetadataNative cameraCharacteristics) { mCameraId = cameraId; CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics); mCameraManager.registerDeviceStateListener(chars); mPreviewExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this); + mToken = token; + CameraExtensionsProxyService.registerDeathRecipient(mToken, this); } @Override - public void onDeInit() { + public void onDeInit(IBinder token) { + CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this); mPreviewExtender.onDeInit(); } @@ -1423,11 +1501,13 @@ public class CameraExtensionsProxyService extends Service { @Override public CaptureStageImpl onEnableSession() { + mSessionEnabled = true; return initializeParcelable(mPreviewExtender.onEnableSession(), mCameraId); } @Override public CaptureStageImpl onDisableSession() { + mSessionEnabled = false; return initializeParcelable(mPreviewExtender.onDisableSession(), mCameraId); } @@ -1516,26 +1596,41 @@ public class CameraExtensionsProxyService extends Service { } return null; } + + @Override + public void binderDied() { + if (mSessionEnabled) { + mPreviewExtender.onDisableSession(); + } + mPreviewExtender.onDeInit(); + } } - private class ImageCaptureExtenderImplStub extends IImageCaptureExtenderImpl.Stub { + private class ImageCaptureExtenderImplStub extends IImageCaptureExtenderImpl.Stub implements + IBinder.DeathRecipient { private final ImageCaptureExtenderImpl mImageExtender; private String mCameraId = null; + private boolean mSessionEnabled; + private IBinder mToken; public ImageCaptureExtenderImplStub(ImageCaptureExtenderImpl imageExtender) { mImageExtender = imageExtender; } @Override - public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) { + public void onInit(IBinder token, String cameraId, + CameraMetadataNative cameraCharacteristics) { CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics); mCameraManager.registerDeviceStateListener(chars); mImageExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this); mCameraId = cameraId; + mToken = token; + CameraExtensionsProxyService.registerDeathRecipient(mToken, this); } @Override - public void onDeInit() { + public void onDeInit(IBinder token) { + CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this); mImageExtender.onDeInit(); } @@ -1564,11 +1659,13 @@ public class CameraExtensionsProxyService extends Service { @Override public CaptureStageImpl onEnableSession() { + mSessionEnabled = true; return initializeParcelable(mImageExtender.onEnableSession(), mCameraId); } @Override public CaptureStageImpl onDisableSession() { + mSessionEnabled = false; return initializeParcelable(mImageExtender.onDisableSession(), mCameraId); } @@ -1737,6 +1834,14 @@ public class CameraExtensionsProxyService extends Service { return null; } + + @Override + public void binderDied() { + if (mSessionEnabled) { + mImageExtender.onDisableSession(); + } + mImageExtender.onDeInit(); + } } private class ProcessResultCallback implements ProcessResultImpl {