CameraManager.java revision feb50af361e4305a25758966b6b5df2738c00259
1b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala/*
2b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * Copyright (C) 2013 The Android Open Source Project
3b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala *
4b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * Licensed under the Apache License, Version 2.0 (the "License");
5b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * you may not use this file except in compliance with the License.
6b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * You may obtain a copy of the License at
7b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala *
8b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala *      http://www.apache.org/licenses/LICENSE-2.0
9b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala *
10b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * Unless required by applicable law or agreed to in writing, software
11b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * distributed under the License is distributed on an "AS IS" BASIS,
12b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * See the License for the specific language governing permissions and
14b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * limitations under the License.
15b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala */
16b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
172f1a2e423e0fbb64467d6fcfa4e82c6384f31210Eino-Ville Talvalapackage android.hardware.camera2;
18b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
19e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkinimport android.content.Context;
20e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkinimport android.hardware.ICameraService;
21e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkinimport android.hardware.ICameraServiceListener;
2270c2207c34cf0e6b3b383b1b1500ff5385aa51a6Eino-Ville Talvalaimport android.hardware.camera2.impl.CameraMetadataNative;
23feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.hardware.camera2.legacy.CameraDeviceUserShim;
242f1a2e423e0fbb64467d6fcfa4e82c6384f31210Eino-Ville Talvalaimport android.hardware.camera2.utils.CameraBinderDecorator;
252f1a2e423e0fbb64467d6fcfa4e82c6384f31210Eino-Ville Talvalaimport android.hardware.camera2.utils.CameraRuntimeException;
2666ef64514464a1bb9c7931993cc8db3d7539f39aRuben Brunkimport android.hardware.camera2.utils.BinderHolder;
27e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkinimport android.os.IBinder;
284af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvalaimport android.os.Handler;
294af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvalaimport android.os.Looper;
30e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkinimport android.os.RemoteException;
31e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkinimport android.os.ServiceManager;
32e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkinimport android.util.Log;
334af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvalaimport android.util.ArrayMap;
34e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
35e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkinimport java.util.ArrayList;
36e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
37b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala/**
38b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * <p>An interface for iterating, listing, and connecting to
39b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * {@link CameraDevice CameraDevices}.</p>
40b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala *
41b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * <p>You can get an instance of this class by calling
42b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p>
43b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala *
44b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre>
45b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala *
46b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * <p>For more details about communicating with camera devices, read the Camera
472f1a2e423e0fbb64467d6fcfa4e82c6384f31210Eino-Ville Talvala * developer guide or the {@link android.hardware.camera2 camera2}
48b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala * package documentation.</p>
49b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala */
50b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvalapublic final class CameraManager {
51b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
5285c4388de1fea3d45783f07895c2b113c4cc1ba5Ruben Brunk    private static final String TAG = "CameraManager";
5385c4388de1fea3d45783f07895c2b113c4cc1ba5Ruben Brunk
54b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    /**
55e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin     * This should match the ICameraService definition
56e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin     */
57e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
58e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    private static final int USE_CALLING_UID = -1;
59e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
60e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    private final ICameraService mCameraService;
61e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    private ArrayList<String> mDeviceIdList;
624af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala
635c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin    private final ArrayMap<AvailabilityListener, Handler> mListenerMap =
644af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            new ArrayMap<AvailabilityListener, Handler>();
654af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala
66e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    private final Context mContext;
67e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    private final Object mLock = new Object();
68e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
69e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    /**
70b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @hide
71b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     */
72e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    public CameraManager(Context context) {
73e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        mContext = context;
74e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
75e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
76e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
77e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
78e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        /**
79e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin         * Wrap the camera service in a decorator which automatically translates return codes
80e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin         * into exceptions, and RemoteExceptions into other exceptions.
81e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin         */
82e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        mCameraService = CameraBinderDecorator.newInstance(cameraServiceRaw);
83e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
84e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        try {
855614cbe64493308dc5330eac5d5ba17202013dc4Igor Murashkin            CameraBinderDecorator.throwOnError(
865614cbe64493308dc5330eac5d5ba17202013dc4Igor Murashkin                    CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
8785c4388de1fea3d45783f07895c2b113c4cc1ba5Ruben Brunk        } catch(CameraRuntimeException e) {
8885c4388de1fea3d45783f07895c2b113c4cc1ba5Ruben Brunk            throw new IllegalStateException("Failed to setup camera vendor tags",
8985c4388de1fea3d45783f07895c2b113c4cc1ba5Ruben Brunk                    e.asChecked());
9085c4388de1fea3d45783f07895c2b113c4cc1ba5Ruben Brunk        }
9185c4388de1fea3d45783f07895c2b113c4cc1ba5Ruben Brunk
9285c4388de1fea3d45783f07895c2b113c4cc1ba5Ruben Brunk        try {
93e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            mCameraService.addListener(new CameraServiceListener());
94e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        } catch(CameraRuntimeException e) {
95e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            throw new IllegalStateException("Failed to register a camera service listener",
96e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    e.asChecked());
97e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        } catch (RemoteException e) {
98e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            // impossible
99e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
100b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    }
101b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
102b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    /**
1034af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * Return the list of currently connected camera devices by
1044af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * identifier.
1054af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     *
1064af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * <p>Non-removable cameras use integers starting at 0 for their
107b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * identifiers, while removable cameras have a unique identifier for each
108b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * individual device, even if they are the same model.</p>
109b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
110b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @return The list of currently connected camera devices.
111b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     */
1124af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala    public String[] getCameraIdList() throws CameraAccessException {
113e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        synchronized (mLock) {
11470725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin            try {
11570725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin                return getOrCreateDeviceIdListLocked().toArray(new String[0]);
11670725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin            } catch(CameraAccessException e) {
11770725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin                // this should almost never happen, except if mediaserver crashes
11870725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin                throw new IllegalStateException(
11970725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin                        "Failed to query camera service for device ID list", e);
12070725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin            }
121e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
122b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    }
123b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
124b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    /**
125b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * Register a listener to be notified about camera device availability.
126b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
1274af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * <p>Registering the same listener again will replace the handler with the
1284af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * new one provided.</p>
129e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin     *
13024eb8a313f1a80a35566b8dc456ec2684a05a22eBenjamin Hendricks     * @param listener The new listener to send camera availability notices to
1314af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * @param handler The handler on which the listener should be invoked, or
1324af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * {@code null} to use the current thread's {@link android.os.Looper looper}.
133b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     */
1344af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala    public void addAvailabilityListener(AvailabilityListener listener, Handler handler) {
1354af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala        if (handler == null) {
1364af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            Looper looper = Looper.myLooper();
1374af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            if (looper == null) {
1384af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                throw new IllegalArgumentException(
1394af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                        "No handler given, and current thread has no looper!");
1404af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            }
1414af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            handler = new Handler(looper);
1424af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala        }
1434af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala
144e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        synchronized (mLock) {
1454af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            mListenerMap.put(listener, handler);
146e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
147b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    }
148b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
149b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    /**
150b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * Remove a previously-added listener; the listener will no longer receive
151b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * connection and disconnection callbacks.
152b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
1534af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * <p>Removing a listener that isn't registered has no effect.</p>
154e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin     *
15524eb8a313f1a80a35566b8dc456ec2684a05a22eBenjamin Hendricks     * @param listener The listener to remove from the notification list
156b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     */
1574af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala    public void removeAvailabilityListener(AvailabilityListener listener) {
158e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        synchronized (mLock) {
1594af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            mListenerMap.remove(listener);
160e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
161b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    }
162b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
163b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    /**
164b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * <p>Query the capabilities of a camera device. These capabilities are
165b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * immutable for a given camera.</p>
166b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
167b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @param cameraId The id of the camera device to query
168b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @return The properties of the given camera
169b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
170b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @throws IllegalArgumentException if the cameraId does not match any
171b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * currently connected camera device.
172b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @throws CameraAccessException if the camera is disabled by device policy.
173b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @throws SecurityException if the application does not have permission to
174b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * access the camera
175b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
1764af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * @see #getCameraIdList
177b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
178b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     */
17968f40066c914aefc1f88819dd46dd1135fb9f5bcIgor Murashkin    public CameraCharacteristics getCameraCharacteristics(String cameraId)
180b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala            throws CameraAccessException {
181e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
182e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        synchronized (mLock) {
183e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
184e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
185e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                        " currently connected camera device", cameraId));
186e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            }
187e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
188e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
1892001188be30649198972a3199a4322d6f7f5f93dZhijun He        CameraMetadataNative info = new CameraMetadataNative();
1902001188be30649198972a3199a4322d6f7f5f93dZhijun He        try {
1912001188be30649198972a3199a4322d6f7f5f93dZhijun He            mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info);
1922001188be30649198972a3199a4322d6f7f5f93dZhijun He        } catch(CameraRuntimeException e) {
1932001188be30649198972a3199a4322d6f7f5f93dZhijun He            throw e.asChecked();
1942001188be30649198972a3199a4322d6f7f5f93dZhijun He        } catch(RemoteException e) {
1952001188be30649198972a3199a4322d6f7f5f93dZhijun He            // impossible
1962001188be30649198972a3199a4322d6f7f5f93dZhijun He            return null;
1972001188be30649198972a3199a4322d6f7f5f93dZhijun He        }
1982001188be30649198972a3199a4322d6f7f5f93dZhijun He        return new CameraCharacteristics(info);
199b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    }
200b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
201b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    /**
202b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * Open a connection to a camera with the given ID. Use
2034af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * {@link #getCameraIdList} to get the list of available camera
204b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * devices. Note that even if an id is listed, open may fail if the device
2054af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * is disconnected between the calls to {@link #getCameraIdList} and
206b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * {@link #openCamera}.
207b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
208b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @param cameraId The unique identifier of the camera device to open
209868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala     * @param listener The listener for the camera. Must not be null.
210868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala     * @param handler  The handler to call the listener on. Must not be null.
211b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
212b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @throws CameraAccessException if the camera is disabled by device policy,
21366ef64514464a1bb9c7931993cc8db3d7539f39aRuben Brunk     * or too many camera devices are already open, or the cameraId does not match
21466ef64514464a1bb9c7931993cc8db3d7539f39aRuben Brunk     * any currently available camera device.
21566ef64514464a1bb9c7931993cc8db3d7539f39aRuben Brunk     *
216b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @throws SecurityException if the application does not have permission to
217b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * access the camera
218868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala     * @throws IllegalArgumentException if listener or handler is null.
219b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     *
2204af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * @see #getCameraIdList
221b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
222b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     */
223868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala    private void openCameraDeviceUserAsync(String cameraId,
224868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala            CameraDevice.StateListener listener, Handler handler)
225868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala            throws CameraAccessException {
226e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        try {
227e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
228e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            synchronized (mLock) {
22970725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin
23070725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin                ICameraDeviceUser cameraUser;
23170725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin
2322f1a2e423e0fbb64467d6fcfa4e82c6384f31210Eino-Ville Talvala                android.hardware.camera2.impl.CameraDevice device =
233868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala                        new android.hardware.camera2.impl.CameraDevice(
234868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala                                cameraId,
235868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala                                listener,
236868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala                                handler);
23770725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin
23866ef64514464a1bb9c7931993cc8db3d7539f39aRuben Brunk                BinderHolder holder = new BinderHolder();
239feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk
240feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                ICameraDeviceCallbacks callbacks = device.getCallbacks();
241feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                int id = Integer.parseInt(cameraId);
242feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                try {
243feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                    mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
244feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                            USE_CALLING_UID, holder);
245feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                    cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
246feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                } catch (CameraRuntimeException e) {
247feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                    if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
248feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                        // Use legacy camera implementation for HAL1 devices
249feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                        Log.i(TAG, "Using legacy camera HAL.");
250feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                        cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
251feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                    } else {
252feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                        // Rethrow otherwise
253feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                        throw e;
254feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                    }
255feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk                }
25670725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin
25770725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin                // TODO: factor out listener to be non-nested, then move setter to constructor
258868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala                // For now, calling setRemoteDevice will fire initial
259868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala                // onOpened/onUnconfigured callbacks.
26070725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin                device.setRemoteDevice(cameraUser);
261e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            }
262e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
263e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        } catch (NumberFormatException e) {
264e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
265e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    + cameraId);
266e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        } catch (CameraRuntimeException e) {
26766ef64514464a1bb9c7931993cc8db3d7539f39aRuben Brunk            throw e.asChecked();
268e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        } catch (RemoteException e) {
269e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            // impossible
270e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
271b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    }
272b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
273b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    /**
2745c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * Open a connection to a camera with the given ID.
2755c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
2765c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * <p>Use {@link #getCameraIdList} to get the list of available camera
2775c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * devices. Note that even if an id is listed, open may fail if the device
2785c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * is disconnected between the calls to {@link #getCameraIdList} and
2795c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * {@link #openCamera}.</p>
2805c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
2815c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * <p>If the camera successfully opens after this function call returns,
2825c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * {@link CameraDevice.StateListener#onOpened} will be invoked with the
2835c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * newly opened {@link CameraDevice} in the unconfigured state.</p>
2845c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
2855c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * <p>If the camera becomes disconnected during initialization
2865c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * after this function call returns,
2875c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * {@link CameraDevice.StateListener#onDisconnected} with a
2885c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * {@link CameraDevice} in the disconnected state (and
2895c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
2905c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
2915c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * <p>If the camera fails to initialize after this function call returns,
2925c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * {@link CameraDevice.StateListener#onError} will be invoked with a
2935c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * {@link CameraDevice} in the error state (and
2945c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
2955c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
2965c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * @param cameraId
2975c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *             The unique identifier of the camera device to open
2985c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * @param listener
2995c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *             The listener which is invoked once the camera is opened
3005c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * @param handler
3015c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *             The handler on which the listener should be invoked, or
3025c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *             {@code null} to use the current thread's {@link android.os.Looper looper}.
3035c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
3045c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * @throws CameraAccessException if the camera is disabled by device policy,
3055c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * or the camera has become or was disconnected.
3065c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
3075c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * @throws IllegalArgumentException if cameraId or the listener was null,
3085c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * or the cameraId does not match any currently or previously available
3095c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * camera device.
3105c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
3115c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * @throws SecurityException if the application does not have permission to
3125c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * access the camera
3135c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     *
3145c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * @see #getCameraIdList
3155c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
3165c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin     */
3175c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin    public void openCamera(String cameraId, final CameraDevice.StateListener listener,
3185c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin            Handler handler)
3195c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin            throws CameraAccessException {
3205c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin
3215c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin        if (cameraId == null) {
3225c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin            throw new IllegalArgumentException("cameraId was null");
3235c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin        } else if (listener == null) {
3245c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin            throw new IllegalArgumentException("listener was null");
3255c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin        } else if (handler == null) {
3265c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin            if (Looper.myLooper() != null) {
3275c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin                handler = new Handler();
3285c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin            } else {
3295c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin                throw new IllegalArgumentException(
3305c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin                        "Looper doesn't exist in the calling thread");
3315c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin            }
3325c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin        }
3335c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin
334868d904306c6a96d94fa0da03515c51c86eefc63Eino-Ville Talvala        openCameraDeviceUserAsync(cameraId, listener, handler);
3355c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin    }
3365c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin
3375c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin    /**
3384af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * Interface for listening to camera devices becoming available or
3394af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * unavailable.
3404af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     *
3414af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * <p>Cameras become available when they are no longer in use, or when a new
342b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * removable camera is connected. They become unavailable when some
343b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     * application or service starts using a camera, or when a removable camera
3444af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * is disconnected.</p>
3454af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     *
3464af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala     * @see addAvailabilityListener
347b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala     */
3484af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala    public static abstract class AvailabilityListener {
3494af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala
350b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala        /**
351b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala         * A new camera has become available to use.
352b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala         *
3534af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala         * <p>The default implementation of this method does nothing.</p>
3544af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala         *
355b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala         * @param cameraId The unique identifier of the new camera.
356b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala         */
3574af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala        public void onCameraAvailable(String cameraId) {
3584af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            // default empty implementation
3594af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala        }
360b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala
361b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala        /**
3624af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala         * A previously-available camera has become unavailable for use.
3634af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala         *
3644af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala         * <p>If an application had an active CameraDevice instance for the
3654af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala         * now-disconnected camera, that application will receive a
3665c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin         * {@link CameraDevice.StateListener#onDisconnected disconnection error}.</p>
3674af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala         *
3684af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala         * <p>The default implementation of this method does nothing.</p>
369b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala         *
370b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala         * @param cameraId The unique identifier of the disconnected camera.
371b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala         */
3724af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala        public void onCameraUnavailable(String cameraId) {
3734af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala            // default empty implementation
3744af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala        }
375b2675542c2f414154125b534767ae0903fba581eEino-Ville Talvala    }
376e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
377e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
378e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        if (mDeviceIdList == null) {
379e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            int numCameras = 0;
380e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
381e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            try {
382e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                numCameras = mCameraService.getNumberOfCameras();
383e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            } catch(CameraRuntimeException e) {
384e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                throw e.asChecked();
385e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            } catch (RemoteException e) {
386e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                // impossible
387e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                return null;
388e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            }
389e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
390e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            mDeviceIdList = new ArrayList<String>();
39118fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He            CameraMetadataNative info = new CameraMetadataNative();
392e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            for (int i = 0; i < numCameras; ++i) {
393e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                // Non-removable cameras use integers starting at 0 for their
394e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                // identifiers
39518fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                boolean isDeviceSupported = false;
39618fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                try {
39718fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    mCameraService.getCameraCharacteristics(i, info);
39818fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    if (!info.isEmpty()) {
39918fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                        isDeviceSupported = true;
40018fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    } else {
40118fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                        throw new AssertionError("Expected to get non-empty characteristics");
40218fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    }
40318fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                } catch(IllegalArgumentException  e) {
40418fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    // Got a BAD_VALUE from service, meaning that this
40518fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    // device is not supported.
40618fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                } catch(CameraRuntimeException e) {
40718fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    throw e.asChecked();
40818fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                } catch(RemoteException e) {
40918fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    // impossible
41018fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                }
41118fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He
41218fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                if (isDeviceSupported) {
41318fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                    mDeviceIdList.add(String.valueOf(i));
41418fe0ae08d49b0fe4b6414a3841bd0fcab5419e8Zhijun He                }
415e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            }
416e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
417e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
418e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        return mDeviceIdList;
419e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    }
420e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
421e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    // TODO: this class needs unit tests
422e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    // TODO: extract class into top level
423ecb323e3ce94f62411b6799f9a0aa42b052de30dZhijun He    private class CameraServiceListener extends ICameraServiceListener.Stub {
424e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
425e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        // Keep up-to-date with ICameraServiceListener.h
426e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
427e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        // Device physically unplugged
428e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        public static final int STATUS_NOT_PRESENT = 0;
429e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        // Device physically has been plugged in
430e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        // and the camera can be used exclusively
431e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        public static final int STATUS_PRESENT = 1;
432e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        // Device physically has been plugged in
433e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        // but it will not be connect-able until enumeration is complete
434e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        public static final int STATUS_ENUMERATING = 2;
435e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        // Camera is in use by another app and cannot be used exclusively
436e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        public static final int STATUS_NOT_AVAILABLE = 0x80000000;
437e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
438e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        // Camera ID -> Status map
4394af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala        private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
440e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
441e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        private static final String TAG = "CameraServiceListener";
442e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
443e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        @Override
444e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        public IBinder asBinder() {
445e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            return this;
446e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
447e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
448e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        private boolean isAvailable(int status) {
449e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            switch (status) {
450e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                case STATUS_PRESENT:
451e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    return true;
452e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                default:
453e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    return false;
454e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            }
455e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
456e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
457e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        private boolean validStatus(int status) {
458e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            switch (status) {
459e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                case STATUS_NOT_PRESENT:
460e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                case STATUS_PRESENT:
461e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                case STATUS_ENUMERATING:
462e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                case STATUS_NOT_AVAILABLE:
463e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    return true;
464e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                default:
465e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    return false;
466e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            }
467e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        }
468e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
469e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        @Override
470e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        public void onStatusChanged(int status, int cameraId) throws RemoteException {
47170725500dcf3b666b43d50563d64705aab58d2d3Igor Murashkin            synchronized(CameraManager.this.mLock) {
472e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
473e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                Log.v(TAG,
474e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                        String.format("Camera id %d has status changed to 0x%x", cameraId, status));
475e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
4764af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                final String id = String.valueOf(cameraId);
477e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
478e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                if (!validStatus(status)) {
479e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    Log.e(TAG, String.format("Ignoring invalid device %d status 0x%x", cameraId,
480e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                            status));
481e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    return;
482e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                }
483e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
484e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                Integer oldStatus = mDeviceStatus.put(id, status);
485e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
4867441695049674e457064f289b74b11f62fa5c943Igor Murashkin                if (oldStatus != null && oldStatus == status) {
487e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    Log.v(TAG, String.format(
488e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                            "Device status changed to 0x%x, which is what it already was",
489e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                            status));
490e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    return;
491e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                }
492e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
493e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                // TODO: consider abstracting out this state minimization + transition
494e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                // into a separate
495e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                // more easily testable class
496e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                // i.e. (new State()).addState(STATE_AVAILABLE)
497e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                //                   .addState(STATE_NOT_AVAILABLE)
498e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                //                   .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
499e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                //                   .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
500e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                //                   .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
501e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                //                   .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
502e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
503e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                // Translate all the statuses to either 'available' or 'not available'
504e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                //  available -> available         => no new update
505e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                //  not available -> not available => no new update
506e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
507e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
508e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    Log.v(TAG,
509e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                            String.format(
510e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                                    "Device status was previously available (%d), " +
511e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                                            " and is now again available (%d)" +
512e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                                            "so no new client visible update will be sent",
513e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                                    isAvailable(status), isAvailable(status)));
514e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    return;
515e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                }
516e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin
5174af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                final int listenerCount = mListenerMap.size();
5184af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                for (int i = 0; i < listenerCount; i++) {
5194af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                    Handler handler = mListenerMap.valueAt(i);
5204af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                    final AvailabilityListener listener = mListenerMap.keyAt(i);
521e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    if (isAvailable(status)) {
5224af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                        handler.post(
5234af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                            new Runnable() {
5245c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin                                @Override
5254af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                                public void run() {
5264af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                                    listener.onCameraAvailable(id);
5274af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                                }
5284af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                            });
529e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    } else {
5304af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                        handler.post(
5314af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                            new Runnable() {
5325c9eaf6796a4c972710dd5cd23cdfa334fa8ad2eIgor Murashkin                                @Override
5334af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                                public void run() {
5344af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                                    listener.onCameraUnavailable(id);
5354af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                                }
5364af73c2153747d0624ccc75dfa001cb91982957fEino-Ville Talvala                            });
537e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                    }
538e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin                } // for
539e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin            } // synchronized
540e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin        } // onStatusChanged
541e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin    } // CameraServiceListener
542e363fbb2647aeb5ef4c87160d84c6b9ae8d45598Igor Murashkin} // CameraManager
543