CameraBinderDecorator.java revision bd9b106806f9792be210cc2d9848d8b1f4b9664d
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.camera2.utils;
18
19import static android.hardware.camera2.CameraAccessException.CAMERA_DISABLED;
20import static android.hardware.camera2.CameraAccessException.CAMERA_DISCONNECTED;
21import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
22import static android.hardware.camera2.CameraAccessException.MAX_CAMERAS_IN_USE;
23import static android.hardware.camera2.CameraAccessException.CAMERA_DEPRECATED_HAL;
24
25import android.os.DeadObjectException;
26import android.os.RemoteException;
27
28import java.lang.reflect.Method;
29
30/**
31 * Translate camera device status_t return values into exceptions.
32 *
33 * @see android.hardware.camera2.utils.CameraBinderDecorator#newInstance
34 * @hide
35 */
36public class CameraBinderDecorator {
37
38    public static final int NO_ERROR = 0;
39    public static final int PERMISSION_DENIED = -1;
40    public static final int ALREADY_EXISTS = -17;
41    public static final int BAD_VALUE = -22;
42    public static final int DEAD_OBJECT = -32;
43    public static final int INVALID_OPERATION = -38;
44
45    /**
46     * TODO: add as error codes in Errors.h
47     * - POLICY_PROHIBITS
48     * - RESOURCE_BUSY
49     * - NO_SUCH_DEVICE
50     * - NOT_SUPPORTED
51     * - TOO_MANY_USERS
52     */
53    public static final int EACCES = -13;
54    public static final int EBUSY = -16;
55    public static final int ENODEV = -19;
56    public static final int EOPNOTSUPP = -95;
57    public static final int EUSERS = -87;
58
59
60    static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
61
62        @Override
63        public void onBeforeInvocation(Method m, Object[] args) {
64        }
65
66        @Override
67        public void onAfterInvocation(Method m, Object[] args, Object result) {
68            // int return type => status_t => convert to exception
69            if (m.getReturnType() == Integer.TYPE) {
70                int returnValue = (Integer) result;
71                throwOnError(returnValue);
72            }
73        }
74
75        @Override
76        public boolean onCatchException(Method m, Object[] args, Throwable t) {
77
78            if (t instanceof DeadObjectException) {
79                throw new CameraRuntimeException(CAMERA_DISCONNECTED,
80                        "Process hosting the camera service has died unexpectedly",
81                        t);
82            } else if (t instanceof RemoteException) {
83                throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
84                        " which should never happen.", t);
85            }
86
87            return false;
88        }
89
90        @Override
91        public void onFinally(Method m, Object[] args) {
92        }
93
94    }
95
96    /**
97     * Throw error codes returned by the camera service as exceptions.
98     *
99     * @param errorFlag error to throw as an exception.
100     */
101    public static void throwOnError(int errorFlag) {
102        switch (errorFlag) {
103            case NO_ERROR:
104                return;
105            case PERMISSION_DENIED:
106                throw new SecurityException("Lacking privileges to access camera service");
107            case ALREADY_EXISTS:
108                // This should be handled at the call site. Typically this isn't bad,
109                // just means we tried to do an operation that already completed.
110                return;
111            case BAD_VALUE:
112                throw new IllegalArgumentException("Bad argument passed to camera service");
113            case DEAD_OBJECT:
114                throw new CameraRuntimeException(CAMERA_DISCONNECTED);
115            case EACCES:
116                throw new CameraRuntimeException(CAMERA_DISABLED);
117            case EBUSY:
118                throw new CameraRuntimeException(CAMERA_IN_USE);
119            case EUSERS:
120                throw new CameraRuntimeException(MAX_CAMERAS_IN_USE);
121            case ENODEV:
122                throw new CameraRuntimeException(CAMERA_DISCONNECTED);
123            case EOPNOTSUPP:
124                throw new CameraRuntimeException(CAMERA_DEPRECATED_HAL);
125            case INVALID_OPERATION:
126                throw new IllegalStateException(
127                        "Illegal state encountered in camera service.");
128        }
129
130        /**
131         * Trap the rest of the negative return values. If we have known
132         * error codes i.e. ALREADY_EXISTS that aren't really runtime
133         * errors, then add them to the top switch statement
134         */
135        if (errorFlag < 0) {
136            throw new UnsupportedOperationException(String.format("Unknown error %d",
137                    errorFlag));
138        }
139    }
140
141    /**
142     * <p>
143     * Wraps the type T with a proxy that will check 'status_t' return codes
144     * from the native side of the camera service, and throw Java exceptions
145     * automatically based on the code.
146     * </p>
147     * <p>
148     * In addition it also rewrites binder's RemoteException into either a
149     * CameraAccessException or an UnsupportedOperationException.
150     * </p>
151     * <p>
152     * As a result of calling any method on the proxy, RemoteException is
153     * guaranteed never to be thrown.
154     * </p>
155     *
156     * @param obj object that will serve as the target for all method calls
157     * @param <T> the type of the element you want to wrap. This must be an interface.
158     * @return a proxy that will intercept all invocations to obj
159     */
160    public static <T> T newInstance(T obj) {
161        return Decorator.<T> newInstance(obj, new CameraBinderDecoratorListener());
162    }
163}
164