/* * Copyright (C) 2013 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 static android.hardware.camera2.CameraAccessException.CAMERA_DISABLED; import static android.hardware.camera2.CameraAccessException.CAMERA_DISCONNECTED; import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; import static android.hardware.camera2.CameraAccessException.CAMERA_ERROR; import static android.hardware.camera2.CameraAccessException.MAX_CAMERAS_IN_USE; import static android.hardware.camera2.CameraAccessException.CAMERA_DEPRECATED_HAL; import android.os.DeadObjectException; import android.os.RemoteException; import java.lang.reflect.Method; /** * Translate camera device status_t return values into exceptions. * * @see android.hardware.camera2.utils.CameraBinderDecorator#newInstance * @hide */ public class CameraBinderDecorator { public static final int NO_ERROR = 0; public static final int PERMISSION_DENIED = -1; public static final int ALREADY_EXISTS = -17; public static final int BAD_VALUE = -22; public static final int DEAD_OBJECT = -32; public static final int INVALID_OPERATION = -38; public static final int TIMED_OUT = -110; /** * TODO: add as error codes in Errors.h * - POLICY_PROHIBITS * - RESOURCE_BUSY * - NO_SUCH_DEVICE * - NOT_SUPPORTED * - TOO_MANY_USERS */ public static final int EACCES = -13; public static final int EBUSY = -16; public static final int ENODEV = -19; public static final int EOPNOTSUPP = -95; public static final int EUSERS = -87; static class CameraBinderDecoratorListener implements Decorator.DecoratorListener { @Override public void onBeforeInvocation(Method m, Object[] args) { } @Override public void onAfterInvocation(Method m, Object[] args, Object result) { // int return type => status_t => convert to exception if (m.getReturnType() == Integer.TYPE) { int returnValue = (Integer) result; throwOnError(returnValue); } } @Override public boolean onCatchException(Method m, Object[] args, Throwable t) { if (t instanceof DeadObjectException) { throw new CameraRuntimeException(CAMERA_DISCONNECTED, "Process hosting the camera service has died unexpectedly", t); } else if (t instanceof RemoteException) { throw new UnsupportedOperationException("An unknown RemoteException was thrown" + " which should never happen.", t); } return false; } @Override public void onFinally(Method m, Object[] args) { } } /** * Throw error codes returned by the camera service as exceptions. * * @param errorFlag error to throw as an exception. */ public static void throwOnError(int errorFlag) { switch (errorFlag) { case NO_ERROR: return; case PERMISSION_DENIED: throw new SecurityException("Lacking privileges to access camera service"); case ALREADY_EXISTS: // This should be handled at the call site. Typically this isn't bad, // just means we tried to do an operation that already completed. return; case BAD_VALUE: throw new IllegalArgumentException("Bad argument passed to camera service"); case DEAD_OBJECT: throw new CameraRuntimeException(CAMERA_DISCONNECTED); case TIMED_OUT: throw new CameraRuntimeException(CAMERA_ERROR, "Operation timed out in camera service"); case EACCES: throw new CameraRuntimeException(CAMERA_DISABLED); case EBUSY: throw new CameraRuntimeException(CAMERA_IN_USE); case EUSERS: throw new CameraRuntimeException(MAX_CAMERAS_IN_USE); case ENODEV: throw new CameraRuntimeException(CAMERA_DISCONNECTED); case EOPNOTSUPP: throw new CameraRuntimeException(CAMERA_DEPRECATED_HAL); case INVALID_OPERATION: throw new CameraRuntimeException(CAMERA_ERROR, "Illegal state encountered in camera service."); } /** * Trap the rest of the negative return values. If we have known * error codes i.e. ALREADY_EXISTS that aren't really runtime * errors, then add them to the top switch statement */ if (errorFlag < 0) { throw new UnsupportedOperationException(String.format("Unknown error %d", errorFlag)); } } /** *

* Wraps the type T with a proxy that will check 'status_t' return codes * from the native side of the camera service, and throw Java exceptions * automatically based on the code. *

*

* In addition it also rewrites binder's RemoteException into either a * CameraAccessException or an UnsupportedOperationException. *

*

* As a result of calling any method on the proxy, RemoteException is * guaranteed never to be thrown. *

* * @param obj object that will serve as the target for all method calls * @param the type of the element you want to wrap. This must be an interface. * @return a proxy that will intercept all invocations to obj */ public static T newInstance(T obj) { return Decorator. newInstance(obj, new CameraBinderDecoratorListener()); } }