CameraManager.java revision 7fcb357811d4dc1f44624e30ad924e9e580d4cbf
127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock/*
227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * Copyright (C) 2013 The Android Open Source Project
327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock *
427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * Licensed under the Apache License, Version 2.0 (the "License");
527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * you may not use this file except in compliance with the License.
627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * You may obtain a copy of the License at
727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock *
827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock *      http://www.apache.org/licenses/LICENSE-2.0
927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock *
1027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * Unless required by applicable law or agreed to in writing, software
1127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * distributed under the License is distributed on an "AS IS" BASIS,
1227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * See the License for the specific language governing permissions and
1427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * limitations under the License.
1527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock */
1627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
1727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockpackage android.hardware.camera2;
1827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
1927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.content.Context;
2027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.hardware.ICameraService;
2127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.hardware.ICameraServiceListener;
2227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.hardware.camera2.impl.CameraMetadataNative;
23b77edbfdab54531023c8bbea7d89b6cefc42096cJohn Spurlockimport android.hardware.camera2.legacy.CameraDeviceUserShim;
2427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.hardware.camera2.utils.CameraBinderDecorator;
2527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.hardware.camera2.utils.CameraRuntimeException;
26bd95740648372449a4d5c164d7050eee352d4c24John Spurlockimport android.hardware.camera2.utils.BinderHolder;
2727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.os.IBinder;
2827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.os.Handler;
2927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.os.Looper;
3027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.os.RemoteException;
3127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.os.ServiceManager;
3227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.util.Log;
3327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport android.util.ArrayMap;
3427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
3527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockimport java.util.ArrayList;
3627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
377edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock/**
3827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * <p>An interface for iterating, listing, and connecting to
3927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * {@link CameraDevice CameraDevices}.</p>
40f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock *
41f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock * <p>You can get an instance of this class by calling
42f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p>
4327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock *
44bd95740648372449a4d5c164d7050eee352d4c24John Spurlock * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre>
45b77edbfdab54531023c8bbea7d89b6cefc42096cJohn Spurlock *
4627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * <p>For more details about communicating with camera devices, read the Camera
4727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock * developer guide or the {@link android.hardware.camera2 camera2}
485b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock * package documentation.</p>
49bd95740648372449a4d5c164d7050eee352d4c24John Spurlock */
5027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlockpublic final class CameraManager {
51bd95740648372449a4d5c164d7050eee352d4c24John Spurlock
5227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    private static final String TAG = "CameraManager";
5327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
5427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    /**
5527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * This should match the ICameraService definition
5627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     */
57d4e6575c4f664e0d42d9306c9762d96533df429eJohn Spurlock    private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
5827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    private static final int USE_CALLING_UID = -1;
5927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
60bd95740648372449a4d5c164d7050eee352d4c24John Spurlock    private final ICameraService mCameraService;
6127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    private ArrayList<String> mDeviceIdList;
62bd95740648372449a4d5c164d7050eee352d4c24John Spurlock
63bd95740648372449a4d5c164d7050eee352d4c24John Spurlock    private final ArrayMap<AvailabilityListener, Handler> mListenerMap =
6427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock            new ArrayMap<AvailabilityListener, Handler>();
6527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
665b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock    private final Context mContext;
67bd95740648372449a4d5c164d7050eee352d4c24John Spurlock    private final Object mLock = new Object();
6827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
69bd95740648372449a4d5c164d7050eee352d4c24John Spurlock    /**
7027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @hide
7127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     */
7227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    public CameraManager(Context context) {
7327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        mContext = context;
7427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
7527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
7627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
7727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
7827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        /**
79f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock         * Wrap the camera service in a decorator which automatically translates return codes
8027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock         * into exceptions, and RemoteExceptions into other exceptions.
8127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock         */
8227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        mCameraService = CameraBinderDecorator.newInstance(cameraServiceRaw);
8327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
8427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        try {
8527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock            CameraBinderDecorator.throwOnError(
8627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                    CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
87f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock        } catch(CameraRuntimeException e) {
88f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock            throw new IllegalStateException("Failed to setup camera vendor tags",
89f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock                    e.asChecked());
90f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock        }
91bd95740648372449a4d5c164d7050eee352d4c24John Spurlock
92bd95740648372449a4d5c164d7050eee352d4c24John Spurlock        try {
93b77edbfdab54531023c8bbea7d89b6cefc42096cJohn Spurlock            mCameraService.addListener(new CameraServiceListener());
94b77edbfdab54531023c8bbea7d89b6cefc42096cJohn Spurlock        } catch(CameraRuntimeException e) {
955b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock            throw new IllegalStateException("Failed to register a camera service listener",
9627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                    e.asChecked());
975b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock        } catch (RemoteException e) {
985b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock            // impossible
9927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        }
10027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    }
1015b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock
1025b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock    /**
1035b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * Return the list of currently connected camera devices by
10427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * identifier.
10527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     *
10627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * <p>Non-removable cameras use integers starting at 0 for their
107bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     * identifiers, while removable cameras have a unique identifier for each
108bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     * individual device, even if they are the same model.</p>
109bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     *
110bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     * @return The list of currently connected camera devices.
111ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos     */
112ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos    public String[] getCameraIdList() throws CameraAccessException {
113bd95740648372449a4d5c164d7050eee352d4c24John Spurlock        synchronized (mLock) {
114bd95740648372449a4d5c164d7050eee352d4c24John Spurlock            try {
115bd95740648372449a4d5c164d7050eee352d4c24John Spurlock                return getOrCreateDeviceIdListLocked().toArray(new String[0]);
116bd95740648372449a4d5c164d7050eee352d4c24John Spurlock            } catch(CameraAccessException e) {
117ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos                // this should almost never happen, except if mediaserver crashes
118ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos                throw new IllegalStateException(
119ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos                        "Failed to query camera service for device ID list", e);
120ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos            }
121ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos        }
122bd95740648372449a4d5c164d7050eee352d4c24John Spurlock    }
123bd95740648372449a4d5c164d7050eee352d4c24John Spurlock
124cffd1e42d329c99f0fd85243b703fd29573d4fe6Adrian Roos    /**
125bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     * Register a listener to be notified about camera device availability.
126bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     *
127bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     * <p>Registering the same listener again will replace the handler with the
128bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     * new one provided.</p>
129bd95740648372449a4d5c164d7050eee352d4c24John Spurlock     *
13027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @param listener The new listener to send camera availability notices to
13127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @param handler The handler on which the listener should be invoked, or
13227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * {@code null} to use the current thread's {@link android.os.Looper looper}.
13327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     */
13427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    public void addAvailabilityListener(AvailabilityListener listener, Handler handler) {
13527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        if (handler == null) {
1367edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock            Looper looper = Looper.myLooper();
1377edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock            if (looper == null) {
1387edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock                throw new IllegalArgumentException(
1397edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock                        "No handler given, and current thread has no looper!");
140c23bd80b38bf9c03c1eb51fbde621eb5a6381c14John Spurlock            }
141c23bd80b38bf9c03c1eb51fbde621eb5a6381c14John Spurlock            handler = new Handler(looper);
1425b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock        }
1435b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock
1447edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock        synchronized (mLock) {
1457edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock            mListenerMap.put(listener, handler);
1467edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock        }
1477edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock    }
1487edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock
1497edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock    /**
150c23bd80b38bf9c03c1eb51fbde621eb5a6381c14John Spurlock     * Remove a previously-added listener; the listener will no longer receive
151c23bd80b38bf9c03c1eb51fbde621eb5a6381c14John Spurlock     * connection and disconnection callbacks.
1527edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock     *
1537edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock     * <p>Removing a listener that isn't registered has no effect.</p>
1547edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock     *
1557edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock     * @param listener The listener to remove from the notification list
1567edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock     */
1577edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock    public void removeAvailabilityListener(AvailabilityListener listener) {
1587edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock        synchronized (mLock) {
1597edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock            mListenerMap.remove(listener);
1607edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock        }
1615b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock    }
1625b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock
163c23bd80b38bf9c03c1eb51fbde621eb5a6381c14John Spurlock    /**
1645b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * <p>Query the capabilities of a camera device. These capabilities are
1655b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * immutable for a given camera.</p>
1660ec64c65fb7fbfd89556bc33f5caab4ef0937fd4John Spurlock     *
1675b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * @param cameraId The id of the camera device to query
1685b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * @return The properties of the given camera
1695b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     *
1705b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * @throws IllegalArgumentException if the cameraId does not match any
1715b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * currently connected camera device.
1725b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * @throws CameraAccessException if the camera is disabled by device policy.
1735b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * @throws SecurityException if the application does not have permission to
1745b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * access the camera
1755b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     *
1760ec64c65fb7fbfd89556bc33f5caab4ef0937fd4John Spurlock     * @see #getCameraIdList
1775b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
1785b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock     */
1795b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock    public CameraCharacteristics getCameraCharacteristics(String cameraId)
1805b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock            throws CameraAccessException {
1815b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock
182c23bd80b38bf9c03c1eb51fbde621eb5a6381c14John Spurlock        synchronized (mLock) {
1835b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock            if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
184c23bd80b38bf9c03c1eb51fbde621eb5a6381c14John Spurlock                throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
18527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        " currently connected camera device", cameraId));
18627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock            }
18727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        }
188d4e6575c4f664e0d42d9306c9762d96533df429eJohn Spurlock
1895b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock        CameraMetadataNative info = new CameraMetadataNative();
1907edfbca5d00cbc376fda790b50a3fedb9c6070abJohn Spurlock        try {
1915b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock            mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info);
1925b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock        } catch(CameraRuntimeException e) {
1935b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock            throw e.asChecked();
1945b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock        } catch(RemoteException e) {
1955b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock            // impossible
1965b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock            return null;
1975b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock        }
1985b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock        return new CameraCharacteristics(info);
1995b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock    }
20027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
20127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    /**
20227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * Helper for openning a connection to a camera with the given ID.
20327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     *
20427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @param cameraId The unique identifier of the camera device to open
20527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @param listener The listener for the camera. Must not be null.
20627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @param handler  The handler to call the listener on. Must not be null.
20727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     *
20827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @throws CameraAccessException if the camera is disabled by device policy,
209f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock     * or too many camera devices are already open, or the cameraId does not match
210f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock     * any currently available camera device.
211f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock     *
21227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @throws SecurityException if the application does not have permission to
21327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * access the camera
21427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @throws IllegalArgumentException if listener or handler is null.
21527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @return A handle to the newly-created camera device.
21627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     *
21727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @see #getCameraIdList
21827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
21927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock     */
22027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock    private CameraDevice openCameraDeviceUserAsync(String cameraId,
22127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock            CameraDevice.StateListener listener, Handler handler)
22227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock            throws CameraAccessException {
223bd95740648372449a4d5c164d7050eee352d4c24John Spurlock        CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
22427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        CameraDevice device = null;
225f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock        try {
226bd95740648372449a4d5c164d7050eee352d4c24John Spurlock
22727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock            synchronized (mLock) {
22827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
2295b9145bf990a9bbf4fdef1739e61ff8c70ec868fJohn Spurlock                ICameraDeviceUser cameraUser = null;
23027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
231f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock                android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
23227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        new android.hardware.camera2.impl.CameraDeviceImpl(
23327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                                cameraId,
23427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                                listener,
23527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                                handler,
23627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                                characteristics);
23727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
23827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                BinderHolder holder = new BinderHolder();
23927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
240ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos                ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
241ea56251d92050e9a672d1f66d0d4621e4dd4136eAdrian Roos                int id = Integer.parseInt(cameraId);
242bd95740648372449a4d5c164d7050eee352d4c24John Spurlock                try {
243b77edbfdab54531023c8bbea7d89b6cefc42096cJohn Spurlock                    mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
24427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                            USE_CALLING_UID, holder);
24527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                    cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
24627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                } catch (CameraRuntimeException e) {
24727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                    if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
24827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        // Use legacy camera implementation for HAL1 devices
249b77edbfdab54531023c8bbea7d89b6cefc42096cJohn Spurlock                        Log.i(TAG, "Using legacy camera HAL.");
250bd95740648372449a4d5c164d7050eee352d4c24John Spurlock                        cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
251b77edbfdab54531023c8bbea7d89b6cefc42096cJohn Spurlock                    } else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
25227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                            e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
2530ec64c65fb7fbfd89556bc33f5caab4ef0937fd4John Spurlock                            e.getReason() == CameraAccessException.CAMERA_DISABLED ||
25427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                            e.getReason() == CameraAccessException.CAMERA_DISCONNECTED ||
25527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                            e.getReason() == CameraAccessException.CAMERA_ERROR) {
25627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        // Received one of the known connection errors
25727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        // The remote camera device cannot be connected to, so
25827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        // set the local camera to the startup error state
25927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        deviceImpl.setRemoteFailure(e);
26027735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
26127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        if (e.getReason() == CameraAccessException.CAMERA_DISABLED ||
26227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                                e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
26327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                            // Per API docs, these failures call onError and throw
26427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                            throw e;
26527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        }
26627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                    } else {
26727735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        // Unexpected failure - rethrow
26827735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                        throw e;
26927735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                    }
270f25706fc3e169f07a81017ad38dc4501698a5cb3John Spurlock                }
27127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock
27227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                // TODO: factor out listener to be non-nested, then move setter to constructor
27327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                // For now, calling setRemoteDevice will fire initial
27427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                // onOpened/onUnconfigured callbacks.
27527735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                deviceImpl.setRemoteDevice(cameraUser);
27627735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                device = deviceImpl;
2770ec64c65fb7fbfd89556bc33f5caab4ef0937fd4John Spurlock            }
278d4e6575c4f664e0d42d9306c9762d96533df429eJohn Spurlock
2790ec64c65fb7fbfd89556bc33f5caab4ef0937fd4John Spurlock        } catch (NumberFormatException e) {
280d4e6575c4f664e0d42d9306c9762d96533df429eJohn Spurlock            throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
28127735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock                    + cameraId);
28227735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        } catch (CameraRuntimeException e) {
28327735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock            throw e.asChecked();
28427735a4ba50d42df64eb82acc3f64fa2d68fd505John Spurlock        } catch (RemoteException e) {
285            // impossible
286        }
287        return device;
288    }
289
290    /**
291     * Open a connection to a camera with the given ID.
292     *
293     * <p>Use {@link #getCameraIdList} to get the list of available camera
294     * devices. Note that even if an id is listed, open may fail if the device
295     * is disconnected between the calls to {@link #getCameraIdList} and
296     * {@link #openCamera}.</p>
297     *
298     * <p>Once the camera is successfully opened, {@link CameraDevice.StateListener#onOpened} will
299     * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
300     * for operation by calling {@link CameraDevice#createCaptureSession} and
301     * {@link CameraDevice#createCaptureRequest}</p>
302     *
303     * <!--
304     * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
305     * on the returned CameraDevice instance will be queued up until the device startup has
306     * completed and the listener's {@link CameraDevice.StateListener#onOpened onOpened} method is
307     * called. The pending operations are then processed in order.</p>
308     * -->
309     * <p>If the camera becomes disconnected during initialization
310     * after this function call returns,
311     * {@link CameraDevice.StateListener#onDisconnected} with a
312     * {@link CameraDevice} in the disconnected state (and
313     * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
314     *
315     * <p>If opening the camera device fails, then the device listener's
316     * {@link CameraDevice.StateListener#onError onError} method will be called, and subsequent
317     * calls on the camera device will throw a {@link CameraAccessException}.</p>
318     *
319     * @param cameraId
320     *             The unique identifier of the camera device to open
321     * @param listener
322     *             The listener which is invoked once the camera is opened
323     * @param handler
324     *             The handler on which the listener should be invoked, or
325     *             {@code null} to use the current thread's {@link android.os.Looper looper}.
326     *
327     * @throws CameraAccessException if the camera is disabled by device policy,
328     * or the camera has become or was disconnected.
329     *
330     * @throws IllegalArgumentException if cameraId or the listener was null,
331     * or the cameraId does not match any currently or previously available
332     * camera device.
333     *
334     * @throws SecurityException if the application does not have permission to
335     * access the camera
336     *
337     * @see #getCameraIdList
338     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
339     */
340    public void openCamera(String cameraId, final CameraDevice.StateListener listener,
341            Handler handler)
342            throws CameraAccessException {
343
344        if (cameraId == null) {
345            throw new IllegalArgumentException("cameraId was null");
346        } else if (listener == null) {
347            throw new IllegalArgumentException("listener was null");
348        } else if (handler == null) {
349            if (Looper.myLooper() != null) {
350                handler = new Handler();
351            } else {
352                throw new IllegalArgumentException(
353                        "Looper doesn't exist in the calling thread");
354            }
355        }
356
357        openCameraDeviceUserAsync(cameraId, listener, handler);
358    }
359
360    /**
361     * Interface for listening to camera devices becoming available or
362     * unavailable.
363     *
364     * <p>Cameras become available when they are no longer in use, or when a new
365     * removable camera is connected. They become unavailable when some
366     * application or service starts using a camera, or when a removable camera
367     * is disconnected.</p>
368     *
369     * @see addAvailabilityListener
370     */
371    public static abstract class AvailabilityListener {
372
373        /**
374         * A new camera has become available to use.
375         *
376         * <p>The default implementation of this method does nothing.</p>
377         *
378         * @param cameraId The unique identifier of the new camera.
379         */
380        public void onCameraAvailable(String cameraId) {
381            // default empty implementation
382        }
383
384        /**
385         * A previously-available camera has become unavailable for use.
386         *
387         * <p>If an application had an active CameraDevice instance for the
388         * now-disconnected camera, that application will receive a
389         * {@link CameraDevice.StateListener#onDisconnected disconnection error}.</p>
390         *
391         * <p>The default implementation of this method does nothing.</p>
392         *
393         * @param cameraId The unique identifier of the disconnected camera.
394         */
395        public void onCameraUnavailable(String cameraId) {
396            // default empty implementation
397        }
398    }
399
400    private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
401        if (mDeviceIdList == null) {
402            int numCameras = 0;
403
404            try {
405                numCameras = mCameraService.getNumberOfCameras();
406            } catch(CameraRuntimeException e) {
407                throw e.asChecked();
408            } catch (RemoteException e) {
409                // impossible
410                return null;
411            }
412
413            mDeviceIdList = new ArrayList<String>();
414            CameraMetadataNative info = new CameraMetadataNative();
415            for (int i = 0; i < numCameras; ++i) {
416                // Non-removable cameras use integers starting at 0 for their
417                // identifiers
418                boolean isDeviceSupported = false;
419                try {
420                    mCameraService.getCameraCharacteristics(i, info);
421                    if (!info.isEmpty()) {
422                        isDeviceSupported = true;
423                    } else {
424                        throw new AssertionError("Expected to get non-empty characteristics");
425                    }
426                } catch(IllegalArgumentException  e) {
427                    // Got a BAD_VALUE from service, meaning that this
428                    // device is not supported.
429                } catch(CameraRuntimeException e) {
430                    throw e.asChecked();
431                } catch(RemoteException e) {
432                    // impossible
433                }
434
435                if (isDeviceSupported) {
436                    mDeviceIdList.add(String.valueOf(i));
437                }
438            }
439
440        }
441        return mDeviceIdList;
442    }
443
444    // TODO: this class needs unit tests
445    // TODO: extract class into top level
446    private class CameraServiceListener extends ICameraServiceListener.Stub {
447
448        // Keep up-to-date with ICameraServiceListener.h
449
450        // Device physically unplugged
451        public static final int STATUS_NOT_PRESENT = 0;
452        // Device physically has been plugged in
453        // and the camera can be used exclusively
454        public static final int STATUS_PRESENT = 1;
455        // Device physically has been plugged in
456        // but it will not be connect-able until enumeration is complete
457        public static final int STATUS_ENUMERATING = 2;
458        // Camera is in use by another app and cannot be used exclusively
459        public static final int STATUS_NOT_AVAILABLE = 0x80000000;
460
461        // Camera ID -> Status map
462        private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
463
464        private static final String TAG = "CameraServiceListener";
465
466        @Override
467        public IBinder asBinder() {
468            return this;
469        }
470
471        private boolean isAvailable(int status) {
472            switch (status) {
473                case STATUS_PRESENT:
474                    return true;
475                default:
476                    return false;
477            }
478        }
479
480        private boolean validStatus(int status) {
481            switch (status) {
482                case STATUS_NOT_PRESENT:
483                case STATUS_PRESENT:
484                case STATUS_ENUMERATING:
485                case STATUS_NOT_AVAILABLE:
486                    return true;
487                default:
488                    return false;
489            }
490        }
491
492        @Override
493        public void onStatusChanged(int status, int cameraId) throws RemoteException {
494            synchronized(CameraManager.this.mLock) {
495
496                Log.v(TAG,
497                        String.format("Camera id %d has status changed to 0x%x", cameraId, status));
498
499                final String id = String.valueOf(cameraId);
500
501                if (!validStatus(status)) {
502                    Log.e(TAG, String.format("Ignoring invalid device %d status 0x%x", cameraId,
503                            status));
504                    return;
505                }
506
507                Integer oldStatus = mDeviceStatus.put(id, status);
508
509                if (oldStatus != null && oldStatus == status) {
510                    Log.v(TAG, String.format(
511                            "Device status changed to 0x%x, which is what it already was",
512                            status));
513                    return;
514                }
515
516                // TODO: consider abstracting out this state minimization + transition
517                // into a separate
518                // more easily testable class
519                // i.e. (new State()).addState(STATE_AVAILABLE)
520                //                   .addState(STATE_NOT_AVAILABLE)
521                //                   .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
522                //                   .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
523                //                   .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
524                //                   .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
525
526                // Translate all the statuses to either 'available' or 'not available'
527                //  available -> available         => no new update
528                //  not available -> not available => no new update
529                if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
530
531                    Log.v(TAG,
532                            String.format(
533                                    "Device status was previously available (%d), " +
534                                            " and is now again available (%d)" +
535                                            "so no new client visible update will be sent",
536                                    isAvailable(status), isAvailable(status)));
537                    return;
538                }
539
540                final int listenerCount = mListenerMap.size();
541                for (int i = 0; i < listenerCount; i++) {
542                    Handler handler = mListenerMap.valueAt(i);
543                    final AvailabilityListener listener = mListenerMap.keyAt(i);
544                    if (isAvailable(status)) {
545                        handler.post(
546                            new Runnable() {
547                                @Override
548                                public void run() {
549                                    listener.onCameraAvailable(id);
550                                }
551                            });
552                    } else {
553                        handler.post(
554                            new Runnable() {
555                                @Override
556                                public void run() {
557                                    listener.onCameraUnavailable(id);
558                                }
559                            });
560                    }
561                } // for
562            } // synchronized
563        } // onStatusChanged
564    } // CameraServiceListener
565} // CameraManager
566