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