• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 
17 package com.android.camera.device;
18 
19 import android.content.Context;
20 import android.os.Handler;
21 
22 import com.android.camera.async.HandlerFactory;
23 import com.android.camera.async.Lifetime;
24 import com.android.camera.async.SafeCloseable;
25 import com.android.camera.debug.Log.Tag;
26 import com.android.camera.debug.Logger;
27 import com.android.ex.camera2.portability.CameraAgent;
28 import com.android.ex.camera2.portability.CameraAgent.CameraOpenCallback;
29 import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
30 import com.android.ex.camera2.portability.CameraAgentFactory;
31 import com.android.ex.camera2.portability.CameraAgentFactory.CameraApi;
32 
33 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.atomic.AtomicBoolean;
35 
36 import javax.annotation.ParametersAreNonnullByDefault;
37 
38 /**
39  * Set of device actions for opening and closing a single portability
40  * layer camera device.
41  */
42 @ParametersAreNonnullByDefault
43 public class PortabilityCameraActions implements SingleDeviceActions<CameraProxy> {
44     private static final Tag TAG = new Tag("PortCamAct");
45 
46     private final CameraDeviceKey mId;
47     private final HandlerFactory mHandlerFactory;
48     private final ExecutorService mBackgroundRunner;
49     private final Context mContext;
50     private final CameraApi mApiVersion;
51     private final Logger mLogger;
52 
PortabilityCameraActions( CameraDeviceKey id, Context context, CameraApi apiVersion, ExecutorService backgroundRunner, HandlerFactory handlerFactory, Logger.Factory logFactory)53     public PortabilityCameraActions(
54           CameraDeviceKey id,
55           Context context,
56           CameraApi apiVersion,
57           ExecutorService backgroundRunner,
58           HandlerFactory handlerFactory,
59           Logger.Factory logFactory) {
60         mId = id;
61         mContext = context;
62         mApiVersion = apiVersion;
63         mBackgroundRunner = backgroundRunner;
64         mHandlerFactory = handlerFactory;
65         mLogger = logFactory.create(TAG);
66 
67         mLogger.d("Created Camera2Request");
68     }
69 
70     @Override
executeOpen(SingleDeviceOpenListener<CameraProxy> openListener, Lifetime deviceLifetime)71     public void executeOpen(SingleDeviceOpenListener<CameraProxy> openListener,
72           Lifetime deviceLifetime) throws UnsupportedOperationException {
73         mLogger.i("executeOpen(id: " + mId.getCameraId() + ")");
74         try {
75             CameraAgent agent = CameraAgentFactory.getAndroidCameraAgent(mContext, mApiVersion);
76             deviceLifetime.add(new CameraAgentRecycler(mApiVersion, mLogger));
77 
78             mBackgroundRunner.execute(new OpenCameraRunnable(agent, mId.getCameraId().getLegacyValue(),
79                     mHandlerFactory.create(deviceLifetime, "Camera2 Lifetime"),
80                     openListener, mLogger));
81         } catch (AssertionError e) {
82             openListener.onDeviceOpenException(e);
83         }
84     }
85 
86     @Override
executeClose(SingleDeviceCloseListener closeListener, CameraProxy device)87     public void executeClose(SingleDeviceCloseListener closeListener, CameraProxy device) {
88         mLogger.i("executeClose(" + device.getCameraId() + ")");
89         mBackgroundRunner.execute(new CloseCameraRunnable(device, device.getAgent(),
90               closeListener, mLogger));
91     }
92 
93     /**
94      * Recycles camera agents and ensures that recycle is only called
95      * once per instance.
96      */
97     private static class CameraAgentRecycler implements SafeCloseable {
98         private final CameraApi mCameraApi;
99         private final Logger mLogger;
100         private final AtomicBoolean mIsClosed;
101 
CameraAgentRecycler(CameraApi cameraApi, Logger logger)102         public CameraAgentRecycler(CameraApi cameraApi, Logger logger) {
103             mCameraApi = cameraApi;
104             mLogger = logger;
105             mIsClosed = new AtomicBoolean(false);
106         }
107 
108         @Override
close()109         public void close() {
110             if (!mIsClosed.getAndSet(true)) {
111                 mLogger.d("Recycling CameraAgentFactory for CameraApi: " + mCameraApi);
112                 CameraAgentFactory.recycle(mCameraApi);
113             }
114         }
115     }
116 
117     /**
118      * Internal runnable that executes a CameraManager openCamera call.
119      */
120     private static class OpenCameraRunnable implements Runnable {
121         private final SingleDeviceOpenListener<CameraProxy> mOpenListener;
122         private final int mCameraId;
123         private final Handler mHandler;
124         private final CameraAgent mCameraAgent;
125         private final Logger mLogger;
126 
OpenCameraRunnable(CameraAgent cameraAgent, int cameraId, Handler handler, SingleDeviceOpenListener<CameraProxy> openListener, Logger logger)127         public OpenCameraRunnable(CameraAgent cameraAgent, int cameraId,
128               Handler handler, SingleDeviceOpenListener<CameraProxy> openListener,
129               Logger logger) {
130             mCameraAgent = cameraAgent;
131             mCameraId = cameraId;
132             mHandler = handler;
133             mOpenListener = openListener;
134             mLogger = logger;
135         }
136 
137         @Override
run()138         public void run() {
139             try {
140                 mLogger.i("mCameraAgent.openCamera(id: " + mCameraId + ")");
141                 mCameraAgent.openCamera(mHandler, mCameraId,
142                       new OpenCameraStateCallback(mOpenListener, mLogger));
143             } catch (SecurityException e) {
144                 mOpenListener.onDeviceOpenException(e);
145             }
146         }
147     }
148 
149     /**
150      * Internal runnable that executes a close on a cameraDevice.
151      */
152     private static class CloseCameraRunnable implements Runnable {
153         private final SingleDeviceCloseListener mCloseListener;
154         private final CameraProxy mCameraDevice;
155         private final CameraAgent mCameraAgent;
156         private final Logger mLogger;
157 
CloseCameraRunnable(CameraProxy cameraDevice, CameraAgent cameraAgent, SingleDeviceCloseListener closeListener, Logger logger)158         public CloseCameraRunnable(CameraProxy cameraDevice, CameraAgent cameraAgent,
159               SingleDeviceCloseListener closeListener, Logger logger) {
160             mCameraDevice = cameraDevice;
161             mCameraAgent = cameraAgent;
162             mCloseListener = closeListener;
163             mLogger = logger;
164         }
165 
166         @Override
run()167         public void run() {
168             try {
169                 mLogger.i("mCameraAgent.closeCamera(id: " + mCameraDevice.getCameraId() + ")");
170                 mCameraAgent.closeCamera(mCameraDevice, true /* synchronous */);
171                 mCloseListener.onDeviceClosed();
172             } catch (Exception e) {
173                 mCloseListener.onDeviceClosingException(e);
174             }
175         }
176     }
177 
178     /**
179      * Internal callback that provides a camera device to a future.
180      */
181     private static class OpenCameraStateCallback implements CameraOpenCallback {
182         private final SingleDeviceOpenListener<CameraProxy> mOpenListener;
183         private final Logger mLogger;
184         private boolean mHasBeenCalled = false;
185 
OpenCameraStateCallback(SingleDeviceOpenListener<CameraProxy> openListener, Logger logger)186         public OpenCameraStateCallback(SingleDeviceOpenListener<CameraProxy> openListener,
187               Logger logger) {
188             mOpenListener = openListener;
189             mLogger = logger;
190         }
191 
192         @Override
onCameraOpened(CameraProxy camera)193         public void onCameraOpened(CameraProxy camera) {
194             if (!called()) {
195                 mLogger.i("onCameraOpened(id: " + camera.getCameraId() + ")");
196                 mOpenListener.onDeviceOpened(camera);
197             }
198         }
199 
200         @Override
onCameraDisabled(int cameraId)201         public void onCameraDisabled(int cameraId) {
202             if (!called()) {
203                 mLogger.w("onCameraDisabled(id: " + cameraId + ")");
204                 mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
205             }
206         }
207 
208         @Override
onDeviceOpenFailure(int cameraId, String info)209         public void onDeviceOpenFailure(int cameraId, String info) {
210             if (!called()) {
211                 mLogger.e("onDeviceOpenFailure(id: " + cameraId
212                       + ", info: " + info + ")");
213                 mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
214             }
215         }
216 
217         @Override
onDeviceOpenedAlready(int cameraId, String info)218         public void onDeviceOpenedAlready(int cameraId, String info) {
219             if (!called()) {
220                 mLogger.w("onDeviceOpenedAlready(id: " + cameraId
221                       + ", info: " + info + ")");
222                 mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
223             }
224         }
225 
226         @Override
onReconnectionFailure(CameraAgent mgr, String info)227         public void onReconnectionFailure(CameraAgent mgr, String info) {
228             if (!called()) {
229                 mLogger.w("onReconnectionFailure(info: " + info + ")");
230                 mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
231             }
232         }
233 
called()234         private boolean called() {
235             boolean result = mHasBeenCalled;
236             if (!mHasBeenCalled) {
237                 mHasBeenCalled = true;
238             } else {
239                 mLogger.v("Callback was re-executed.");
240             }
241 
242             return result;
243         }
244     }
245 }
246