1 /* 2 * Copyright (C) 2014 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.one.v2.commands; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import android.hardware.camera2.CameraAccessException; 22 23 import com.android.camera.async.SafeCloseable; 24 import com.android.camera.debug.Log; 25 import com.android.camera.debug.Logger; 26 import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException; 27 import com.android.camera.one.v2.core.ResourceAcquisitionFailedException; 28 import com.android.camera.util.Provider; 29 import com.google.common.util.concurrent.Futures; 30 31 import java.util.concurrent.ExecutorService; 32 import java.util.concurrent.Future; 33 34 import javax.annotation.Nullable; 35 import javax.annotation.concurrent.GuardedBy; 36 37 /** 38 * Executes camera commands on a thread pool. 39 */ 40 public class CameraCommandExecutor implements SafeCloseable { 41 private class CommandRunnable implements Runnable { 42 private final CameraCommand mCommand; 43 CommandRunnable(CameraCommand command)44 public CommandRunnable(CameraCommand command) { 45 mCommand = command; 46 } 47 48 @Override run()49 public void run() { 50 try { 51 mLog.d("Executing command: " + mCommand + " START"); 52 mCommand.run(); 53 mLog.d("Executing command: " + mCommand + " END"); 54 } catch (ResourceAcquisitionFailedException e) { 55 // This may indicate that the command would have otherwise 56 // deadlocked waiting for resources which can never be acquired, 57 // or the command was aborted because the necessary resources 58 // will never be available because the system is shutting down. 59 e.printStackTrace(); 60 } catch (InterruptedException e) { 61 // If interrupted, just return because the system is shutting 62 // down. 63 mLog.d("Interrupted while executing command: " + mCommand); 64 } catch (CameraAccessException e) { 65 // If the camera was closed and the command failed, just return. 66 mLog.d("Unable to connect to camera while executing command: " + mCommand); 67 } catch (CameraCaptureSessionClosedException e) { 68 // If the session was closed and the command failed, just 69 // return. 70 mLog.d("Unable to connect to capture session while executing command: " + 71 mCommand); 72 } catch (Exception e) { 73 mLog.e("Exception when executing command: " + mCommand, e); 74 } 75 } 76 } 77 78 private final Logger mLog; 79 private final Provider<ExecutorService> mExecutorProvider; 80 private final Object mLock; 81 @Nullable 82 @GuardedBy("mLock") 83 private ExecutorService mExecutor; 84 @GuardedBy("mLock") 85 private boolean mClosed; 86 CameraCommandExecutor(Logger.Factory loggerFactory, Provider<ExecutorService> threadPoolExecutor)87 public CameraCommandExecutor(Logger.Factory loggerFactory, 88 Provider<ExecutorService> threadPoolExecutor) { 89 mLog = loggerFactory.create(new Log.Tag("CommandExecutor")); 90 91 mLock = new Object(); 92 mExecutorProvider = threadPoolExecutor; 93 mClosed = false; 94 } 95 96 /** 97 * Executes the given command, returning a Future to indicate its status and 98 * allow (interruptible) cancellation. 99 */ execute(CameraCommand command)100 public Future<?> execute(CameraCommand command) { 101 if (mClosed) { 102 return Futures.immediateFuture(null); 103 } 104 synchronized (mLock) { 105 if (mExecutor == null) { 106 // Create a new executor, if necessary. 107 mExecutor = mExecutorProvider.get(); 108 } 109 checkNotNull(mExecutor); 110 return mExecutor.submit(new CommandRunnable(command)); 111 } 112 } 113 114 /** 115 * Cancels any pending, or currently-executing commands. 116 * <p> 117 * Like {@link #close} but allows reusing the object. 118 */ flush()119 public void flush() { 120 synchronized (mLock) { 121 if (mExecutor != null) { 122 mExecutor.shutdownNow(); 123 } 124 125 mExecutor = null; 126 } 127 } 128 129 @Override close()130 public void close() { 131 // Shutdown immediately by interrupting all currently-executing 132 // commands. Sending an interrupt is critical since commands may be 133 // waiting for results from the camera device which will never arrive, 134 // or for resources which may no longer be acquired. 135 synchronized (mLock) { 136 if (mExecutor != null) { 137 mExecutor.shutdownNow(); 138 } 139 140 mExecutor = null; 141 mClosed = true; 142 } 143 } 144 } 145