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