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