• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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;
18 
19 import static com.android.camera.Util.Assert;
20 
21 import android.hardware.Camera.CameraInfo;
22 import android.hardware.Camera.Parameters;
23 import android.os.Build;
24 import android.os.Handler;
25 import android.os.HandlerThread;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.util.Log;
29 
30 import com.android.camera.CameraManager.CameraProxy;
31 
32 import java.io.IOException;
33 
34 /**
35  * The class is used to hold an {@code android.hardware.Camera} instance.
36  *
37  * <p>The {@code open()} and {@code release()} calls are similar to the ones
38  * in {@code android.hardware.Camera}. The difference is if {@code keep()} is
39  * called before {@code release()}, CameraHolder will try to hold the {@code
40  * android.hardware.Camera} instance for a while, so if {@code open()} is
41  * called soon after, we can avoid the cost of {@code open()} in {@code
42  * android.hardware.Camera}.
43  *
44  * <p>This is used in switching between {@code Camera} and {@code VideoCamera}
45  * activities.
46  */
47 public class CameraHolder {
48     private static final String TAG = "CameraHolder";
49     private CameraProxy mCameraDevice;
50     private long mKeepBeforeTime;  // Keep the Camera before this time.
51     private final Handler mHandler;
52     private boolean mCameraOpened;  // true if camera is opened
53     private final int mNumberOfCameras;
54     private int mCameraId = -1;  // current camera id
55     private int mBackCameraId = -1;
56     private int mFrontCameraId = -1;
57     private final CameraInfo[] mInfo;
58     private static CameraProxy mMockCamera[];
59     private static CameraInfo mMockCameraInfo[];
60 
61     // We store the camera parameters when we actually open the device,
62     // so we can restore them in the subsequent open() requests by the user.
63     // This prevents the parameters set by the Camera activity used by
64     // the VideoCamera activity inadvertently.
65     private Parameters mParameters;
66 
67     // Use a singleton.
68     private static CameraHolder sHolder;
instance()69     public static synchronized CameraHolder instance() {
70         if (sHolder == null) {
71             sHolder = new CameraHolder();
72         }
73         return sHolder;
74     }
75 
76     private static final int RELEASE_CAMERA = 1;
77     private class MyHandler extends Handler {
MyHandler(Looper looper)78         MyHandler(Looper looper) {
79             super(looper);
80         }
81 
82         @Override
handleMessage(Message msg)83         public void handleMessage(Message msg) {
84             switch(msg.what) {
85                 case RELEASE_CAMERA:
86                     synchronized (CameraHolder.this) {
87                         // In 'CameraHolder.open', the 'RELEASE_CAMERA' message
88                         // will be removed if it is found in the queue. However,
89                         // there is a chance that this message has been handled
90                         // before being removed. So, we need to add a check
91                         // here:
92                         if (!mCameraOpened) release();
93                     }
94                     break;
95             }
96         }
97     }
98 
injectMockCamera(CameraInfo[] info, CameraProxy[] camera)99     public static void injectMockCamera(CameraInfo[] info, CameraProxy[] camera) {
100         mMockCameraInfo = info;
101         mMockCamera = camera;
102         sHolder = new CameraHolder();
103     }
104 
CameraHolder()105     private CameraHolder() {
106         HandlerThread ht = new HandlerThread("CameraHolder");
107         ht.start();
108         mHandler = new MyHandler(ht.getLooper());
109         if (mMockCameraInfo != null) {
110             mNumberOfCameras = mMockCameraInfo.length;
111             mInfo = mMockCameraInfo;
112         } else {
113             mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();
114             mInfo = new CameraInfo[mNumberOfCameras];
115             for (int i = 0; i < mNumberOfCameras; i++) {
116                 mInfo[i] = new CameraInfo();
117                 android.hardware.Camera.getCameraInfo(i, mInfo[i]);
118             }
119         }
120 
121         // get the first (smallest) back and first front camera id
122         for (int i = 0; i < mNumberOfCameras; i++) {
123             if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) {
124                 mBackCameraId = i;
125             } else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) {
126                 mFrontCameraId = i;
127             }
128         }
129     }
130 
getNumberOfCameras()131     public int getNumberOfCameras() {
132         return mNumberOfCameras;
133     }
134 
getCameraInfo()135     public CameraInfo[] getCameraInfo() {
136         return mInfo;
137     }
138 
open(int cameraId)139     public synchronized CameraProxy open(int cameraId)
140             throws CameraHardwareException {
141         Assert(!mCameraOpened);
142         if (mCameraDevice != null && mCameraId != cameraId) {
143             mCameraDevice.release();
144             mCameraDevice = null;
145             mCameraId = -1;
146         }
147         if (mCameraDevice == null) {
148             try {
149                 Log.v(TAG, "open camera " + cameraId);
150                 if (mMockCameraInfo == null) {
151                     mCameraDevice = CameraManager.instance().cameraOpen(cameraId);
152                 } else {
153                     if (mMockCamera == null)
154                         throw new RuntimeException();
155                     mCameraDevice = mMockCamera[cameraId];
156                 }
157                 mCameraId = cameraId;
158             } catch (RuntimeException e) {
159                 Log.e(TAG, "fail to connect Camera", e);
160                 throw new CameraHardwareException(e);
161             }
162             mParameters = mCameraDevice.getParameters();
163         } else {
164             try {
165                 mCameraDevice.reconnect();
166             } catch (IOException e) {
167                 Log.e(TAG, "reconnect failed.");
168                 throw new CameraHardwareException(e);
169             }
170             mCameraDevice.setParameters(mParameters);
171         }
172         mCameraOpened = true;
173         mHandler.removeMessages(RELEASE_CAMERA);
174         mKeepBeforeTime = 0;
175         return mCameraDevice;
176     }
177 
178     /**
179      * Tries to open the hardware camera. If the camera is being used or
180      * unavailable then return {@code null}.
181      */
tryOpen(int cameraId)182     public synchronized CameraProxy tryOpen(int cameraId) {
183         try {
184             return !mCameraOpened ? open(cameraId) : null;
185         } catch (CameraHardwareException e) {
186             // In eng build, we throw the exception so that test tool
187             // can detect it and report it
188             if ("eng".equals(Build.TYPE)) {
189                 throw new RuntimeException(e);
190             }
191             return null;
192         }
193     }
194 
release()195     public synchronized void release() {
196         Assert(mCameraDevice != null);
197 
198         long now = System.currentTimeMillis();
199         if (now < mKeepBeforeTime) {
200             if (mCameraOpened) {
201                 mCameraOpened = false;
202                 mCameraDevice.stopPreview();
203             }
204             mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
205                     mKeepBeforeTime - now);
206             return;
207         }
208         mCameraOpened = false;
209         mCameraDevice.release();
210         mCameraDevice = null;
211         // We must set this to null because it has a reference to Camera.
212         // Camera has references to the listeners.
213         mParameters = null;
214         mCameraId = -1;
215     }
216 
keep()217     public synchronized void keep() {
218         // We allow mCameraOpened in either state for the convenience of the
219         // calling activity. The activity may not have a chance to call open()
220         // before the user switches to another activity.
221 
222         // Keep the camera instance for 3 seconds.
223         mKeepBeforeTime = System.currentTimeMillis() + 3000;
224     }
225 
getBackCameraId()226     public int getBackCameraId() {
227         return mBackCameraId;
228     }
229 
getFrontCameraId()230     public int getFrontCameraId() {
231         return mFrontCameraId;
232     }
233 }
234