CameraHolder.java revision 1df224649e0663c4bc6366f299e060d5967f526d
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;  // Keep the Camera before this time.
49    private final Handler mHandler;
50    private boolean mCameraOpened;  // true if camera is opened
51    private final int mNumberOfCameras;
52    private int mCameraId = -1;  // current camera id
53    private int mBackCameraId = -1;
54    private int mFrontCameraId = -1;
55    private final CameraInfo[] mInfo;
56    private static CameraDevice mMockCamera[];
57    private static CameraInfo mMockCameraInfo[];
58
59    // We store the camera parameters when we actually open the device,
60    // so we can restore them in the subsequent open() requests by the user.
61    // This prevents the parameters set by the Camera activity used by
62    // the VideoCamera activity inadvertently.
63    private Parameters mParameters;
64
65    // Use a singleton.
66    private static CameraHolder sHolder;
67    public static synchronized CameraHolder instance() {
68        if (sHolder == null) {
69            sHolder = new CameraHolder();
70        }
71        return sHolder;
72    }
73
74    private static final int RELEASE_CAMERA = 1;
75    private class MyHandler extends Handler {
76        MyHandler(Looper looper) {
77            super(looper);
78        }
79
80        @Override
81        public void handleMessage(Message msg) {
82            switch(msg.what) {
83                case RELEASE_CAMERA:
84                    synchronized (CameraHolder.this) {
85                        // In 'CameraHolder.open', the 'RELEASE_CAMERA' message
86                        // will be removed if it is found in the queue. However,
87                        // there is a chance that this message has been handled
88                        // before being removed. So, we need to add a check
89                        // here:
90                        if (!CameraHolder.this.mCameraOpened) releaseCamera();
91                    }
92                    break;
93            }
94        }
95    }
96
97    public static void injectMockCamera(CameraInfo[] info, CameraDevice[] camera) {
98        mMockCameraInfo = info;
99        mMockCamera = camera;
100        sHolder = new CameraHolder();
101    }
102
103    private CameraHolder() {
104        HandlerThread ht = new HandlerThread("CameraHolder");
105        ht.start();
106        mHandler = new MyHandler(ht.getLooper());
107        if (mMockCameraInfo != null) {
108            mNumberOfCameras = mMockCameraInfo.length;
109            mInfo = mMockCameraInfo;
110        } else {
111            mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();
112            mInfo = new CameraInfo[mNumberOfCameras];
113            for (int i = 0; i < mNumberOfCameras; i++) {
114                mInfo[i] = new CameraInfo();
115                android.hardware.Camera.getCameraInfo(i, mInfo[i]);
116            }
117        }
118
119        // get the first (smallest) back and first front camera id
120        for (int i = 0; i < mNumberOfCameras; i++) {
121            if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) {
122                mBackCameraId = i;
123            } else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) {
124                mFrontCameraId = i;
125            }
126        }
127    }
128
129    public int getNumberOfCameras() {
130        return mNumberOfCameras;
131    }
132
133    public CameraInfo[] getCameraInfo() {
134        return mInfo;
135    }
136
137    public synchronized CameraDevice open(int cameraId)
138            throws CameraHardwareException {
139        Assert(!mCameraOpened);
140        if (mCameraDevice != null && mCameraId != cameraId) {
141            mCameraDevice.release();
142            mCameraDevice = null;
143            mCameraId = -1;
144        }
145        if (mCameraDevice == null) {
146            try {
147                Log.v(TAG, "open camera " + cameraId);
148                if (mMockCameraInfo == null) {
149                    android.hardware.Camera camera = android.hardware.Camera.open(cameraId);
150                    if (camera != null)
151                        mCameraDevice = new RealCamera(camera);
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     */
182    public synchronized CameraDevice 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
195    public synchronized void release() {
196        Assert(mCameraOpened);
197        mCameraOpened = false;
198        mCameraDevice.stopPreview();
199        releaseCamera();
200    }
201
202    private synchronized void releaseCamera() {
203        Assert(!mCameraOpened);
204        Assert(mCameraDevice != null);
205        long now = System.currentTimeMillis();
206        if (now < mKeepBeforeTime) {
207            mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
208                    mKeepBeforeTime - now);
209            return;
210        }
211        mCameraDevice.release();
212        mCameraDevice = null;
213        // We must set this to null because it has a reference to Camera.
214        // Camera has references to the listeners.
215        mParameters = null;
216        mCameraId = -1;
217    }
218
219    public synchronized void keep() {
220        // We allow mCameraOpened in either state for the convenience of the calling activity.
221        // The activity may not have a chance to call open() before the user
222        // choose the menu item to switch to another activity.
223
224        // Keep the camera instance for 3 seconds.
225        mKeepBeforeTime = System.currentTimeMillis() + 3000;
226    }
227
228    public int getBackCameraId() {
229        return mBackCameraId;
230    }
231
232    public int getFrontCameraId() {
233        return mFrontCameraId;
234    }
235}
236