/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera.device; import android.content.Context; import android.os.Handler; import com.android.camera.async.HandlerFactory; import com.android.camera.async.Lifetime; import com.android.camera.async.SafeCloseable; import com.android.camera.debug.Log.Tag; import com.android.camera.debug.Logger; import com.android.ex.camera2.portability.CameraAgent; import com.android.ex.camera2.portability.CameraAgent.CameraOpenCallback; import com.android.ex.camera2.portability.CameraAgent.CameraProxy; import com.android.ex.camera2.portability.CameraAgentFactory; import com.android.ex.camera2.portability.CameraAgentFactory.CameraApi; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.ParametersAreNonnullByDefault; /** * Set of device actions for opening and closing a single portability * layer camera device. */ @ParametersAreNonnullByDefault public class PortabilityCameraActions implements SingleDeviceActions { private static final Tag TAG = new Tag("PortCamAct"); private final CameraDeviceKey mId; private final HandlerFactory mHandlerFactory; private final ExecutorService mBackgroundRunner; private final Context mContext; private final CameraApi mApiVersion; private final Logger mLogger; public PortabilityCameraActions( CameraDeviceKey id, Context context, CameraApi apiVersion, ExecutorService backgroundRunner, HandlerFactory handlerFactory, Logger.Factory logFactory) { mId = id; mContext = context; mApiVersion = apiVersion; mBackgroundRunner = backgroundRunner; mHandlerFactory = handlerFactory; mLogger = logFactory.create(TAG); mLogger.d("Created Camera2Request"); } @Override public void executeOpen(SingleDeviceOpenListener openListener, Lifetime deviceLifetime) throws UnsupportedOperationException { mLogger.i("executeOpen(id: " + mId.getCameraId() + ")"); try { CameraAgent agent = CameraAgentFactory.getAndroidCameraAgent(mContext, mApiVersion); deviceLifetime.add(new CameraAgentRecycler(mApiVersion, mLogger)); mBackgroundRunner.execute(new OpenCameraRunnable(agent, mId.getCameraId().getLegacyValue(), mHandlerFactory.create(deviceLifetime, "Camera2 Lifetime"), openListener, mLogger)); } catch (AssertionError e) { openListener.onDeviceOpenException(e); } } @Override public void executeClose(SingleDeviceCloseListener closeListener, CameraProxy device) { mLogger.i("executeClose(" + device.getCameraId() + ")"); mBackgroundRunner.execute(new CloseCameraRunnable(device, device.getAgent(), closeListener, mLogger)); } /** * Recycles camera agents and ensures that recycle is only called * once per instance. */ private static class CameraAgentRecycler implements SafeCloseable { private final CameraApi mCameraApi; private final Logger mLogger; private final AtomicBoolean mIsClosed; public CameraAgentRecycler(CameraApi cameraApi, Logger logger) { mCameraApi = cameraApi; mLogger = logger; mIsClosed = new AtomicBoolean(false); } @Override public void close() { if (!mIsClosed.getAndSet(true)) { mLogger.d("Recycling CameraAgentFactory for CameraApi: " + mCameraApi); CameraAgentFactory.recycle(mCameraApi); } } } /** * Internal runnable that executes a CameraManager openCamera call. */ private static class OpenCameraRunnable implements Runnable { private final SingleDeviceOpenListener mOpenListener; private final int mCameraId; private final Handler mHandler; private final CameraAgent mCameraAgent; private final Logger mLogger; public OpenCameraRunnable(CameraAgent cameraAgent, int cameraId, Handler handler, SingleDeviceOpenListener openListener, Logger logger) { mCameraAgent = cameraAgent; mCameraId = cameraId; mHandler = handler; mOpenListener = openListener; mLogger = logger; } @Override public void run() { try { mLogger.i("mCameraAgent.openCamera(id: " + mCameraId + ")"); mCameraAgent.openCamera(mHandler, mCameraId, new OpenCameraStateCallback(mOpenListener, mLogger)); } catch (SecurityException e) { mOpenListener.onDeviceOpenException(e); } } } /** * Internal runnable that executes a close on a cameraDevice. */ private static class CloseCameraRunnable implements Runnable { private final SingleDeviceCloseListener mCloseListener; private final CameraProxy mCameraDevice; private final CameraAgent mCameraAgent; private final Logger mLogger; public CloseCameraRunnable(CameraProxy cameraDevice, CameraAgent cameraAgent, SingleDeviceCloseListener closeListener, Logger logger) { mCameraDevice = cameraDevice; mCameraAgent = cameraAgent; mCloseListener = closeListener; mLogger = logger; } @Override public void run() { try { mLogger.i("mCameraAgent.closeCamera(id: " + mCameraDevice.getCameraId() + ")"); mCameraAgent.closeCamera(mCameraDevice, true /* synchronous */); mCloseListener.onDeviceClosed(); } catch (Exception e) { mCloseListener.onDeviceClosingException(e); } } } /** * Internal callback that provides a camera device to a future. */ private static class OpenCameraStateCallback implements CameraOpenCallback { private final SingleDeviceOpenListener mOpenListener; private final Logger mLogger; private boolean mHasBeenCalled = false; public OpenCameraStateCallback(SingleDeviceOpenListener openListener, Logger logger) { mOpenListener = openListener; mLogger = logger; } @Override public void onCameraOpened(CameraProxy camera) { if (!called()) { mLogger.i("onCameraOpened(id: " + camera.getCameraId() + ")"); mOpenListener.onDeviceOpened(camera); } } @Override public void onCameraDisabled(int cameraId) { if (!called()) { mLogger.w("onCameraDisabled(id: " + cameraId + ")"); mOpenListener.onDeviceOpenException(new CameraOpenException(-1)); } } @Override public void onDeviceOpenFailure(int cameraId, String info) { if (!called()) { mLogger.e("onDeviceOpenFailure(id: " + cameraId + ", info: " + info + ")"); mOpenListener.onDeviceOpenException(new CameraOpenException(-1)); } } @Override public void onDeviceOpenedAlready(int cameraId, String info) { if (!called()) { mLogger.w("onDeviceOpenedAlready(id: " + cameraId + ", info: " + info + ")"); mOpenListener.onDeviceOpenException(new CameraOpenException(-1)); } } @Override public void onReconnectionFailure(CameraAgent mgr, String info) { if (!called()) { mLogger.w("onReconnectionFailure(info: " + info + ")"); mOpenListener.onDeviceOpenException(new CameraOpenException(-1)); } } private boolean called() { boolean result = mHasBeenCalled; if (!mHasBeenCalled) { mHasBeenCalled = true; } else { mLogger.v("Callback was re-executed."); } return result; } } }