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