1 /* 2 * Copyright (C) 2013 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 android.hardware.camera2.impl; 18 19 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; 20 21 import android.hardware.camera2.CameraAccessException; 22 import android.hardware.camera2.CaptureRequest; 23 import android.hardware.camera2.CaptureResult; 24 import android.hardware.camera2.ICameraDeviceCallbacks; 25 import android.hardware.camera2.ICameraDeviceUser; 26 import android.hardware.camera2.utils.CameraBinderDecorator; 27 import android.hardware.camera2.utils.CameraRuntimeException; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Looper; 31 import android.os.RemoteException; 32 import android.util.Log; 33 import android.util.SparseArray; 34 import android.view.Surface; 35 36 import java.util.ArrayList; 37 import java.util.HashSet; 38 import java.util.Iterator; 39 import java.util.List; 40 41 /** 42 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate 43 */ 44 public class CameraDevice implements android.hardware.camera2.CameraDevice { 45 46 private final String TAG; 47 private final boolean DEBUG; 48 49 private static final int REQUEST_ID_NONE = -1; 50 51 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) 52 private ICameraDeviceUser mRemoteDevice; 53 54 private final Object mLock = new Object(); 55 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); 56 57 private final StateListener mDeviceListener; 58 private final Handler mDeviceHandler; 59 60 private boolean mIdle = true; 61 62 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap = 63 new SparseArray<CaptureListenerHolder>(); 64 65 private int mRepeatingRequestId = REQUEST_ID_NONE; 66 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>(); 67 // Map stream IDs to Surfaces 68 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>(); 69 70 private final String mCameraId; 71 72 // Runnables for all state transitions, except error, which needs the 73 // error code argument 74 75 private final Runnable mCallOnOpened = new Runnable() { 76 public void run() { 77 if (!CameraDevice.this.isClosed()) { 78 mDeviceListener.onOpened(CameraDevice.this); 79 } 80 } 81 }; 82 83 private final Runnable mCallOnUnconfigured = new Runnable() { 84 public void run() { 85 if (!CameraDevice.this.isClosed()) { 86 mDeviceListener.onUnconfigured(CameraDevice.this); 87 } 88 } 89 }; 90 91 private final Runnable mCallOnActive = new Runnable() { 92 public void run() { 93 if (!CameraDevice.this.isClosed()) { 94 mDeviceListener.onActive(CameraDevice.this); 95 } 96 } 97 }; 98 99 private final Runnable mCallOnBusy = new Runnable() { 100 public void run() { 101 if (!CameraDevice.this.isClosed()) { 102 mDeviceListener.onBusy(CameraDevice.this); 103 } 104 } 105 }; 106 107 private final Runnable mCallOnClosed = new Runnable() { 108 public void run() { 109 if (!CameraDevice.this.isClosed()) { 110 mDeviceListener.onClosed(CameraDevice.this); 111 } 112 } 113 }; 114 115 private final Runnable mCallOnIdle = new Runnable() { 116 public void run() { 117 if (!CameraDevice.this.isClosed()) { 118 mDeviceListener.onIdle(CameraDevice.this); 119 } 120 } 121 }; 122 123 private final Runnable mCallOnDisconnected = new Runnable() { 124 public void run() { 125 if (!CameraDevice.this.isClosed()) { 126 mDeviceListener.onDisconnected(CameraDevice.this); 127 } 128 } 129 }; 130 131 public CameraDevice(String cameraId, StateListener listener, Handler handler) { 132 if (cameraId == null || listener == null || handler == null) { 133 throw new IllegalArgumentException("Null argument given"); 134 } 135 mCameraId = cameraId; 136 mDeviceListener = listener; 137 mDeviceHandler = handler; 138 TAG = String.format("CameraDevice-%s-JV", mCameraId); 139 DEBUG = Log.isLoggable(TAG, Log.DEBUG); 140 } 141 142 public CameraDeviceCallbacks getCallbacks() { 143 return mCallbacks; 144 } 145 146 public void setRemoteDevice(ICameraDeviceUser remoteDevice) { 147 // TODO: Move from decorator to direct binder-mediated exceptions 148 synchronized(mLock) { 149 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice); 150 151 mDeviceHandler.post(mCallOnOpened); 152 mDeviceHandler.post(mCallOnUnconfigured); 153 } 154 } 155 156 @Override 157 public String getId() { 158 return mCameraId; 159 } 160 161 @Override 162 public void configureOutputs(List<Surface> outputs) throws CameraAccessException { 163 // Treat a null input the same an empty list 164 if (outputs == null) { 165 outputs = new ArrayList<Surface>(); 166 } 167 synchronized (mLock) { 168 checkIfCameraClosed(); 169 170 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create 171 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete 172 173 // Determine which streams need to be created, which to be deleted 174 for (int i = 0; i < mConfiguredOutputs.size(); ++i) { 175 int streamId = mConfiguredOutputs.keyAt(i); 176 Surface s = mConfiguredOutputs.valueAt(i); 177 178 if (!outputs.contains(s)) { 179 deleteList.add(streamId); 180 } else { 181 addSet.remove(s); // Don't create a stream previously created 182 } 183 } 184 185 mDeviceHandler.post(mCallOnBusy); 186 stopRepeating(); 187 188 try { 189 waitUntilIdle(); 190 191 // TODO: mRemoteDevice.beginConfigure 192 // Delete all streams first (to free up HW resources) 193 for (Integer streamId : deleteList) { 194 mRemoteDevice.deleteStream(streamId); 195 mConfiguredOutputs.delete(streamId); 196 } 197 198 // Add all new streams 199 for (Surface s : addSet) { 200 // TODO: remove width,height,format since we are ignoring 201 // it. 202 int streamId = mRemoteDevice.createStream(0, 0, 0, s); 203 mConfiguredOutputs.put(streamId, s); 204 } 205 206 // TODO: mRemoteDevice.endConfigure 207 } catch (CameraRuntimeException e) { 208 if (e.getReason() == CAMERA_IN_USE) { 209 throw new IllegalStateException("The camera is currently busy." + 210 " You must wait until the previous operation completes."); 211 } 212 213 throw e.asChecked(); 214 } catch (RemoteException e) { 215 // impossible 216 return; 217 } 218 219 if (outputs.size() > 0) { 220 mDeviceHandler.post(mCallOnIdle); 221 } else { 222 mDeviceHandler.post(mCallOnUnconfigured); 223 } 224 } 225 } 226 227 @Override 228 public CaptureRequest.Builder createCaptureRequest(int templateType) 229 throws CameraAccessException { 230 synchronized (mLock) { 231 checkIfCameraClosed(); 232 233 CameraMetadataNative templatedRequest = new CameraMetadataNative(); 234 235 try { 236 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest); 237 } catch (CameraRuntimeException e) { 238 throw e.asChecked(); 239 } catch (RemoteException e) { 240 // impossible 241 return null; 242 } 243 244 CaptureRequest.Builder builder = 245 new CaptureRequest.Builder(templatedRequest); 246 247 return builder; 248 } 249 } 250 251 @Override 252 public int capture(CaptureRequest request, CaptureListener listener, Handler handler) 253 throws CameraAccessException { 254 return submitCaptureRequest(request, listener, handler, /*streaming*/false); 255 } 256 257 @Override 258 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener, 259 Handler handler) throws CameraAccessException { 260 if (requests.isEmpty()) { 261 Log.w(TAG, "Capture burst request list is empty, do nothing!"); 262 return -1; 263 } 264 // TODO 265 throw new UnsupportedOperationException("Burst capture implemented yet"); 266 267 } 268 269 private int submitCaptureRequest(CaptureRequest request, CaptureListener listener, 270 Handler handler, boolean repeating) throws CameraAccessException { 271 272 // Need a valid handler, or current thread needs to have a looper, if 273 // listener is valid 274 if (listener != null) { 275 handler = checkHandler(handler); 276 } 277 278 synchronized (mLock) { 279 checkIfCameraClosed(); 280 int requestId; 281 282 if (repeating) { 283 stopRepeating(); 284 } 285 286 try { 287 requestId = mRemoteDevice.submitRequest(request, repeating); 288 } catch (CameraRuntimeException e) { 289 throw e.asChecked(); 290 } catch (RemoteException e) { 291 // impossible 292 return -1; 293 } 294 if (listener != null) { 295 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request, 296 handler, repeating)); 297 } 298 299 if (repeating) { 300 mRepeatingRequestId = requestId; 301 } 302 303 if (mIdle) { 304 mDeviceHandler.post(mCallOnActive); 305 } 306 mIdle = false; 307 308 return requestId; 309 } 310 } 311 312 @Override 313 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener, 314 Handler handler) throws CameraAccessException { 315 return submitCaptureRequest(request, listener, handler, /*streaming*/true); 316 } 317 318 @Override 319 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, 320 Handler handler) throws CameraAccessException { 321 if (requests.isEmpty()) { 322 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!"); 323 return -1; 324 } 325 // TODO 326 throw new UnsupportedOperationException("Burst capture implemented yet"); 327 } 328 329 @Override 330 public void stopRepeating() throws CameraAccessException { 331 332 synchronized (mLock) { 333 checkIfCameraClosed(); 334 if (mRepeatingRequestId != REQUEST_ID_NONE) { 335 336 int requestId = mRepeatingRequestId; 337 mRepeatingRequestId = REQUEST_ID_NONE; 338 339 // Queue for deletion after in-flight requests finish 340 mRepeatingRequestIdDeletedList.add(requestId); 341 342 try { 343 mRemoteDevice.cancelRequest(requestId); 344 } catch (CameraRuntimeException e) { 345 throw e.asChecked(); 346 } catch (RemoteException e) { 347 // impossible 348 return; 349 } 350 } 351 } 352 } 353 354 @Override 355 public void waitUntilIdle() throws CameraAccessException { 356 357 synchronized (mLock) { 358 checkIfCameraClosed(); 359 if (mRepeatingRequestId != REQUEST_ID_NONE) { 360 throw new IllegalStateException("Active repeating request ongoing"); 361 } 362 363 try { 364 mRemoteDevice.waitUntilIdle(); 365 } catch (CameraRuntimeException e) { 366 throw e.asChecked(); 367 } catch (RemoteException e) { 368 // impossible 369 return; 370 } 371 372 mRepeatingRequestId = REQUEST_ID_NONE; 373 mRepeatingRequestIdDeletedList.clear(); 374 mCaptureListenerMap.clear(); 375 } 376 } 377 378 @Override 379 public void flush() throws CameraAccessException { 380 synchronized (mLock) { 381 checkIfCameraClosed(); 382 383 mDeviceHandler.post(mCallOnBusy); 384 try { 385 mRemoteDevice.flush(); 386 } catch (CameraRuntimeException e) { 387 throw e.asChecked(); 388 } catch (RemoteException e) { 389 // impossible 390 return; 391 } 392 } 393 } 394 395 @Override 396 public void close() { 397 synchronized (mLock) { 398 399 try { 400 if (mRemoteDevice != null) { 401 mRemoteDevice.disconnect(); 402 } 403 } catch (CameraRuntimeException e) { 404 Log.e(TAG, "Exception while closing: ", e.asChecked()); 405 } catch (RemoteException e) { 406 // impossible 407 } 408 409 if (mRemoteDevice != null) { 410 mDeviceHandler.post(mCallOnClosed); 411 } 412 413 mRemoteDevice = null; 414 } 415 } 416 417 @Override 418 protected void finalize() throws Throwable { 419 try { 420 close(); 421 } 422 finally { 423 super.finalize(); 424 } 425 } 426 427 static class CaptureListenerHolder { 428 429 private final boolean mRepeating; 430 private final CaptureListener mListener; 431 private final CaptureRequest mRequest; 432 private final Handler mHandler; 433 434 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler, 435 boolean repeating) { 436 if (listener == null || handler == null) { 437 throw new UnsupportedOperationException( 438 "Must have a valid handler and a valid listener"); 439 } 440 mRepeating = repeating; 441 mHandler = handler; 442 mRequest = request; 443 mListener = listener; 444 } 445 446 public boolean isRepeating() { 447 return mRepeating; 448 } 449 450 public CaptureListener getListener() { 451 return mListener; 452 } 453 454 public CaptureRequest getRequest() { 455 return mRequest; 456 } 457 458 public Handler getHandler() { 459 return mHandler; 460 } 461 462 } 463 464 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 465 466 // 467 // Constants below need to be kept up-to-date with 468 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h 469 // 470 471 // 472 // Error codes for onCameraError 473 // 474 475 /** 476 * Camera has been disconnected 477 */ 478 static final int ERROR_CAMERA_DISCONNECTED = 0; 479 480 /** 481 * Camera has encountered a device-level error 482 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE 483 */ 484 static final int ERROR_CAMERA_DEVICE = 1; 485 486 /** 487 * Camera has encountered a service-level error 488 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE 489 */ 490 static final int ERROR_CAMERA_SERVICE = 2; 491 492 @Override 493 public IBinder asBinder() { 494 return this; 495 } 496 497 @Override 498 public void onCameraError(final int errorCode) { 499 Runnable r = null; 500 if (isClosed()) return; 501 502 synchronized(mLock) { 503 switch (errorCode) { 504 case ERROR_CAMERA_DISCONNECTED: 505 r = mCallOnDisconnected; 506 break; 507 default: 508 Log.e(TAG, "Unknown error from camera device: " + errorCode); 509 // no break 510 case ERROR_CAMERA_DEVICE: 511 case ERROR_CAMERA_SERVICE: 512 r = new Runnable() { 513 public void run() { 514 if (!CameraDevice.this.isClosed()) { 515 mDeviceListener.onError(CameraDevice.this, errorCode); 516 } 517 } 518 }; 519 break; 520 } 521 CameraDevice.this.mDeviceHandler.post(r); 522 } 523 } 524 525 @Override 526 public void onCameraIdle() { 527 if (isClosed()) return; 528 529 if (DEBUG) { 530 Log.d(TAG, "Camera now idle"); 531 } 532 synchronized (mLock) { 533 if (!CameraDevice.this.mIdle) { 534 CameraDevice.this.mDeviceHandler.post(mCallOnIdle); 535 } 536 CameraDevice.this.mIdle = true; 537 } 538 } 539 540 @Override 541 public void onCaptureStarted(int requestId, final long timestamp) { 542 if (DEBUG) { 543 Log.d(TAG, "Capture started for id " + requestId); 544 } 545 final CaptureListenerHolder holder; 546 547 // Get the listener for this frame ID, if there is one 548 synchronized (mLock) { 549 holder = CameraDevice.this.mCaptureListenerMap.get(requestId); 550 } 551 552 if (holder == null) { 553 return; 554 } 555 556 if (isClosed()) return; 557 558 // Dispatch capture start notice 559 holder.getHandler().post( 560 new Runnable() { 561 public void run() { 562 if (!CameraDevice.this.isClosed()) { 563 holder.getListener().onCaptureStarted( 564 CameraDevice.this, 565 holder.getRequest(), 566 timestamp); 567 } 568 } 569 }); 570 } 571 572 @Override 573 public void onResultReceived(int requestId, CameraMetadataNative result) 574 throws RemoteException { 575 if (DEBUG) { 576 Log.d(TAG, "Received result for id " + requestId); 577 } 578 final CaptureListenerHolder holder; 579 580 Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT); 581 boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial); 582 583 synchronized (mLock) { 584 // TODO: move this whole map into this class to make it more testable, 585 // exposing the methods necessary like subscribeToRequest, unsubscribe.. 586 // TODO: make class static class 587 588 holder = CameraDevice.this.mCaptureListenerMap.get(requestId); 589 590 // Clean up listener once we no longer expect to see it. 591 if (holder != null && !holder.isRepeating() && !quirkIsPartialResult) { 592 CameraDevice.this.mCaptureListenerMap.remove(requestId); 593 } 594 595 // TODO: add 'capture sequence completed' callback to the 596 // service, and clean up repeating requests there instead. 597 598 // If we received a result for a repeating request and have 599 // prior repeating requests queued for deletion, remove those 600 // requests from mCaptureListenerMap. 601 if (holder != null && holder.isRepeating() && !quirkIsPartialResult 602 && mRepeatingRequestIdDeletedList.size() > 0) { 603 Iterator<Integer> iter = mRepeatingRequestIdDeletedList.iterator(); 604 while (iter.hasNext()) { 605 int deletedRequestId = iter.next(); 606 if (deletedRequestId < requestId) { 607 CameraDevice.this.mCaptureListenerMap.remove(deletedRequestId); 608 iter.remove(); 609 } 610 } 611 } 612 613 } 614 615 // Check if we have a listener for this 616 if (holder == null) { 617 return; 618 } 619 620 if (isClosed()) return; 621 622 final CaptureRequest request = holder.getRequest(); 623 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId); 624 625 Runnable resultDispatch = null; 626 627 // Either send a partial result or the final capture completed result 628 if (quirkIsPartialResult) { 629 // Partial result 630 resultDispatch = new Runnable() { 631 @Override 632 public void run() { 633 if (!CameraDevice.this.isClosed()){ 634 holder.getListener().onCapturePartial( 635 CameraDevice.this, 636 request, 637 resultAsCapture); 638 } 639 } 640 }; 641 } else { 642 // Final capture result 643 resultDispatch = new Runnable() { 644 @Override 645 public void run() { 646 if (!CameraDevice.this.isClosed()){ 647 holder.getListener().onCaptureCompleted( 648 CameraDevice.this, 649 request, 650 resultAsCapture); 651 } 652 } 653 }; 654 } 655 656 holder.getHandler().post(resultDispatch); 657 } 658 659 } 660 661 /** 662 * Default handler management. If handler is null, get the current thread's 663 * Looper to create a Handler with. If no looper exists, throw exception. 664 */ 665 private Handler checkHandler(Handler handler) { 666 if (handler == null) { 667 Looper looper = Looper.myLooper(); 668 if (looper == null) { 669 throw new IllegalArgumentException( 670 "No handler given, and current thread has no looper!"); 671 } 672 handler = new Handler(looper); 673 } 674 return handler; 675 } 676 677 private void checkIfCameraClosed() { 678 if (mRemoteDevice == null) { 679 throw new IllegalStateException("CameraDevice was already closed"); 680 } 681 } 682 683 private boolean isClosed() { 684 synchronized(mLock) { 685 return (mRemoteDevice == null); 686 } 687 } 688 } 689