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
30475b4c6eca6f7e7ca0e4b3ee0ee234c6fe079687Pin Tingimport com.android.camera.CameraManager.CameraProxy;
31475b4c6eca6f7e7ca0e4b3ee0ee234c6fe079687Pin Ting
32cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport java.io.IOException;
33f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Changimport java.text.SimpleDateFormat;
34f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Changimport java.util.ArrayList;
35f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Changimport java.util.Date;
36cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
37271b3095b9f763421c0547109da9de774795072dChih-Chung Chang/**
38271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * The class is used to hold an {@code android.hardware.Camera} instance.
39271b3095b9f763421c0547109da9de774795072dChih-Chung Chang *
40271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>The {@code open()} and {@code release()} calls are similar to the ones
41271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * in {@code android.hardware.Camera}. The difference is if {@code keep()} is
42271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called before {@code release()}, CameraHolder will try to hold the {@code
43271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera} instance for a while, so if {@code open()} is
44271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called soon after, we can avoid the cost of {@code open()} in {@code
45271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera}.
46271b3095b9f763421c0547109da9de774795072dChih-Chung Chang *
47ed19156e705efb982b351653a3ea9b15f5a08df7Wu-cheng Li * <p>This is used in switching between different modules.
48271b3095b9f763421c0547109da9de774795072dChih-Chung Chang */
49cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changpublic class CameraHolder {
50cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static final String TAG = "CameraHolder";
517717d2f0fc0e1393971cab44b3bba8a0f1297835Wu-cheng Li    private static final int KEEP_CAMERA_TIMEOUT = 3000; // 3 seconds
52475b4c6eca6f7e7ca0e4b3ee0ee234c6fe079687Pin Ting    private CameraProxy mCameraDevice;
531df224649e0663c4bc6366f299e060d5967f526dPin Ting    private long mKeepBeforeTime;  // Keep the Camera before this time.
543f3c857e3f34650c15d764810335024654b0fcc3Owen Lin    private final Handler mHandler;
551df224649e0663c4bc6366f299e060d5967f526dPin Ting    private boolean mCameraOpened;  // true if camera is opened
561df224649e0663c4bc6366f299e060d5967f526dPin Ting    private final int mNumberOfCameras;
57003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li    private int mCameraId = -1;  // current camera id
581df224649e0663c4bc6366f299e060d5967f526dPin Ting    private int mBackCameraId = -1;
591df224649e0663c4bc6366f299e060d5967f526dPin Ting    private int mFrontCameraId = -1;
601df224649e0663c4bc6366f299e060d5967f526dPin Ting    private final CameraInfo[] mInfo;
61475b4c6eca6f7e7ca0e4b3ee0ee234c6fe079687Pin Ting    private static CameraProxy mMockCamera[];
6219c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li    private static CameraInfo mMockCameraInfo[];
63cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
64f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    /* Debug double-open issue */
65f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    private static final boolean DEBUG_OPEN_RELEASE = true;
66f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    private static class OpenReleaseState {
67f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        long time;
68f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        int id;
69f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        String device;
70f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        String[] stack;
71f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    }
72f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    private static ArrayList<OpenReleaseState> sOpenReleaseStates =
73f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            new ArrayList<OpenReleaseState>();
74f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    private static SimpleDateFormat sDateFormat = new SimpleDateFormat(
75f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            "yyyy-MM-dd HH:mm:ss.SSS");
76f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang
77f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    private static synchronized void collectState(int id, CameraProxy device) {
78f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        OpenReleaseState s = new OpenReleaseState();
79f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        s.time = System.currentTimeMillis();
80f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        s.id = id;
81f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        if (device == null) {
82f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            s.device = "(null)";
83f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        } else {
84f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            s.device = device.toString();
85f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        }
86f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang
87f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        StackTraceElement[] stack = Thread.currentThread().getStackTrace();
88f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        String[] lines = new String[stack.length];
89f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        for (int i = 0; i < stack.length; i++) {
90f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            lines[i] = stack[i].toString();
91f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        }
92f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        s.stack = lines;
93f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang
94f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        if (sOpenReleaseStates.size() > 10) {
95f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            sOpenReleaseStates.remove(0);
96f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        }
97f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        sOpenReleaseStates.add(s);
98f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    }
99f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang
100f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    private static synchronized void dumpStates() {
101f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        for (int i = sOpenReleaseStates.size() - 1; i >= 0; i--) {
102f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            OpenReleaseState s = sOpenReleaseStates.get(i);
103f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            String date = sDateFormat.format(new Date(s.time));
104f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            Log.d(TAG, "State " + i + " at " + date);
105f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            Log.d(TAG, "mCameraId = " + s.id + ", mCameraDevice = " + s.device);
106f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            Log.d(TAG, "Stack:");
107f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            for (int j = 0; j < s.stack.length; j++) {
108f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang                Log.d(TAG, "  " + s.stack[j]);
109f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            }
110f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        }
111f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang    }
112f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang
1134c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // We store the camera parameters when we actually open the device,
1144c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // so we can restore them in the subsequent open() requests by the user.
115ed19156e705efb982b351653a3ea9b15f5a08df7Wu-cheng Li    // This prevents the parameters set by PhotoModule used by VideoModule
116ed19156e705efb982b351653a3ea9b15f5a08df7Wu-cheng Li    // inadvertently.
1174c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    private Parameters mParameters;
1184c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang
119cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    // Use a singleton.
120cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static CameraHolder sHolder;
121cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public static synchronized CameraHolder instance() {
122cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        if (sHolder == null) {
123cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            sHolder = new CameraHolder();
124cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
125cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        return sHolder;
126cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
127cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
128cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static final int RELEASE_CAMERA = 1;
129cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private class MyHandler extends Handler {
130cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        MyHandler(Looper looper) {
131cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            super(looper);
132cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
133cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
134cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        @Override
135cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        public void handleMessage(Message msg) {
136cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            switch(msg.what) {
137cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                case RELEASE_CAMERA:
138dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                    synchronized (CameraHolder.this) {
139dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // In 'CameraHolder.open', the 'RELEASE_CAMERA' message
140dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // will be removed if it is found in the queue. However,
141dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // there is a chance that this message has been handled
142dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // before being removed. So, we need to add a check
143dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // here:
1448147561670f166bc090e60cbb271555cde86a9c9Wu-cheng Li                        if (!mCameraOpened) release();
145dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                    }
146cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                    break;
147cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            }
148cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
149cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
150cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
151475b4c6eca6f7e7ca0e4b3ee0ee234c6fe079687Pin Ting    public static void injectMockCamera(CameraInfo[] info, CameraProxy[] camera) {
15219c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li        mMockCameraInfo = info;
15319c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li        mMockCamera = camera;
15475aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5Wu-cheng Li        sHolder = new CameraHolder();
15519c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li    }
15619c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li
157cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private CameraHolder() {
158cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        HandlerThread ht = new HandlerThread("CameraHolder");
159cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        ht.start();
160cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mHandler = new MyHandler(ht.getLooper());
16119c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li        if (mMockCameraInfo != null) {
16219c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li            mNumberOfCameras = mMockCameraInfo.length;
16319c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li            mInfo = mMockCameraInfo;
16475aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5Wu-cheng Li        } else {
16575aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5Wu-cheng Li            mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();
16675aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5Wu-cheng Li            mInfo = new CameraInfo[mNumberOfCameras];
16775aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5Wu-cheng Li            for (int i = 0; i < mNumberOfCameras; i++) {
16875aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5Wu-cheng Li                mInfo[i] = new CameraInfo();
16975aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5Wu-cheng Li                android.hardware.Camera.getCameraInfo(i, mInfo[i]);
17075aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5Wu-cheng Li            }
17119c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li        }
1721df224649e0663c4bc6366f299e060d5967f526dPin Ting
1731df224649e0663c4bc6366f299e060d5967f526dPin Ting        // get the first (smallest) back and first front camera id
174ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        for (int i = 0; i < mNumberOfCameras; i++) {
175003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li            if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) {
176003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li                mBackCameraId = i;
1771df224649e0663c4bc6366f299e060d5967f526dPin Ting            } else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) {
178003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li                mFrontCameraId = i;
179003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li            }
180ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        }
181ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    }
182ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang
183ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public int getNumberOfCameras() {
184ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        return mNumberOfCameras;
185cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
186cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
1877add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li    public CameraInfo[] getCameraInfo() {
1887add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li        return mInfo;
1897add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li    }
1907add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li
191475b4c6eca6f7e7ca0e4b3ee0ee234c6fe079687Pin Ting    public synchronized CameraProxy open(int cameraId)
1923f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            throws CameraHardwareException {
193f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        if (DEBUG_OPEN_RELEASE) {
194f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            collectState(cameraId, mCameraDevice);
195f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            if (mCameraOpened) {
196f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang                Log.e(TAG, "double open");
197f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang                dumpStates();
198f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            }
199f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        }
2001df224649e0663c4bc6366f299e060d5967f526dPin Ting        Assert(!mCameraOpened);
20143b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li        if (mCameraDevice != null && mCameraId != cameraId) {
20243b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li            mCameraDevice.release();
20343b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li            mCameraDevice = null;
20443b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li            mCameraId = -1;
20543b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li        }
206cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        if (mCameraDevice == null) {
2073f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            try {
208ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang                Log.v(TAG, "open camera " + cameraId);
20919c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li                if (mMockCameraInfo == null) {
210475b4c6eca6f7e7ca0e4b3ee0ee234c6fe079687Pin Ting                    mCameraDevice = CameraManager.instance().cameraOpen(cameraId);
21119c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li                } else {
21219c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li                    if (mMockCamera == null)
21319c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li                        throw new RuntimeException();
21419c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li                    mCameraDevice = mMockCamera[cameraId];
21519c0b7882d14679bc0fe5d25c4e1ea34350d0b8dWu-cheng Li                }
21643b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li                mCameraId = cameraId;
2173f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            } catch (RuntimeException e) {
2183f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                Log.e(TAG, "fail to connect Camera", e);
219f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new CameraHardwareException(e);
2203f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            }
2214c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang            mParameters = mCameraDevice.getParameters();
222cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        } else {
223cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            try {
224cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                mCameraDevice.reconnect();
225cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            } catch (IOException e) {
226cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                Log.e(TAG, "reconnect failed.");
227f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new CameraHardwareException(e);
228cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            }
2294c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang            mCameraDevice.setParameters(mParameters);
230cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
2311df224649e0663c4bc6366f299e060d5967f526dPin Ting        mCameraOpened = true;
232cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mHandler.removeMessages(RELEASE_CAMERA);
23391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        mKeepBeforeTime = 0;
234cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        return mCameraDevice;
235cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
236cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
237d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin    /**
238d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     * Tries to open the hardware camera. If the camera is being used or
239d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     * unavailable then return {@code null}.
240d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     */
241475b4c6eca6f7e7ca0e4b3ee0ee234c6fe079687Pin Ting    public synchronized CameraProxy tryOpen(int cameraId) {
242d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        try {
2431df224649e0663c4bc6366f299e060d5967f526dPin Ting            return !mCameraOpened ? open(cameraId) : null;
244d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        } catch (CameraHardwareException e) {
245f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            // In eng build, we throw the exception so that test tool
246f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            // can detect it and report it
247f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            if ("eng".equals(Build.TYPE)) {
248f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new RuntimeException(e);
249f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            }
250d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin            return null;
251d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        }
252d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin    }
253d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin
254cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public synchronized void release() {
255f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        if (DEBUG_OPEN_RELEASE) {
256f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang            collectState(mCameraId, mCameraDevice);
257f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang        }
258f33fc45cc2aa5c7888e7a8aa8a9e49bc960237d7Chih-Chung Chang
259c0ef4802f17a76045b1d32586b963fe90bb5460fBobby Georgescu        if (mCameraDevice == null) return;
2608147561670f166bc090e60cbb271555cde86a9c9Wu-cheng Li
261cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        long now = System.currentTimeMillis();
26291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        if (now < mKeepBeforeTime) {
2638147561670f166bc090e60cbb271555cde86a9c9Wu-cheng Li            if (mCameraOpened) {
2648147561670f166bc090e60cbb271555cde86a9c9Wu-cheng Li                mCameraOpened = false;
2658147561670f166bc090e60cbb271555cde86a9c9Wu-cheng Li                mCameraDevice.stopPreview();
2668147561670f166bc090e60cbb271555cde86a9c9Wu-cheng Li            }
267cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
26891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang                    mKeepBeforeTime - now);
269cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            return;
270cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
2718147561670f166bc090e60cbb271555cde86a9c9Wu-cheng Li        mCameraOpened = false;
272cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice.release();
273cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice = null;
274788aad96ea4abcda2469867464139daa8087af65Wu-cheng Li        // We must set this to null because it has a reference to Camera.
275788aad96ea4abcda2469867464139daa8087af65Wu-cheng Li        // Camera has references to the listeners.
276788aad96ea4abcda2469867464139daa8087af65Wu-cheng Li        mParameters = null;
27743b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li        mCameraId = -1;
278cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
279cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
2807717d2f0fc0e1393971cab44b3bba8a0f1297835Wu-cheng Li    public void keep() {
2817717d2f0fc0e1393971cab44b3bba8a0f1297835Wu-cheng Li        keep(KEEP_CAMERA_TIMEOUT);
2827717d2f0fc0e1393971cab44b3bba8a0f1297835Wu-cheng Li    }
2837717d2f0fc0e1393971cab44b3bba8a0f1297835Wu-cheng Li
2847717d2f0fc0e1393971cab44b3bba8a0f1297835Wu-cheng Li    public synchronized void keep(int time) {
28524e4e6cc25b7628ef15eff703f70b2872575a4ccWu-cheng Li        // We allow mCameraOpened in either state for the convenience of the
28624e4e6cc25b7628ef15eff703f70b2872575a4ccWu-cheng Li        // calling activity. The activity may not have a chance to call open()
28724e4e6cc25b7628ef15eff703f70b2872575a4ccWu-cheng Li        // before the user switches to another activity.
2887717d2f0fc0e1393971cab44b3bba8a0f1297835Wu-cheng Li        mKeepBeforeTime = System.currentTimeMillis() + time;
289cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
290003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li
291003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li    public int getBackCameraId() {
292003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li        return mBackCameraId;
293003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li    }
294003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li
295003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li    public int getFrontCameraId() {
296003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li        return mFrontCameraId;
297003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li    }
298cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang}
299