diff options
| author | 2024-01-16 19:12:01 -0800 | |
|---|---|---|
| committer | 2024-02-12 10:57:03 -0800 | |
| commit | ad646cf4d8a366e8455bb16823635593353b99ea (patch) | |
| tree | eb7e14ff9aece1ac97b3752f02f656bc4539fa4c | |
| parent | 59e8043a4e08b4d7dae04e08e4f9442c50344666 (diff) | |
camera: make exception handling more explicit in camera2 classes
Camera2 classes used CameraManager's `throwAsPublicFunction` as a
catchall to wrap and re-throw binder exceptions to the applications.
This is functionally correct, however it left some code paths
ambiguous whether it would terminate the method or not.
Consequently there were a few anti-pattern in the code,
specifically:
1. Unreachable statements from compiler not realizing that calls
to `throwAsPublicException` are terminal and no handling is needed
after.
2. Methods catching Throwables only to pass it to
`throwAsPublicFunction` which would finally re-throw it. Catching
generic Throwables is only one step away from difficult to debug
bugs and may hide larger issues. It is recommended to only catch
exceptions intended to be handled.
As more classes are getting added which rely on `throwAsPublicFunction`
to wrap relevant exceptions into application facing exceptions,
this CL moves the logic into a separate helper class, and breaks the
method up to accept specific Exceptions rather than a generic
`Throwable`.
It also updates all usages of `throwAsPublicException` to ensure
that callers are only handling the exceptions they expect to
handle, and explicitly marks the call with `throw` to ensure that
the compiler knows where execution will terminate preventing false
error messages about missing return statements.
Bug: 320741775
Test: No functional change.
atest CtsCameraTestCases:CameraManagerTest passes
atest CtsCameraTestCases:CameraDeviceTest passes
Change-Id: Ic2b765961688ab1cd5e931d8a6ffca8b583d1952
3 files changed, 217 insertions, 143 deletions
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index bcce4b65be18..ada5e1950238 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -45,10 +45,10 @@ import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.params.StreamConfiguration; import android.hardware.camera2.utils.CameraIdAndSessionConfiguration; import android.hardware.camera2.utils.ConcurrentCameraIdCombination; +import android.hardware.camera2.utils.ExceptionUtils; import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.DisplayManager; import android.os.Binder; -import android.os.DeadObjectException; import android.os.Handler; import android.os.HandlerExecutor; import android.os.HandlerThread; @@ -643,7 +643,7 @@ public final class CameraManager { ServiceSpecificException sse = new ServiceSpecificException( ICameraService.ERROR_DISCONNECTED, "Camera service is currently unavailable"); - throwAsPublicException(sse); + throw ExceptionUtils.throwAsPublicException(sse); } return multiResolutionStreamConfigurations; @@ -736,7 +736,7 @@ public final class CameraManager { characteristics = new CameraCharacteristics(info); } catch (ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { // Camera service died - act as if the camera was disconnected throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, @@ -858,11 +858,11 @@ public final class CameraManager { e.errorCode == ICameraService.ERROR_DISCONNECTED || e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) { // Per API docs, these failures call onError and throw - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } } else { // Unexpected failure - rethrow - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } } catch (RemoteException e) { // Camera service died - act as if it's a CAMERA_DISCONNECTED case @@ -870,7 +870,7 @@ public final class CameraManager { ICameraService.ERROR_DISCONNECTED, "Camera service is currently unavailable"); deviceImpl.setRemoteFailure(sse); - throwAsPublicException(sse); + throw ExceptionUtils.throwAsPublicException(sse); } // TODO: factor out callback to be non-nested, then move setter to constructor @@ -1705,56 +1705,6 @@ public final class CameraManager { } /** - * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces - * into the correct public exceptions. - * - * @hide - */ - public static void throwAsPublicException(Throwable t) throws CameraAccessException { - if (t instanceof ServiceSpecificException) { - ServiceSpecificException e = (ServiceSpecificException) t; - int reason = CameraAccessException.CAMERA_ERROR; - switch(e.errorCode) { - case ICameraService.ERROR_DISCONNECTED: - reason = CameraAccessException.CAMERA_DISCONNECTED; - break; - case ICameraService.ERROR_DISABLED: - reason = CameraAccessException.CAMERA_DISABLED; - break; - case ICameraService.ERROR_CAMERA_IN_USE: - reason = CameraAccessException.CAMERA_IN_USE; - break; - case ICameraService.ERROR_MAX_CAMERAS_IN_USE: - reason = CameraAccessException.MAX_CAMERAS_IN_USE; - break; - case ICameraService.ERROR_DEPRECATED_HAL: - reason = CameraAccessException.CAMERA_DEPRECATED_HAL; - break; - case ICameraService.ERROR_ILLEGAL_ARGUMENT: - case ICameraService.ERROR_ALREADY_EXISTS: - throw new IllegalArgumentException(e.getMessage(), e); - case ICameraService.ERROR_PERMISSION_DENIED: - throw new SecurityException(e.getMessage(), e); - case ICameraService.ERROR_TIMED_OUT: - case ICameraService.ERROR_INVALID_OPERATION: - default: - reason = CameraAccessException.CAMERA_ERROR; - } - throw new CameraAccessException(reason, e.getMessage(), e); - } else if (t instanceof DeadObjectException) { - throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, - "Camera service has died unexpectedly", - t); - } else if (t instanceof RemoteException) { - throw new UnsupportedOperationException("An unknown RemoteException was thrown" + - " which should never happen.", t); - } else if (t instanceof RuntimeException) { - RuntimeException e = (RuntimeException) t; - throw e; - } - } - - /** * Queries the camera service if a cameraId is a hidden physical camera that belongs to a * logical camera device. * @@ -1829,13 +1779,13 @@ public final class CameraManager { internalCamId, externalCamId, cameraInjectionCallback); injectionSessionImpl.setRemoteInjectionSession(injectionSession); } catch (ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { // Camera service died - act as if it's a CAMERA_DISCONNECTED case ServiceSpecificException sse = new ServiceSpecificException( ICameraService.ERROR_DISCONNECTED, "Camera service is currently unavailable"); - throwAsPublicException(sse); + throw ExceptionUtils.throwAsPublicException(sse); } } } @@ -2124,7 +2074,7 @@ public final class CameraManager { cameraService.remapCameraIds(cameraIdRemapping); mActiveCameraIdRemapping = cameraIdRemapping; } catch (ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { throw new CameraAccessException( CameraAccessException.CAMERA_DISCONNECTED, @@ -2148,7 +2098,7 @@ public final class CameraManager { try { cameraService.injectSessionParams(cameraId, sessionParams.getNativeMetadata()); } catch (ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { throw new CameraAccessException( CameraAccessException.CAMERA_DISCONNECTED, @@ -2391,15 +2341,13 @@ public final class CameraManager { return mCameraService.isConcurrentSessionConfigurationSupported( cameraIdsAndConfigs, targetSdkVersion); } catch (ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { // Camera service died - act as if the camera was disconnected throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "Camera service is currently unavailable", e); } } - - return false; } public boolean isSessionConfigurationWithParametersSupported( @@ -2411,15 +2359,13 @@ public final class CameraManager { return mCameraService.isSessionConfigurationWithParametersSupported( cameraId, sessionConfiguration); } catch (ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { // Camera service died - act as if the camera was disconnected throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "Camera service is currently unavailable", e); } } - - return false; } /** @@ -2462,7 +2408,7 @@ public final class CameraManager { try { cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder); } catch(ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "Camera service is currently unavailable"); @@ -2488,7 +2434,7 @@ public final class CameraManager { cameraService.turnOnTorchWithStrengthLevel(cameraId, torchStrength, mTorchClientBinder); } catch(ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "Camera service is currently unavailable."); @@ -2512,7 +2458,7 @@ public final class CameraManager { try { torchStrength = cameraService.getTorchStrengthLevel(cameraId); } catch(ServiceSpecificException e) { - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "Camera service is currently unavailable."); @@ -2551,7 +2497,7 @@ public final class CameraManager { throw new UnsupportedOperationException(e.getMessage()); } - throwAsPublicException(e); + throw ExceptionUtils.throwAsPublicException(e); } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "Camera service is currently unavailable."); diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index 2129260b0ae8..241268d866a4 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -18,14 +18,13 @@ package android.hardware.camera2.impl; import android.hardware.ICameraService; import android.hardware.camera2.CameraAccessException; -import android.hardware.camera2.CameraManager; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.ICameraOfflineSession; -import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.utils.ExceptionUtils; import android.hardware.camera2.utils.SubmitInfo; import android.os.IBinder; import android.os.RemoteException; @@ -69,9 +68,10 @@ public class ICameraDeviceUserWrapper { throws CameraAccessException { try { return mRemoteDevice.submitRequest(request, streaming); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -79,27 +79,30 @@ public class ICameraDeviceUserWrapper { throws CameraAccessException { try { return mRemoteDevice.submitRequestList(requestList, streaming); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public long cancelRequest(int requestId) throws CameraAccessException { try { return mRemoteDevice.cancelRequest(requestId); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public void beginConfigure() throws CameraAccessException { try { mRemoteDevice.beginConfigure(); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -108,18 +111,20 @@ public class ICameraDeviceUserWrapper { try { return mRemoteDevice.endConfigure(operatingMode, (sessionParams == null) ? new CameraMetadataNative() : sessionParams, startTimeMs); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public void deleteStream(int streamId) throws CameraAccessException { try { mRemoteDevice.deleteStream(streamId); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -127,9 +132,10 @@ public class ICameraDeviceUserWrapper { throws CameraAccessException { try { return mRemoteDevice.createStream(outputConfiguration); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -137,45 +143,50 @@ public class ICameraDeviceUserWrapper { throws CameraAccessException { try { return mRemoteDevice.createInputStream(width, height, format, isMultiResolution); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public Surface getInputSurface() throws CameraAccessException { try { return mRemoteDevice.getInputSurface(); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public CameraMetadataNative createDefaultRequest(int templateId) throws CameraAccessException { try { return mRemoteDevice.createDefaultRequest(templateId); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public CameraMetadataNative getCameraInfo() throws CameraAccessException { try { return mRemoteDevice.getCameraInfo(); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public void waitUntilIdle() throws CameraAccessException { try { mRemoteDevice.waitUntilIdle(); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -191,10 +202,9 @@ public class ICameraDeviceUserWrapper { throw new IllegalArgumentException("Invalid session configuration"); } - throw e; - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -213,46 +223,49 @@ public class ICameraDeviceUserWrapper { throw new IllegalArgumentException("Invalid session configuration"); } - throw e; - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public long flush() throws CameraAccessException { try { return mRemoteDevice.flush(); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public void prepare(int streamId) throws CameraAccessException { try { mRemoteDevice.prepare(streamId); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public void tearDown(int streamId) throws CameraAccessException { try { mRemoteDevice.tearDown(streamId); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public void prepare2(int maxCount, int streamId) throws CameraAccessException { try { mRemoteDevice.prepare2(maxCount, streamId); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -260,9 +273,10 @@ public class ICameraDeviceUserWrapper { throws CameraAccessException { try { mRemoteDevice.updateOutputConfiguration(streamId, config); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -270,9 +284,10 @@ public class ICameraDeviceUserWrapper { int[] offlineOutputIds) throws CameraAccessException { try { return mRemoteDevice.switchToOffline(cbs, offlineOutputIds); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } @@ -280,27 +295,30 @@ public class ICameraDeviceUserWrapper { throws CameraAccessException { try { mRemoteDevice.finalizeOutputConfigurations(streamId, deferredConfig); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public void setCameraAudioRestriction(int mode) throws CameraAccessException { try { mRemoteDevice.setCameraAudioRestriction(mode); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } public int getGlobalAudioRestriction() throws CameraAccessException { try { return mRemoteDevice.getGlobalAudioRestriction(); - } catch (Throwable t) { - CameraManager.throwAsPublicException(t); - throw new UnsupportedOperationException("Unexpected exception", t); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); } } diff --git a/core/java/android/hardware/camera2/utils/ExceptionUtils.java b/core/java/android/hardware/camera2/utils/ExceptionUtils.java new file mode 100644 index 000000000000..bfa96f26fa18 --- /dev/null +++ b/core/java/android/hardware/camera2/utils/ExceptionUtils.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 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.utils; + +import android.hardware.ICameraService; +import android.hardware.camera2.CameraAccessException; +import android.os.DeadObjectException; +import android.os.RemoteException; +import android.os.ServiceSpecificException; + +/** + * @hide + */ +public class ExceptionUtils { + /** + * Converts and throws {@link ServiceSpecificException} from camera binder interfaces as + * {@link CameraAccessException}, {@link IllegalArgumentException}, or {@link SecurityException} + * based on {@link ServiceSpecificException#errorCode} + * <p> + * Usage: {@code throw ExceptionUtils.throwAsPublicException(e)} + * <p> + * Notice the preceding `throw` before calling this method. The throw is essentially + * useless but lets the compiler know that execution will terminate at that statement + * preventing false "missing return statement" errors. + * <p> + * The return type is set to the only checked exception this method throws to ensure + * that the caller knows exactly which checked exception to declare/handle. + * + * @hide + */ + public static CameraAccessException throwAsPublicException(ServiceSpecificException e) + throws CameraAccessException { + int reason; + switch(e.errorCode) { + case ICameraService.ERROR_DISCONNECTED: + reason = CameraAccessException.CAMERA_DISCONNECTED; + break; + case ICameraService.ERROR_DISABLED: + reason = CameraAccessException.CAMERA_DISABLED; + break; + case ICameraService.ERROR_CAMERA_IN_USE: + reason = CameraAccessException.CAMERA_IN_USE; + break; + case ICameraService.ERROR_MAX_CAMERAS_IN_USE: + reason = CameraAccessException.MAX_CAMERAS_IN_USE; + break; + case ICameraService.ERROR_DEPRECATED_HAL: + reason = CameraAccessException.CAMERA_DEPRECATED_HAL; + break; + case ICameraService.ERROR_ILLEGAL_ARGUMENT: + case ICameraService.ERROR_ALREADY_EXISTS: + throw new IllegalArgumentException(e.getMessage(), e); + case ICameraService.ERROR_PERMISSION_DENIED: + throw new SecurityException(e.getMessage(), e); + case ICameraService.ERROR_TIMED_OUT: + case ICameraService.ERROR_INVALID_OPERATION: + default: + reason = CameraAccessException.CAMERA_ERROR; + } + + throw new CameraAccessException(reason, e.getMessage(), e); + } + + /** + * Converts and throws Binder {@link DeadObjectException} and {@link RemoteException} from + * camera binder interfaces as {@link CameraAccessException} or + * {@link UnsupportedOperationException} + * <p> + * Usage: {@code throw ExceptionUtils.throwAsPublicException(e)} + * <p> + * Notice the preceding `throw` before calling this method. The throw is essentially + * useless but lets the compiler know that execution will terminate at that statement + * preventing false "missing return statement" errors. + * <p> + * The return type is set to the only checked exception this method throws to ensure + * that the caller knows exactly which checked exception to declare/handle. + * + * @hide + */ + public static CameraAccessException throwAsPublicException(RemoteException e) + throws CameraAccessException { + if (e instanceof DeadObjectException) { + throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, + "Camera service has died unexpectedly", e); + } + + throw new UnsupportedOperationException("An unknown RemoteException was thrown" + + " which should never happen.", e); + } + + /** + * Static methods only. Do not initialize. + * @hide + */ + private ExceptionUtils() {} +} |