CameraHolder.java revision 7add00693c1ec910bc8700fe046ee18cbe4e1148
191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang/*
291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Copyright (C) 2009 The Android Open Source Project
391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang *
491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License");
591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * you may not use this file except in compliance with the License.
691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * You may obtain a copy of the License at
791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang *
891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang *      http://www.apache.org/licenses/LICENSE-2.0
991acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang *
1091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Unless required by applicable law or agreed to in writing, software
1191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS,
1291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * See the License for the specific language governing permissions and
1491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * limitations under the License.
1591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang */
1691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang
17cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changpackage com.android.camera;
18cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
193f3c857e3f34650c15d764810335024654b0fcc3Owen Linimport static com.android.camera.Util.Assert;
203f3c857e3f34650c15d764810335024654b0fcc3Owen Lin
217add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Liimport android.hardware.Camera.CameraInfo;
224c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Changimport android.hardware.Camera.Parameters;
23f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Linimport android.os.Build;
24cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Handler;
25cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.HandlerThread;
26cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Looper;
27cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Message;
28cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.util.Log;
29cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
30cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport java.io.IOException;
31cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
32271b3095b9f763421c0547109da9de774795072dChih-Chung Chang/**
33271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * The class is used to hold an {@code android.hardware.Camera} instance.
34271b3095b9f763421c0547109da9de774795072dChih-Chung Chang *
35271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>The {@code open()} and {@code release()} calls are similar to the ones
36271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * in {@code android.hardware.Camera}. The difference is if {@code keep()} is
37271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called before {@code release()}, CameraHolder will try to hold the {@code
38271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera} instance for a while, so if {@code open()} is
39271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called soon after, we can avoid the cost of {@code open()} in {@code
40271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera}.
41271b3095b9f763421c0547109da9de774795072dChih-Chung Chang *
42271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>This is used in switching between {@code Camera} and {@code VideoCamera}
43271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * activities.
44271b3095b9f763421c0547109da9de774795072dChih-Chung Chang */
45cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changpublic class CameraHolder {
46cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static final String TAG = "CameraHolder";
47cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private android.hardware.Camera mCameraDevice;
4891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang    private long mKeepBeforeTime = 0;  // Keep the Camera before this time.
493f3c857e3f34650c15d764810335024654b0fcc3Owen Lin    private final Handler mHandler;
5091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang    private int mUsers = 0;  // number of open() - number of release()
51ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    private int mNumberOfCameras;
527add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li    private CameraInfo[] mInfo;
53cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
544c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // We store the camera parameters when we actually open the device,
554c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // so we can restore them in the subsequent open() requests by the user.
564c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // This prevents the parameters set by the Camera activity used by
574c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // the VideoCamera activity inadvertently.
584c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    private Parameters mParameters;
594c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang
60cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    // Use a singleton.
61cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static CameraHolder sHolder;
62cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public static synchronized CameraHolder instance() {
63cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        if (sHolder == null) {
64cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            sHolder = new CameraHolder();
65cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
66cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        return sHolder;
67cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
68cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
69cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static final int RELEASE_CAMERA = 1;
70cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private class MyHandler extends Handler {
71cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        MyHandler(Looper looper) {
72cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            super(looper);
73cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
74cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
75cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        @Override
76cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        public void handleMessage(Message msg) {
77cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            switch(msg.what) {
78cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                case RELEASE_CAMERA:
79dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                    synchronized (CameraHolder.this) {
80dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // In 'CameraHolder.open', the 'RELEASE_CAMERA' message
81dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // will be removed if it is found in the queue. However,
82dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // there is a chance that this message has been handled
83dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // before being removed. So, we need to add a check
84dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // here:
85dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        if (CameraHolder.this.mUsers == 0) releaseCamera();
86dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                    }
87cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                    break;
88cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            }
89cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
90cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
91cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
92cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private CameraHolder() {
93cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        HandlerThread ht = new HandlerThread("CameraHolder");
94cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        ht.start();
95cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mHandler = new MyHandler(ht.getLooper());
96ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();
977add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li        mInfo = new CameraInfo[mNumberOfCameras];
98ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        for (int i = 0; i < mNumberOfCameras; i++) {
997add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li            mInfo[i] = new CameraInfo();
1007add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li            android.hardware.Camera.getCameraInfo(i, mInfo[i]);
101ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        }
102ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    }
103ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang
104ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public int getNumberOfCameras() {
105ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        return mNumberOfCameras;
106cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
107cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
1087add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li    public CameraInfo[] getCameraInfo() {
1097add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li        return mInfo;
1107add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li    }
1117add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li
112ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public synchronized android.hardware.Camera open(int cameraId)
1133f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            throws CameraHardwareException {
11491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 0);
115cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        if (mCameraDevice == null) {
1163f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            try {
117ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang                Log.v(TAG, "open camera " + cameraId);
118ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang                mCameraDevice = android.hardware.Camera.open(cameraId);
1193f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            } catch (RuntimeException e) {
1203f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                Log.e(TAG, "fail to connect Camera", e);
121f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new CameraHardwareException(e);
1223f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            }
1234c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang            mParameters = mCameraDevice.getParameters();
124cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        } else {
125cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            try {
126cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                mCameraDevice.reconnect();
127cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            } catch (IOException e) {
128cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                Log.e(TAG, "reconnect failed.");
129f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new CameraHardwareException(e);
130cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            }
1314c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang            mCameraDevice.setParameters(mParameters);
132cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
13391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        ++mUsers;
134cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mHandler.removeMessages(RELEASE_CAMERA);
13591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        mKeepBeforeTime = 0;
136cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        return mCameraDevice;
137cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
138cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
139d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin    /**
140d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     * Tries to open the hardware camera. If the camera is being used or
141d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     * unavailable then return {@code null}.
142d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     */
143ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public synchronized android.hardware.Camera tryOpen(int cameraId) {
144d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        try {
145ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang            return mUsers == 0 ? open(cameraId) : null;
146d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        } catch (CameraHardwareException e) {
147f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            // In eng build, we throw the exception so that test tool
148f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            // can detect it and report it
149f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            if ("eng".equals(Build.TYPE)) {
150f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new RuntimeException(e);
151f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            }
152d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin            return null;
153d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        }
154d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin    }
155d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin
156cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public synchronized void release() {
15791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 1);
15891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        --mUsers;
159cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice.stopPreview();
160cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        releaseCamera();
161cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
162cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
163cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private synchronized void releaseCamera() {
16491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 0);
165cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        Assert(mCameraDevice != null);
166cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        long now = System.currentTimeMillis();
16791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        if (now < mKeepBeforeTime) {
168cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
16991acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang                    mKeepBeforeTime - now);
170cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            return;
171cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
172cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice.release();
173cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice = null;
174cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
175cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
176cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public synchronized void keep() {
17791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        // We allow (mUsers == 0) for the convenience of the calling activity.
1785075f53df86f3dcb0a46fd6353057260ad480f43Chih-Chung Chang        // The activity may not have a chance to call open() before the user
1795075f53df86f3dcb0a46fd6353057260ad480f43Chih-Chung Chang        // choose the menu item to switch to another activity.
18091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 1 || mUsers == 0);
181cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        // Keep the camera instance for 3 seconds.
18291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        mKeepBeforeTime = System.currentTimeMillis() + 3000;
183cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
184cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang}
185