CameraHolder.java revision dd6600e7f0adf322e5a8fcb0ed5389b14655106e
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.Parameters;
22import android.os.Build;
23import android.os.Handler;
24import android.os.HandlerThread;
25import android.os.Looper;
26import android.os.Message;
27import android.util.Log;
28
29import java.io.IOException;
30
31/**
32 * The class is used to hold an {@code android.hardware.Camera} instance.
33 *
34 * <p>The {@code open()} and {@code release()} calls are similar to the ones
35 * in {@code android.hardware.Camera}. The difference is if {@code keep()} is
36 * called before {@code release()}, CameraHolder will try to hold the {@code
37 * android.hardware.Camera} instance for a while, so if {@code open()} is
38 * called soon after, we can avoid the cost of {@code open()} in {@code
39 * android.hardware.Camera}.
40 *
41 * <p>This is used in switching between {@code Camera} and {@code VideoCamera}
42 * activities.
43 */
44public class CameraHolder {
45    private static final String TAG = "CameraHolder";
46    private android.hardware.Camera mCameraDevice;
47    private long mKeepBeforeTime = 0;  // Keep the Camera before this time.
48    private final Handler mHandler;
49    private int mUsers = 0;  // number of open() - number of release()
50
51    // We store the camera parameters when we actually open the device,
52    // so we can restore them in the subsequent open() requests by the user.
53    // This prevents the parameters set by the Camera activity used by
54    // the VideoCamera activity inadvertently.
55    private Parameters mParameters;
56
57    // Use a singleton.
58    private static CameraHolder sHolder;
59    public static synchronized CameraHolder instance() {
60        if (sHolder == null) {
61            sHolder = new CameraHolder();
62        }
63        return sHolder;
64    }
65
66    private static final int RELEASE_CAMERA = 1;
67    private class MyHandler extends Handler {
68        MyHandler(Looper looper) {
69            super(looper);
70        }
71
72        @Override
73        public void handleMessage(Message msg) {
74            switch(msg.what) {
75                case RELEASE_CAMERA:
76                    synchronized (CameraHolder.this) {
77                        // In 'CameraHolder.open', the 'RELEASE_CAMERA' message
78                        // will be removed if it is found in the queue. However,
79                        // there is a chance that this message has been handled
80                        // before being removed. So, we need to add a check
81                        // here:
82                        if (CameraHolder.this.mUsers == 0) releaseCamera();
83                    }
84                    break;
85            }
86        }
87    }
88
89    private CameraHolder() {
90        HandlerThread ht = new HandlerThread("CameraHolder");
91        ht.start();
92        mHandler = new MyHandler(ht.getLooper());
93    }
94
95    public synchronized android.hardware.Camera open()
96            throws CameraHardwareException {
97        Assert(mUsers == 0);
98        if (mCameraDevice == null) {
99            try {
100                mCameraDevice = android.hardware.Camera.open();
101            } catch (RuntimeException e) {
102                Log.e(TAG, "fail to connect Camera", e);
103                throw new CameraHardwareException(e);
104            }
105            mParameters = mCameraDevice.getParameters();
106        } else {
107            try {
108                mCameraDevice.reconnect();
109            } catch (IOException e) {
110                Log.e(TAG, "reconnect failed.");
111                throw new CameraHardwareException(e);
112            }
113            mCameraDevice.setParameters(mParameters);
114        }
115        ++mUsers;
116        mHandler.removeMessages(RELEASE_CAMERA);
117        mKeepBeforeTime = 0;
118        return mCameraDevice;
119    }
120
121    /**
122     * Tries to open the hardware camera. If the camera is being used or
123     * unavailable then return {@code null}.
124     */
125    public synchronized android.hardware.Camera tryOpen() {
126        try {
127            return mUsers == 0 ? open() : null;
128        } catch (CameraHardwareException e) {
129            // In eng build, we throw the exception so that test tool
130            // can detect it and report it
131            if ("eng".equals(Build.TYPE)) {
132                throw new RuntimeException(e);
133            }
134            return null;
135        }
136    }
137
138    public synchronized void release() {
139        Assert(mUsers == 1);
140        --mUsers;
141        mCameraDevice.stopPreview();
142        releaseCamera();
143    }
144
145    private synchronized void releaseCamera() {
146        Assert(mUsers == 0);
147        Assert(mCameraDevice != null);
148        long now = System.currentTimeMillis();
149        if (now < mKeepBeforeTime) {
150            mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
151                    mKeepBeforeTime - now);
152            return;
153        }
154        mCameraDevice.release();
155        mCameraDevice = null;
156    }
157
158    public synchronized void keep() {
159        // We allow (mUsers == 0) for the convenience of the calling activity.
160        // The activity may not have a chance to call open() before the user
161        // choose the menu item to switch to another activity.
162        Assert(mUsers == 1 || mUsers == 0);
163        // Keep the camera instance for 3 seconds.
164        mKeepBeforeTime = System.currentTimeMillis() + 3000;
165    }
166}
167