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
This commit is contained in:
Kwangkyu Park
2023-06-08 02:23:15 +09:00
committed by Emilian Peev
parent 41fc44e370
commit b2456b4058
8 changed files with 266 additions and 142 deletions

View File

@@ -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<Integer> getSupportedExtensions() {
ArrayList<Integer> 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<Size> 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<Size> 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<CaptureRequest.Key> 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<IPreviewExtenderImpl, IImageCaptureExtenderImpl> 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<CaptureResult.Key> 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<IPreviewExtenderImpl, IImageCaptureExtenderImpl> 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<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>) 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);

View File

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

View File

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

View File

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

View File

@@ -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<String, CameraMetadataNative> 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);

View File

@@ -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<String, CameraMetadataNative> 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<Integer, ImageReader> 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<String, CameraCharacteristics> 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<String, CameraMetadataNative> 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<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
ArrayList<OutputConfiguration> 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;

View File

@@ -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<CaptureRequest.Key> mSupportedRequestKeys;
private final Set<CaptureResult.Key> 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<Size> 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<CaptureRequest.Key> requestKeys,
@Nullable Set<CaptureResult.Key> 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;

View File

@@ -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<Long> mActiveClients = new ArraySet<>();
private ArraySet<IBinder> mActiveClients = new ArraySet<>();
private HashMap<IBinder, ArraySet<IBinder.DeathRecipient>> 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<IBinder.DeathRecipient> recipients = mClientDeathRecipient.get(token);
recipients.add(recipient);
}
}
}
public void unregisterDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
synchronized (mLock) {
if (mClientDeathRecipient.containsKey(token)) {
ArraySet<IBinder.DeathRecipient> 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<String, CameraMetadataNative> 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 {