/*
* 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 CameraRuntimeException(CAMERA_ERROR,
String.format("Unknown camera device error %d", errorFlag));
}
}
/**
* <p>
* 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.
* </p>
* <p>
* In addition it also rewrites binder's RemoteException into either a
* CameraAccessException or an UnsupportedOperationException.
* </p>
* <p>
* As a result of calling any method on the proxy, RemoteException is
* guaranteed never to be thrown.
* </p>
*
* @param obj object that will serve as the target for all method calls
* @param <T> 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> T newInstance(T obj) {
return Decorator.<T> newInstance(obj, new CameraBinderDecoratorListener());
}
}