blob: 1a44b97f8e4300f30b44675466aa5643b20729e5 [file] [log] [blame]
Igor Murashkine363fbb2013-06-25 20:26:06 +00001/*
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.photography.utils;
18
19import static android.hardware.photography.CameraAccessException.CAMERA_DISABLED;
20import static android.hardware.photography.CameraAccessException.CAMERA_DISCONNECTED;
21import static android.hardware.photography.CameraAccessException.CAMERA_IN_USE;
22
23import android.os.DeadObjectException;
24import android.os.RemoteException;
25
26import java.lang.reflect.Method;
27
28/**
29 * Translate camera service status_t return values into exceptions.
30 *
31 * @see android.hardware.photography.utils.CameraBinderDecorator#newInstance
32 * @hide
33 */
34public class CameraBinderDecorator {
35
36 public static final int NO_ERROR = 0;
37 public static final int PERMISSION_DENIED = -1;
38 public static final int ALREADY_EXISTS = -17;
39 public static final int BAD_VALUE = -22;
40 public static final int DEAD_OBJECT = -32;
41
42 /**
43 * TODO: add as error codes in Errors.h
44 * - POLICY_PROHIBITS
45 * - RESOURCE_BUSY
46 * - NO_SUCH_DEVICE
47 */
48 public static final int EACCES = -13;
49 public static final int EBUSY = -16;
50 public static final int ENODEV = -19;
51
52 private static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
53
54 @Override
55 public void onBeforeInvocation(Method m, Object[] args) {
56 }
57
58 @Override
59 public void onAfterInvocation(Method m, Object[] args, Object result) {
60 // int return type => status_t => convert to exception
61 if (m.getReturnType() == Integer.TYPE) {
62 int returnValue = (Integer) result;
63
64 switch (returnValue) {
65 case NO_ERROR:
66 return;
67 case PERMISSION_DENIED:
68 throw new SecurityException("Lacking privileges to access camera service");
69 case ALREADY_EXISTS:
Igor Murashkin70725502013-06-25 20:27:06 +000070 // This should be handled at the call site. Typically this isn't bad,
71 // just means we tried to do an operation that already completed.
Igor Murashkine363fbb2013-06-25 20:26:06 +000072 return;
73 case BAD_VALUE:
74 throw new IllegalArgumentException("Bad argument passed to camera service");
75 case DEAD_OBJECT:
76 UncheckedThrow.throwAnyException(new CameraRuntimeException(
77 CAMERA_DISCONNECTED));
78 // TODO: Camera service (native side) should return
79 // EACCES error
80 // when there's a policy manager disabled causing this
81 case EACCES:
82 UncheckedThrow.throwAnyException(new CameraRuntimeException(
83 CAMERA_DISABLED));
84 case EBUSY:
85 UncheckedThrow.throwAnyException(new CameraRuntimeException(
86 CAMERA_IN_USE));
87 case ENODEV:
88 UncheckedThrow.throwAnyException(new CameraRuntimeException(
89 CAMERA_DISCONNECTED));
90 }
91
92 /**
93 * Trap the rest of the negative return values. If we have known
94 * error codes i.e. ALREADY_EXISTS that aren't really runtime
95 * errors, then add them to the top switch statement
96 */
97 if (returnValue < 0) {
98 throw new UnsupportedOperationException(String.format("Unknown error %d",
99 returnValue));
100 }
101 }
102 }
103
104 @Override
105 public boolean onCatchException(Method m, Object[] args, Throwable t) {
106
107 if (t instanceof DeadObjectException) {
108 UncheckedThrow.throwAnyException(new CameraRuntimeException(
109 CAMERA_DISCONNECTED,
110 "Process hosting the camera service has died unexpectedly",
111 t));
112 } else if (t instanceof RemoteException) {
113 throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
114 " which should never happen.", t);
115 }
116
117 return false;
118 }
119
120 @Override
121 public void onFinally(Method m, Object[] args) {
122 }
123
124 }
125
126 /**
127 * <p>
128 * Wraps the type T with a proxy that will check 'status_t' return codes
129 * from the native side of the camera service, and throw Java exceptions
130 * automatically based on the code.
131 * </p>
132 * <p>
133 * In addition it also rewrites binder's RemoteException into either a
134 * CameraAccessException or an UnsupportedOperationException.
135 * </p>
136 * <p>
137 * As a result of calling any method on the proxy, RemoteException is
138 * guaranteed never to be thrown.
139 * </p>
140 *
141 * @param obj object that will serve as the target for all method calls
142 * @param <T> the type of the element you want to wrap. This must be an interface.
143 * @return a proxy that will intercept all invocations to obj
144 */
145 public static <T> T newInstance(T obj) {
146 return Decorator.<T> newInstance(obj, new CameraBinderDecoratorListener());
147 }
148}