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 PortabilityCameraActions( CameraDeviceKey id, Context context, CameraApi apiVersion, ExecutorService backgroundRunner, HandlerFactory handlerFactory, Logger.Factory logFactory)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 executeOpen(SingleDeviceOpenListener<CameraProxy> openListener, Lifetime deviceLifetime)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 executeClose(SingleDeviceCloseListener closeListener, CameraProxy device)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 CameraAgentRecycler(CameraApi cameraApi, Logger logger)102 public CameraAgentRecycler(CameraApi cameraApi, Logger logger) { 103 mCameraApi = cameraApi; 104 mLogger = logger; 105 mIsClosed = new AtomicBoolean(false); 106 } 107 108 @Override close()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 OpenCameraRunnable(CameraAgent cameraAgent, int cameraId, Handler handler, SingleDeviceOpenListener<CameraProxy> openListener, Logger logger)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 run()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 CloseCameraRunnable(CameraProxy cameraDevice, CameraAgent cameraAgent, SingleDeviceCloseListener closeListener, Logger logger)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 run()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 OpenCameraStateCallback(SingleDeviceOpenListener<CameraProxy> openListener, Logger logger)186 public OpenCameraStateCallback(SingleDeviceOpenListener<CameraProxy> openListener, 187 Logger logger) { 188 mOpenListener = openListener; 189 mLogger = logger; 190 } 191 192 @Override onCameraOpened(CameraProxy camera)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 onCameraDisabled(int cameraId)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 onDeviceOpenFailure(int cameraId, String info)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 onDeviceOpenedAlready(int cameraId, String info)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 onReconnectionFailure(CameraAgent mgr, String info)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 called()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