Home | History | Annotate | Download | only in device
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.camera.device;
     18 
     19 import android.content.Context;
     20 import android.os.Handler;
     21 
     22 import com.android.camera.async.HandlerFactory;
     23 import com.android.camera.async.Lifetime;
     24 import com.android.camera.async.SafeCloseable;
     25 import com.android.camera.debug.Log.Tag;
     26 import com.android.camera.debug.Logger;
     27 import com.android.ex.camera2.portability.CameraAgent;
     28 import com.android.ex.camera2.portability.CameraAgent.CameraOpenCallback;
     29 import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
     30 import com.android.ex.camera2.portability.CameraAgentFactory;
     31 import com.android.ex.camera2.portability.CameraAgentFactory.CameraApi;
     32 
     33 import java.util.concurrent.ExecutorService;
     34 import java.util.concurrent.atomic.AtomicBoolean;
     35 
     36 import javax.annotation.ParametersAreNonnullByDefault;
     37 
     38 /**
     39  * Set of device actions for opening and closing a single portability
     40  * layer camera device.
     41  */
     42 @ParametersAreNonnullByDefault
     43 public class PortabilityCameraActions implements SingleDeviceActions<CameraProxy> {
     44     private static final Tag TAG = new Tag("PortCamAct");
     45 
     46     private final CameraDeviceKey mId;
     47     private final HandlerFactory mHandlerFactory;
     48     private final ExecutorService mBackgroundRunner;
     49     private final Context mContext;
     50     private final CameraApi mApiVersion;
     51     private final Logger mLogger;
     52 
     53     public PortabilityCameraActions(
     54           CameraDeviceKey id,
     55           Context context,
     56           CameraApi apiVersion,
     57           ExecutorService backgroundRunner,
     58           HandlerFactory handlerFactory,
     59           Logger.Factory logFactory) {
     60         mId = id;
     61         mContext = context;
     62         mApiVersion = apiVersion;
     63         mBackgroundRunner = backgroundRunner;
     64         mHandlerFactory = handlerFactory;
     65         mLogger = logFactory.create(TAG);
     66 
     67         mLogger.d("Created Camera2Request");
     68     }
     69 
     70     @Override
     71     public void executeOpen(SingleDeviceOpenListener<CameraProxy> openListener,
     72           Lifetime deviceLifetime) throws UnsupportedOperationException {
     73         mLogger.i("executeOpen(id: " + mId.getCameraId() + ")");
     74         try {
     75             CameraAgent agent = CameraAgentFactory.getAndroidCameraAgent(mContext, mApiVersion);
     76             deviceLifetime.add(new CameraAgentRecycler(mApiVersion, mLogger));
     77 
     78             mBackgroundRunner.execute(new OpenCameraRunnable(agent, mId.getCameraId().getLegacyValue(),
     79                     mHandlerFactory.create(deviceLifetime, "Camera2 Lifetime"),
     80                     openListener, mLogger));
     81         } catch (AssertionError e) {
     82             openListener.onDeviceOpenException(e);
     83         }
     84     }
     85 
     86     @Override
     87     public void executeClose(SingleDeviceCloseListener closeListener, CameraProxy device) {
     88         mLogger.i("executeClose(" + device.getCameraId() + ")");
     89         mBackgroundRunner.execute(new CloseCameraRunnable(device, device.getAgent(),
     90               closeListener, mLogger));
     91     }
     92 
     93     /**
     94      * Recycles camera agents and ensures that recycle is only called
     95      * once per instance.
     96      */
     97     private static class CameraAgentRecycler implements SafeCloseable {
     98         private final CameraApi mCameraApi;
     99         private final Logger mLogger;
    100         private final AtomicBoolean mIsClosed;
    101 
    102         public CameraAgentRecycler(CameraApi cameraApi, Logger logger) {
    103             mCameraApi = cameraApi;
    104             mLogger = logger;
    105             mIsClosed = new AtomicBoolean(false);
    106         }
    107 
    108         @Override
    109         public void close() {
    110             if (!mIsClosed.getAndSet(true)) {
    111                 mLogger.d("Recycling CameraAgentFactory for CameraApi: " + mCameraApi);
    112                 CameraAgentFactory.recycle(mCameraApi);
    113             }
    114         }
    115     }
    116 
    117     /**
    118      * Internal runnable that executes a CameraManager openCamera call.
    119      */
    120     private static class OpenCameraRunnable implements Runnable {
    121         private final SingleDeviceOpenListener<CameraProxy> mOpenListener;
    122         private final int mCameraId;
    123         private final Handler mHandler;
    124         private final CameraAgent mCameraAgent;
    125         private final Logger mLogger;
    126 
    127         public OpenCameraRunnable(CameraAgent cameraAgent, int cameraId,
    128               Handler handler, SingleDeviceOpenListener<CameraProxy> openListener,
    129               Logger logger) {
    130             mCameraAgent = cameraAgent;
    131             mCameraId = cameraId;
    132             mHandler = handler;
    133             mOpenListener = openListener;
    134             mLogger = logger;
    135         }
    136 
    137         @Override
    138         public void run() {
    139             try {
    140                 mLogger.i("mCameraAgent.openCamera(id: " + mCameraId + ")");
    141                 mCameraAgent.openCamera(mHandler, mCameraId,
    142                       new OpenCameraStateCallback(mOpenListener, mLogger));
    143             } catch (SecurityException e) {
    144                 mOpenListener.onDeviceOpenException(e);
    145             }
    146         }
    147     }
    148 
    149     /**
    150      * Internal runnable that executes a close on a cameraDevice.
    151      */
    152     private static class CloseCameraRunnable implements Runnable {
    153         private final SingleDeviceCloseListener mCloseListener;
    154         private final CameraProxy mCameraDevice;
    155         private final CameraAgent mCameraAgent;
    156         private final Logger mLogger;
    157 
    158         public CloseCameraRunnable(CameraProxy cameraDevice, CameraAgent cameraAgent,
    159               SingleDeviceCloseListener closeListener, Logger logger) {
    160             mCameraDevice = cameraDevice;
    161             mCameraAgent = cameraAgent;
    162             mCloseListener = closeListener;
    163             mLogger = logger;
    164         }
    165 
    166         @Override
    167         public void run() {
    168             try {
    169                 mLogger.i("mCameraAgent.closeCamera(id: " + mCameraDevice.getCameraId() + ")");
    170                 mCameraAgent.closeCamera(mCameraDevice, true /* synchronous */);
    171                 mCloseListener.onDeviceClosed();
    172             } catch (Exception e) {
    173                 mCloseListener.onDeviceClosingException(e);
    174             }
    175         }
    176     }
    177 
    178     /**
    179      * Internal callback that provides a camera device to a future.
    180      */
    181     private static class OpenCameraStateCallback implements CameraOpenCallback {
    182         private final SingleDeviceOpenListener<CameraProxy> mOpenListener;
    183         private final Logger mLogger;
    184         private boolean mHasBeenCalled = false;
    185 
    186         public OpenCameraStateCallback(SingleDeviceOpenListener<CameraProxy> openListener,
    187               Logger logger) {
    188             mOpenListener = openListener;
    189             mLogger = logger;
    190         }
    191 
    192         @Override
    193         public void onCameraOpened(CameraProxy camera) {
    194             if (!called()) {
    195                 mLogger.i("onCameraOpened(id: " + camera.getCameraId() + ")");
    196                 mOpenListener.onDeviceOpened(camera);
    197             }
    198         }
    199 
    200         @Override
    201         public void onCameraDisabled(int cameraId) {
    202             if (!called()) {
    203                 mLogger.w("onCameraDisabled(id: " + cameraId + ")");
    204                 mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
    205             }
    206         }
    207 
    208         @Override
    209         public void onDeviceOpenFailure(int cameraId, String info) {
    210             if (!called()) {
    211                 mLogger.e("onDeviceOpenFailure(id: " + cameraId
    212                       + ", info: " + info + ")");
    213                 mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
    214             }
    215         }
    216 
    217         @Override
    218         public void onDeviceOpenedAlready(int cameraId, String info) {
    219             if (!called()) {
    220                 mLogger.w("onDeviceOpenedAlready(id: " + cameraId
    221                       + ", info: " + info + ")");
    222                 mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
    223             }
    224         }
    225 
    226         @Override
    227         public void onReconnectionFailure(CameraAgent mgr, String info) {
    228             if (!called()) {
    229                 mLogger.w("onReconnectionFailure(info: " + info + ")");
    230                 mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
    231             }
    232         }
    233 
    234         private boolean called() {
    235             boolean result = mHasBeenCalled;
    236             if (!mHasBeenCalled) {
    237                 mHasBeenCalled = true;
    238             } else {
    239                 mLogger.v("Callback was re-executed.");
    240             }
    241 
    242             return result;
    243         }
    244     }
    245 }
    246