/* * 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 android.os.DeadObjectException; import android.os.RemoteException; import java.lang.reflect.Method; /** * Translate camera service 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; /** * TODO: add as error codes in Errors.h * - POLICY_PROHIBITS * - RESOURCE_BUSY * - NO_SUCH_DEVICE */ public static final int EACCES = -13; public static final int EBUSY = -16; public static final int ENODEV = -19; private 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; switch (returnValue) { 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: UncheckedThrow.throwAnyException(new CameraRuntimeException( CAMERA_DISCONNECTED)); // TODO: Camera service (native side) should return // EACCES error // when there's a policy manager disabled causing this case EACCES: UncheckedThrow.throwAnyException(new CameraRuntimeException( CAMERA_DISABLED)); case EBUSY: UncheckedThrow.throwAnyException(new CameraRuntimeException( CAMERA_IN_USE)); case ENODEV: UncheckedThrow.throwAnyException(new CameraRuntimeException( CAMERA_DISCONNECTED)); } /** * 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 (returnValue < 0) { throw new UnsupportedOperationException(String.format("Unknown error %d", returnValue)); } } } @Override public boolean onCatchException(Method m, Object[] args, Throwable t) { if (t instanceof DeadObjectException) { UncheckedThrow.throwAnyException(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) { } } /** *

* 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()); } }