1 /* 2 * Copyright (C) 2020 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 package android.hardware.camera2; 17 18 import android.annotation.FlaggedApi; 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SuppressLint; 23 import android.content.ComponentName; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.PackageManager; 28 import android.content.ServiceConnection; 29 import android.graphics.ImageFormat; 30 import android.hardware.camera2.CameraCharacteristics.Key; 31 import android.hardware.camera2.extension.IAdvancedExtenderImpl; 32 import android.hardware.camera2.extension.ICameraExtensionsProxyService; 33 import android.hardware.camera2.extension.IImageCaptureExtenderImpl; 34 import android.hardware.camera2.extension.IInitializeSessionCallback; 35 import android.hardware.camera2.extension.IPreviewExtenderImpl; 36 import android.hardware.camera2.extension.LatencyRange; 37 import android.hardware.camera2.extension.SizeList; 38 import android.hardware.camera2.impl.CameraExtensionUtils; 39 import android.hardware.camera2.impl.CameraMetadataNative; 40 import android.hardware.camera2.impl.ExtensionKey; 41 import android.hardware.camera2.impl.PublicKey; 42 import android.hardware.camera2.params.ExtensionSessionConfiguration; 43 import android.hardware.camera2.params.StreamConfigurationMap; 44 import android.os.Binder; 45 import android.os.ConditionVariable; 46 import android.os.IBinder; 47 import android.os.RemoteException; 48 import android.os.SystemProperties; 49 import android.provider.Settings; 50 import android.util.IntArray; 51 import android.util.Log; 52 import android.util.Pair; 53 import android.util.Range; 54 import android.util.Size; 55 56 import com.android.internal.camera.flags.Flags; 57 58 import java.lang.annotation.Retention; 59 import java.lang.annotation.RetentionPolicy; 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.Collections; 63 import java.util.HashMap; 64 import java.util.HashSet; 65 import java.util.List; 66 import java.util.Map; 67 import java.util.Objects; 68 import java.util.Set; 69 import java.util.concurrent.Future; 70 import java.util.concurrent.TimeUnit; 71 import java.util.concurrent.TimeoutException; 72 73 /** 74 * <p>Allows clients to query availability and supported resolutions of camera extensions.</p> 75 * 76 * <p>Camera extensions give camera clients access to device-specific algorithms and sequences that 77 * can improve the overall image quality of snapshots in various cases such as low light, selfies, 78 * portraits, and scenes that can benefit from enhanced dynamic range. Often such sophisticated 79 * processing sequences will rely on multiple camera frames as input and will produce a single 80 * output.</p> 81 * 82 * <p>Camera extensions are not guaranteed to be present on all devices so camera clients must 83 * query for their availability via {@link CameraExtensionCharacteristics#getSupportedExtensions()}. 84 * </p> 85 * 86 * <p>In order to use any available camera extension, camera clients must create a corresponding 87 * {@link CameraExtensionSession} via 88 * {@link CameraDevice#createExtensionSession(ExtensionSessionConfiguration)}</p> 89 * 90 * <p>Camera clients must be aware that device-specific camera extensions may support only a 91 * subset of the available camera resolutions and must first query 92 * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)} for supported 93 * single high-quality request output sizes and 94 * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, Class)} for supported 95 * repeating request output sizes.</p> 96 * 97 * <p>The extension characteristics for a given device are expected to remain static under 98 * normal operating conditions.</p> 99 * 100 * @see CameraManager#getCameraExtensionCharacteristics(String) 101 */ 102 public final class CameraExtensionCharacteristics { 103 private static final String TAG = "CameraExtensionCharacteristics"; 104 105 /** 106 * Device-specific extension implementation for automatic selection of particular extension 107 * such as HDR or NIGHT depending on the current lighting and environment conditions. 108 */ 109 public static final int EXTENSION_AUTOMATIC = 0; 110 111 /** 112 * Device-specific extension implementation which tends to smooth the skin and apply other 113 * cosmetic effects to people's faces. 114 */ 115 public static final int EXTENSION_FACE_RETOUCH = 1; 116 117 /** 118 * Device-specific extension implementation which tends to smooth the skin and apply other 119 * cosmetic effects to people's faces. 120 * 121 * @deprecated Use {@link #EXTENSION_FACE_RETOUCH} instead. 122 */ 123 public @Deprecated static final int EXTENSION_BEAUTY = EXTENSION_FACE_RETOUCH; 124 125 /** 126 * Device-specific extension implementation which can blur certain regions of the final image 127 * thereby "enhancing" focus for all remaining non-blurred parts. 128 */ 129 public static final int EXTENSION_BOKEH = 2; 130 131 /** 132 * Device-specific extension implementation for enhancing the dynamic range of the 133 * final image. 134 */ 135 public static final int EXTENSION_HDR = 3; 136 137 /** 138 * Device-specific extension implementation that aims to suppress noise and improve the 139 * overall image quality under low light conditions. 140 */ 141 public static final int EXTENSION_NIGHT = 4; 142 143 /** 144 * An extension that aims to lock and stabilize a given region or object of interest. 145 */ 146 @FlaggedApi(Flags.FLAG_CONCERT_MODE_API) 147 public static final int EXTENSION_EYES_FREE_VIDEOGRAPHY = 5; 148 149 /** 150 * @hide 151 */ 152 @Retention(RetentionPolicy.SOURCE) 153 @IntDef(flag = true, value = {EXTENSION_AUTOMATIC, 154 EXTENSION_FACE_RETOUCH, 155 EXTENSION_BOKEH, 156 EXTENSION_HDR, 157 EXTENSION_NIGHT, 158 EXTENSION_EYES_FREE_VIDEOGRAPHY}) 159 public @interface Extension { 160 } 161 162 /** 163 * Default camera output in case additional processing from CameraX extensions is not needed 164 * 165 * @hide 166 */ 167 public static final int NON_PROCESSING_INPUT_FORMAT = ImageFormat.PRIVATE; 168 169 /** 170 * CameraX extensions require YUV_420_888 as default input for processing at the moment 171 * 172 * @hide 173 */ 174 public static final int PROCESSING_INPUT_FORMAT = ImageFormat.YUV_420_888; 175 176 private static final @Extension 177 int[] EXTENSION_LIST = new int[]{ 178 EXTENSION_AUTOMATIC, 179 EXTENSION_FACE_RETOUCH, 180 EXTENSION_BOKEH, 181 EXTENSION_HDR, 182 EXTENSION_NIGHT}; 183 184 /** 185 * List of synthetic CameraCharacteristics keys that are supported in the extensions. 186 */ 187 private static final List<CameraCharacteristics.Key> 188 SUPPORTED_SYNTHETIC_CAMERA_CHARACTERISTICS = 189 Arrays.asList( 190 CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES, 191 CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES 192 ); 193 194 private final Context mContext; 195 private final String mCameraId; 196 private final Map<String, CameraCharacteristics> mCharacteristicsMap; 197 private final Map<String, CameraMetadataNative> mCharacteristicsMapNative; 198 199 /** 200 * @hide 201 */ CameraExtensionCharacteristics(Context context, String cameraId, Map<String, CameraCharacteristics> characteristicsMap)202 public CameraExtensionCharacteristics(Context context, String cameraId, 203 Map<String, CameraCharacteristics> characteristicsMap) { 204 mContext = context; 205 mCameraId = cameraId; 206 mCharacteristicsMap = characteristicsMap; 207 mCharacteristicsMapNative = 208 CameraExtensionUtils.getCharacteristicsMapNative(characteristicsMap); 209 } 210 getSupportedSizes(List<SizeList> sizesList, Integer format)211 private static ArrayList<Size> getSupportedSizes(List<SizeList> sizesList, 212 Integer format) { 213 ArrayList<Size> ret = new ArrayList<>(); 214 if ((sizesList != null) && (!sizesList.isEmpty())) { 215 for (SizeList entry : sizesList) { 216 if ((entry.format == format) && !entry.sizes.isEmpty()) { 217 for (android.hardware.camera2.extension.Size sz : entry.sizes) { 218 ret.add(new Size(sz.width, sz.height)); 219 } 220 return ret; 221 } 222 } 223 } 224 225 return ret; 226 } 227 generateSupportedSizes(List<SizeList> sizesList, Integer format, StreamConfigurationMap streamMap)228 private static List<Size> generateSupportedSizes(List<SizeList> sizesList, 229 Integer format, 230 StreamConfigurationMap streamMap) { 231 ArrayList<Size> ret = getSupportedSizes(sizesList, format); 232 233 if (format == ImageFormat.JPEG || format == ImageFormat.YUV_420_888 || 234 format == ImageFormat.PRIVATE) { 235 // Per API contract it is assumed that the extension is able to support all 236 // camera advertised sizes for JPEG, YUV_420_888 and PRIVATE in case it doesn't return 237 // a valid non-empty size list. 238 Size[] supportedSizes = streamMap.getOutputSizes(format); 239 if ((ret.isEmpty()) && (supportedSizes != null)) { 240 ret.addAll(Arrays.asList(supportedSizes)); 241 } 242 } 243 244 return ret; 245 } 246 generateJpegSupportedSizes(List<SizeList> sizesList, StreamConfigurationMap streamMap)247 private static List<Size> generateJpegSupportedSizes(List<SizeList> sizesList, 248 StreamConfigurationMap streamMap) { 249 ArrayList<Size> extensionSizes = getSupportedSizes(sizesList, ImageFormat.YUV_420_888); 250 HashSet<Size> supportedSizes = extensionSizes.isEmpty() ? new HashSet<>(Arrays.asList( 251 streamMap.getOutputSizes(ImageFormat.YUV_420_888))) : new HashSet<>(extensionSizes); 252 HashSet<Size> supportedJpegSizes = new HashSet<>(Arrays.asList(streamMap.getOutputSizes( 253 ImageFormat.JPEG))); 254 supportedSizes.retainAll(supportedJpegSizes); 255 256 return new ArrayList<>(supportedSizes); 257 } 258 259 /** 260 * A per-process global camera extension manager instance, to track and 261 * initialize/release extensions depending on client activity. 262 */ 263 private static final class CameraExtensionManagerGlobal { 264 private static final String TAG = "CameraExtensionManagerGlobal"; 265 private static final String PROXY_PACKAGE_NAME = "com.android.cameraextensions"; 266 private static final String PROXY_SERVICE_NAME = 267 "com.android.cameraextensions.CameraExtensionsProxyService"; 268 269 @FlaggedApi(Flags.FLAG_CONCERT_MODE) 270 private static final int FALLBACK_PACKAGE_NAME = 271 com.android.internal.R.string.config_extensionFallbackPackageName; 272 @FlaggedApi(Flags.FLAG_CONCERT_MODE) 273 private static final int FALLBACK_SERVICE_NAME = 274 com.android.internal.R.string.config_extensionFallbackServiceName; 275 276 // Singleton instance 277 private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER = 278 new CameraExtensionManagerGlobal(); 279 private final Object mLock = new Object(); 280 private final int PROXY_SERVICE_DELAY_MS = 2000; 281 private ExtensionConnectionManager mConnectionManager = new ExtensionConnectionManager(); 282 private boolean mPermissionForFallbackEnabled = false; 283 private boolean mIsFallbackEnabled = false; 284 285 // Singleton, don't allow construction CameraExtensionManagerGlobal()286 private CameraExtensionManagerGlobal() {} 287 get()288 public static CameraExtensionManagerGlobal get() { 289 return GLOBAL_CAMERA_MANAGER; 290 } 291 releaseProxyConnectionLocked(Context ctx, int extension)292 private void releaseProxyConnectionLocked(Context ctx, int extension) { 293 if (mConnectionManager.getConnection(extension) != null) { 294 ctx.unbindService(mConnectionManager.getConnection(extension)); 295 mConnectionManager.setConnection(extension, null); 296 mConnectionManager.setProxy(extension, null); 297 mConnectionManager.resetConnectionCount(extension); 298 } 299 } 300 connectToProxyLocked(Context ctx, int extension, boolean useFallback)301 private void connectToProxyLocked(Context ctx, int extension, boolean useFallback) { 302 if (mConnectionManager.getConnection(extension) == null) { 303 Intent intent = new Intent(); 304 intent.setClassName(PROXY_PACKAGE_NAME, PROXY_SERVICE_NAME); 305 String vendorProxyPackage = SystemProperties.get( 306 "ro.vendor.camera.extensions.package"); 307 String vendorProxyService = SystemProperties.get( 308 "ro.vendor.camera.extensions.service"); 309 if (!vendorProxyPackage.isEmpty() && !vendorProxyService.isEmpty()) { 310 Log.v(TAG, 311 "Choosing the vendor camera extensions proxy package: " 312 + vendorProxyPackage); 313 Log.v(TAG, 314 "Choosing the vendor camera extensions proxy service: " 315 + vendorProxyService); 316 intent.setClassName(vendorProxyPackage, vendorProxyService); 317 } 318 319 if (Flags.concertMode() && useFallback) { 320 String packageName = ctx.getResources().getString(FALLBACK_PACKAGE_NAME); 321 String serviceName = ctx.getResources().getString(FALLBACK_SERVICE_NAME); 322 323 if (!packageName.isEmpty() && !serviceName.isEmpty()) { 324 Log.v(TAG, 325 "Choosing the fallback software implementation package: " 326 + packageName); 327 Log.v(TAG, 328 "Choosing the fallback software implementation service: " 329 + serviceName); 330 intent.setClassName(packageName, serviceName); 331 mIsFallbackEnabled = true; 332 } 333 } 334 335 InitializerFuture initFuture = new InitializerFuture(); 336 ServiceConnection connection = new ServiceConnection() { 337 @Override 338 public void onServiceDisconnected(ComponentName component) { 339 mConnectionManager.setConnection(extension, null); 340 mConnectionManager.setProxy(extension, null); 341 } 342 343 @Override 344 public void onServiceConnected(ComponentName component, IBinder binder) { 345 ICameraExtensionsProxyService proxy = 346 ICameraExtensionsProxyService.Stub.asInterface(binder); 347 mConnectionManager.setProxy(extension, proxy); 348 if (mConnectionManager.getProxy(extension) == null) { 349 throw new IllegalStateException("Camera Proxy service is null"); 350 } 351 try { 352 mConnectionManager.setAdvancedExtensionsSupported(extension, 353 mConnectionManager.getProxy(extension) 354 .advancedExtensionsSupported()); 355 } catch (RemoteException e) { 356 Log.e(TAG, "Remote IPC failed!"); 357 } 358 initFuture.setStatus(true); 359 } 360 }; 361 ctx.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | 362 Context.BIND_ABOVE_CLIENT | Context.BIND_NOT_VISIBLE, 363 android.os.AsyncTask.THREAD_POOL_EXECUTOR, connection); 364 mConnectionManager.setConnection(extension, connection); 365 366 try { 367 initFuture.get(PROXY_SERVICE_DELAY_MS, TimeUnit.MILLISECONDS); 368 } catch (TimeoutException e) { 369 Log.e(TAG, "Timed out while initializing proxy service!"); 370 } 371 } 372 } 373 374 private static class InitializerFuture implements Future<Boolean> { 375 private volatile Boolean mStatus; 376 ConditionVariable mCondVar = new ConditionVariable(/*opened*/false); 377 setStatus(boolean status)378 public void setStatus(boolean status) { 379 mStatus = status; 380 mCondVar.open(); 381 } 382 383 @Override cancel(boolean mayInterruptIfRunning)384 public boolean cancel(boolean mayInterruptIfRunning) { 385 return false; // don't allow canceling this task 386 } 387 388 @Override isCancelled()389 public boolean isCancelled() { 390 return false; // can never cancel this task 391 } 392 393 @Override isDone()394 public boolean isDone() { 395 return mStatus != null; 396 } 397 398 @Override get()399 public Boolean get() { 400 mCondVar.block(); 401 return mStatus; 402 } 403 404 @Override get(long timeout, TimeUnit unit)405 public Boolean get(long timeout, TimeUnit unit) throws TimeoutException { 406 long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS); 407 if (!mCondVar.block(timeoutMs)) { 408 throw new TimeoutException( 409 "Failed to receive status after " + timeout + " " + unit); 410 } 411 412 if (mStatus == null) { 413 throw new AssertionError(); 414 } 415 return mStatus; 416 } 417 } 418 registerClientHelper(Context ctx, IBinder token, int extension, boolean useFallback)419 public boolean registerClientHelper(Context ctx, IBinder token, int extension, 420 boolean useFallback) { 421 synchronized (mLock) { 422 boolean ret = false; 423 connectToProxyLocked(ctx, extension, useFallback); 424 if (mConnectionManager.getProxy(extension) == null) { 425 return false; 426 } 427 mConnectionManager.incrementConnectionCount(extension); 428 429 try { 430 ret = mConnectionManager.getProxy(extension).registerClient(token); 431 } catch (RemoteException e) { 432 Log.e(TAG, "Failed to initialize extension! Extension service does " 433 + " not respond!"); 434 } 435 if (!ret) { 436 mConnectionManager.decrementConnectionCount(extension); 437 } 438 439 if (mConnectionManager.getConnectionCount(extension) <= 0) { 440 releaseProxyConnectionLocked(ctx, extension); 441 } 442 443 if (Flags.concertMode() && ret && useFallback && mIsFallbackEnabled) { 444 try { 445 InitializeSessionHandler cb = new InitializeSessionHandler(ctx); 446 initializeSession(cb, extension); 447 ret = mPermissionForFallbackEnabled; 448 } catch (RemoteException e) { 449 Log.e(TAG, "Failed to initialize extension. Extension service does not" 450 + " respond!"); 451 ret = false; 452 } finally { 453 releaseSession(extension); 454 } 455 } 456 457 return ret; 458 } 459 } 460 461 @SuppressLint("NonUserGetterCalled") registerClient(Context ctx, IBinder token, int extension, String cameraId, Map<String, CameraMetadataNative> characteristicsMapNative)462 public boolean registerClient(Context ctx, IBinder token, int extension, 463 String cameraId, Map<String, CameraMetadataNative> characteristicsMapNative) { 464 boolean ret = registerClientHelper(ctx, token, extension, false /*useFallback*/); 465 466 if (Flags.concertMode()) { 467 // Check if user enabled fallback impl 468 ContentResolver resolver = ctx.getContentResolver(); 469 int userEnabled = Settings.Secure.getInt(resolver, 470 Settings.Secure.CAMERA_EXTENSIONS_FALLBACK, 1); 471 472 boolean vendorImpl = true; 473 if (ret && (mConnectionManager.getProxy(extension) != null) && (userEnabled == 1)) { 474 // At this point, we are connected to either CameraExtensionsProxyService or 475 // the vendor extension proxy service. If the vendor does not support the 476 // extension, unregisterClient and re-register client with the proxy service 477 // containing the fallback impl 478 vendorImpl = isExtensionSupported(cameraId, extension, 479 characteristicsMapNative); 480 } 481 482 if (!vendorImpl) { 483 unregisterClient(ctx, token, extension); 484 ret = registerClientHelper(ctx, token, extension, true /*useFallback*/); 485 } 486 } 487 488 return ret; 489 } 490 unregisterClient(Context ctx, IBinder token, int extension)491 public void unregisterClient(Context ctx, IBinder token, int extension) { 492 synchronized (mLock) { 493 if (mConnectionManager.getProxy(extension) != null) { 494 try { 495 mConnectionManager.getProxy(extension).unregisterClient(token); 496 } catch (RemoteException e) { 497 Log.e(TAG, "Failed to de-initialize extension! Extension service does" 498 + " not respond!"); 499 } finally { 500 mConnectionManager.decrementConnectionCount(extension); 501 if (mConnectionManager.getConnectionCount(extension) <= 0) { 502 releaseProxyConnectionLocked(ctx, extension); 503 } 504 } 505 } 506 } 507 } 508 initializeSession(IInitializeSessionCallback cb, int extension)509 public void initializeSession(IInitializeSessionCallback cb, int extension) 510 throws RemoteException { 511 synchronized (mLock) { 512 if (mConnectionManager.getProxy(extension) != null 513 && !mConnectionManager.isSessionInitialized()) { 514 mConnectionManager.getProxy(extension).initializeSession(cb); 515 mConnectionManager.setSessionInitialized(true); 516 } else { 517 cb.onFailure(); 518 } 519 } 520 } 521 releaseSession(int extension)522 public void releaseSession(int extension) { 523 synchronized (mLock) { 524 if (mConnectionManager.getProxy(extension) != null) { 525 try { 526 mConnectionManager.getProxy(extension).releaseSession(); 527 mConnectionManager.setSessionInitialized(false); 528 mPermissionForFallbackEnabled = false; // Reset permission status 529 } catch (RemoteException e) { 530 Log.e(TAG, "Failed to release session! Extension service does" 531 + " not respond!"); 532 } 533 } 534 } 535 } 536 areAdvancedExtensionsSupported(int extension)537 public boolean areAdvancedExtensionsSupported(int extension) { 538 return mConnectionManager.areAdvancedExtensionsSupported(extension); 539 } 540 initializePreviewExtension(int extension)541 public IPreviewExtenderImpl initializePreviewExtension(int extension) 542 throws RemoteException { 543 synchronized (mLock) { 544 if (mConnectionManager.getProxy(extension) != null) { 545 return mConnectionManager.getProxy(extension) 546 .initializePreviewExtension(extension); 547 } else { 548 return null; 549 } 550 } 551 } 552 initializeImageExtension(int extension)553 public IImageCaptureExtenderImpl initializeImageExtension(int extension) 554 throws RemoteException { 555 synchronized (mLock) { 556 if (mConnectionManager.getProxy(extension) != null) { 557 return mConnectionManager.getProxy(extension) 558 .initializeImageExtension(extension); 559 } else { 560 return null; 561 } 562 } 563 } 564 initializeAdvancedExtension(int extension)565 public IAdvancedExtenderImpl initializeAdvancedExtension(int extension) 566 throws RemoteException { 567 synchronized (mLock) { 568 if (mConnectionManager.getProxy(extension) != null) { 569 return mConnectionManager.getProxy(extension) 570 .initializeAdvancedExtension(extension); 571 } else { 572 return null; 573 } 574 } 575 } 576 577 private class InitializeSessionHandler extends IInitializeSessionCallback.Stub { 578 private Context mContext; 579 InitializeSessionHandler(Context context)580 public InitializeSessionHandler(Context context) { 581 mContext = context; 582 } 583 584 @Override onSuccess()585 public void onSuccess() { 586 // Verify that the camera permission is granted if using 587 // the fallback implementation for an extension 588 String[] callingUidPackages = mContext.getPackageManager() 589 .getPackagesForUid(Binder.getCallingUid()); 590 String fallbackPackageName = mContext.getResources() 591 .getString(FALLBACK_PACKAGE_NAME); 592 593 if (!fallbackPackageName.isEmpty() 594 && Arrays.stream(callingUidPackages) 595 .anyMatch(fallbackPackageName::equals)) { 596 String[] cameraPermissions = { 597 android.Manifest.permission.SYSTEM_CAMERA, 598 android.Manifest.permission.CAMERA 599 }; 600 601 boolean allPermissionsGranted = true; 602 for (String permission : cameraPermissions) { 603 int permissionResult = mContext.checkPermission(permission, 604 Binder.getCallingPid(), Binder.getCallingUid()); 605 if (permissionResult != PackageManager.PERMISSION_GRANTED) { 606 Log.w(TAG, permission + " permission not granted for " 607 + fallbackPackageName + ", permission check result: " 608 + permissionResult); 609 allPermissionsGranted = false; 610 } 611 } 612 613 mPermissionForFallbackEnabled = allPermissionsGranted; 614 } 615 } 616 617 @Override onFailure()618 public void onFailure() { 619 Log.e(TAG, "Failed to initialize proxy service session!"); 620 } 621 } 622 623 private class ExtensionConnectionManager { 624 // Maps extension to ExtensionConnection 625 private Map<Integer, ExtensionConnection> mConnections = new HashMap<>(); 626 private boolean mSessionInitialized = false; 627 ExtensionConnectionManager()628 public ExtensionConnectionManager() { 629 IntArray extensionList = new IntArray(EXTENSION_LIST.length); 630 extensionList.addAll(EXTENSION_LIST); 631 if (Flags.concertModeApi()) { 632 extensionList.add(EXTENSION_EYES_FREE_VIDEOGRAPHY); 633 } 634 635 for (int extensionType : extensionList.toArray()) { 636 mConnections.put(extensionType, new ExtensionConnection()); 637 } 638 } 639 getProxy(@xtension int extension)640 public ICameraExtensionsProxyService getProxy(@Extension int extension) { 641 return mConnections.get(extension).mProxy; 642 } 643 getConnection(@xtension int extension)644 public ServiceConnection getConnection(@Extension int extension) { 645 return mConnections.get(extension).mConnection; 646 } 647 getConnectionCount(@xtension int extension)648 public int getConnectionCount(@Extension int extension) { 649 return mConnections.get(extension).mConnectionCount; 650 } 651 areAdvancedExtensionsSupported(@xtension int extension)652 public boolean areAdvancedExtensionsSupported(@Extension int extension) { 653 return mConnections.get(extension).mSupportsAdvancedExtensions; 654 } 655 isSessionInitialized()656 public boolean isSessionInitialized() { 657 return mSessionInitialized; 658 } 659 setProxy(@xtension int extension, ICameraExtensionsProxyService proxy)660 public void setProxy(@Extension int extension, ICameraExtensionsProxyService proxy) { 661 mConnections.get(extension).mProxy = proxy; 662 } 663 setConnection(@xtension int extension, ServiceConnection connection)664 public void setConnection(@Extension int extension, ServiceConnection connection) { 665 mConnections.get(extension).mConnection = connection; 666 } 667 incrementConnectionCount(@xtension int extension)668 public void incrementConnectionCount(@Extension int extension) { 669 mConnections.get(extension).mConnectionCount++; 670 } 671 decrementConnectionCount(@xtension int extension)672 public void decrementConnectionCount(@Extension int extension) { 673 mConnections.get(extension).mConnectionCount--; 674 } 675 resetConnectionCount(@xtension int extension)676 public void resetConnectionCount(@Extension int extension) { 677 mConnections.get(extension).mConnectionCount = 0; 678 } 679 setAdvancedExtensionsSupported(@xtension int extension, boolean advancedExtSupported)680 public void setAdvancedExtensionsSupported(@Extension int extension, 681 boolean advancedExtSupported) { 682 mConnections.get(extension).mSupportsAdvancedExtensions = advancedExtSupported; 683 } 684 setSessionInitialized(boolean initialized)685 public void setSessionInitialized(boolean initialized) { 686 mSessionInitialized = initialized; 687 } 688 689 private class ExtensionConnection { 690 public ICameraExtensionsProxyService mProxy = null; 691 public ServiceConnection mConnection = null; 692 public int mConnectionCount = 0; 693 public boolean mSupportsAdvancedExtensions = false; 694 } 695 } 696 } 697 698 /** 699 * @hide 700 */ registerClient(Context ctx, IBinder token, int extension, String cameraId, Map<String, CameraMetadataNative> characteristicsMapNative)701 public static boolean registerClient(Context ctx, IBinder token, int extension, 702 String cameraId, Map<String, CameraMetadataNative> characteristicsMapNative) { 703 return CameraExtensionManagerGlobal.get().registerClient(ctx, token, extension, cameraId, 704 characteristicsMapNative); 705 } 706 707 /** 708 * @hide 709 */ unregisterClient(Context ctx, IBinder token, int extension)710 public static void unregisterClient(Context ctx, IBinder token, int extension) { 711 CameraExtensionManagerGlobal.get().unregisterClient(ctx, token, extension); 712 } 713 714 /** 715 * @hide 716 */ initializeSession(IInitializeSessionCallback cb, int extension)717 public static void initializeSession(IInitializeSessionCallback cb, int extension) 718 throws RemoteException { 719 CameraExtensionManagerGlobal.get().initializeSession(cb, extension); 720 } 721 722 /** 723 * @hide 724 */ releaseSession(int extension)725 public static void releaseSession(int extension) { 726 CameraExtensionManagerGlobal.get().releaseSession(extension); 727 } 728 729 /** 730 * @hide 731 */ areAdvancedExtensionsSupported(int extension)732 public static boolean areAdvancedExtensionsSupported(int extension) { 733 return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported(extension); 734 } 735 736 /** 737 * @hide 738 */ isExtensionSupported(String cameraId, int extensionType, Map<String, CameraMetadataNative> characteristicsMap)739 public static boolean isExtensionSupported(String cameraId, int extensionType, 740 Map<String, CameraMetadataNative> characteristicsMap) { 741 if (areAdvancedExtensionsSupported(extensionType)) { 742 try { 743 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extensionType); 744 return extender.isExtensionAvailable(cameraId, characteristicsMap); 745 } catch (RemoteException e) { 746 Log.e(TAG, "Failed to query extension availability! Extension service does not" 747 + " respond!"); 748 return false; 749 } 750 } else { 751 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders; 752 try { 753 extenders = initializeExtension(extensionType); 754 } catch (IllegalArgumentException e) { 755 return false; 756 } 757 758 try { 759 return extenders.first.isExtensionAvailable(cameraId, 760 characteristicsMap.get(cameraId)) 761 && extenders.second.isExtensionAvailable(cameraId, 762 characteristicsMap.get(cameraId)); 763 } catch (RemoteException e) { 764 Log.e(TAG, "Failed to query extension availability! Extension service does not" 765 + " respond!"); 766 return false; 767 } 768 } 769 } 770 771 /** 772 * @hide 773 */ initializeAdvancedExtension(@xtension int extensionType)774 public static IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) { 775 IAdvancedExtenderImpl extender; 776 try { 777 extender = CameraExtensionManagerGlobal.get().initializeAdvancedExtension( 778 extensionType); 779 } catch (RemoteException e) { 780 throw new IllegalStateException("Failed to initialize extension: " + extensionType); 781 } 782 783 if (extender == null) { 784 throw new IllegalArgumentException("Unknown extension: " + extensionType); 785 } 786 787 return extender; 788 } 789 790 /** 791 * @hide 792 */ initializeExtension( @xtension int extensionType)793 public static Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> initializeExtension( 794 @Extension int extensionType) { 795 IPreviewExtenderImpl previewExtender; 796 IImageCaptureExtenderImpl imageExtender; 797 try { 798 previewExtender = 799 CameraExtensionManagerGlobal.get().initializePreviewExtension(extensionType); 800 imageExtender = 801 CameraExtensionManagerGlobal.get().initializeImageExtension(extensionType); 802 } catch (RemoteException e) { 803 throw new IllegalStateException("Failed to initialize extension: " + extensionType); 804 } 805 if ((imageExtender == null) || (previewExtender == null)) { 806 throw new IllegalArgumentException("Unknown extension: " + extensionType); 807 } 808 809 return new Pair<>(previewExtender, imageExtender); 810 } 811 isOutputSupportedFor(Class<T> klass)812 private static <T> boolean isOutputSupportedFor(Class<T> klass) { 813 Objects.requireNonNull(klass, "klass must not be null"); 814 815 if ((klass == android.graphics.SurfaceTexture.class) || 816 (klass == android.view.SurfaceView.class)) { 817 return true; 818 } 819 820 return false; 821 } 822 823 /** 824 * Return a list of supported device-specific extensions for a given camera device. 825 * 826 * @return non-modifiable list of available extensions 827 */ getSupportedExtensions()828 public @NonNull List<Integer> getSupportedExtensions() { 829 ArrayList<Integer> ret = new ArrayList<>(); 830 final IBinder token = new Binder(TAG + "#getSupportedExtensions:" + mCameraId); 831 832 IntArray extensionList = new IntArray(EXTENSION_LIST.length); 833 extensionList.addAll(EXTENSION_LIST); 834 if (Flags.concertModeApi()) { 835 extensionList.add(EXTENSION_EYES_FREE_VIDEOGRAPHY); 836 } 837 838 for (int extensionType : extensionList.toArray()) { 839 try { 840 boolean success = registerClient(mContext, token, extensionType, mCameraId, 841 mCharacteristicsMapNative); 842 if (success && isExtensionSupported(mCameraId, extensionType, 843 mCharacteristicsMapNative)) { 844 ret.add(extensionType); 845 } 846 } finally { 847 unregisterClient(mContext, token, extensionType); 848 } 849 } 850 851 return Collections.unmodifiableList(ret); 852 } 853 854 /** 855 * Gets an extension specific camera characteristics field value. 856 * 857 * <p>An extension can have a reduced set of camera capabilities (such as limited zoom ratio 858 * range, available video stabilization modes, etc). This API enables applications to query for 859 * an extension’s specific camera characteristics. Applications are recommended to prioritize 860 * obtaining camera characteristics using this API when using an extension. A {@code null} 861 * result indicates that the extension specific characteristic is not defined or available. 862 * 863 * @param extension The extension type. 864 * @param key The characteristics field to read. 865 * @return The value of that key, or {@code null} if the field is not set. 866 * 867 * @throws IllegalArgumentException if the key is not valid or extension type is not a supported 868 * device-specific extension. 869 */ 870 @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET) get(@xtension int extension, @NonNull CameraCharacteristics.Key<T> key)871 public <T> @Nullable T get(@Extension int extension, 872 @NonNull CameraCharacteristics.Key<T> key) { 873 final IBinder token = new Binder(TAG + "#get:" + mCameraId); 874 boolean success = registerClient(mContext, token, extension, mCameraId, 875 mCharacteristicsMapNative); 876 if (!success) { 877 throw new IllegalArgumentException("Unsupported extensions"); 878 } 879 880 try { 881 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 882 throw new IllegalArgumentException("Unsupported extension"); 883 } 884 885 if (areAdvancedExtensionsSupported(extension) && getKeys(extension).contains(key)) { 886 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 887 extender.init(mCameraId, mCharacteristicsMapNative); 888 CameraMetadataNative metadata = 889 extender.getAvailableCharacteristicsKeyValues(mCameraId); 890 if (metadata == null) { 891 return null; 892 } 893 CameraCharacteristics characteristics = new CameraCharacteristics(metadata); 894 return characteristics.get(key); 895 } 896 } catch (RemoteException e) { 897 Log.e(TAG, "Failed to query the extension for the specified key! Extension " 898 + "service does not respond!"); 899 } finally { 900 unregisterClient(mContext, token, extension); 901 } 902 return null; 903 } 904 905 /** 906 * Returns the {@link CameraCharacteristics} keys that have extension-specific values. 907 * 908 * <p>An application can query the value from the key using 909 * {@link #get(int, CameraCharacteristics.Key)} API. 910 * 911 * @param extension The extension type. 912 * @return An unmodifiable set of keys that are extension specific. 913 * 914 * @throws IllegalArgumentException in case the extension type is not a 915 * supported device-specific extension 916 */ 917 @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET) getKeys(@xtension int extension)918 public @NonNull Set<CameraCharacteristics.Key> getKeys(@Extension int extension) { 919 final IBinder token = 920 new Binder(TAG + "#getKeys:" + mCameraId); 921 boolean success = registerClient(mContext, token, extension, mCameraId, 922 mCharacteristicsMapNative); 923 if (!success) { 924 throw new IllegalArgumentException("Unsupported extensions"); 925 } 926 927 HashSet<CameraCharacteristics.Key> ret = new HashSet<>(); 928 929 try { 930 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 931 throw new IllegalArgumentException("Unsupported extension"); 932 } 933 934 if (areAdvancedExtensionsSupported(extension)) { 935 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 936 extender.init(mCameraId, mCharacteristicsMapNative); 937 CameraMetadataNative metadata = 938 extender.getAvailableCharacteristicsKeyValues(mCameraId); 939 if (metadata == null) { 940 return Collections.emptySet(); 941 } 942 943 int[] keys = metadata.get( 944 CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS); 945 if (keys == null) { 946 throw new AssertionError( 947 "android.request.availableCharacteristicsKeys must be non-null" 948 + " in the characteristics"); 949 } 950 CameraCharacteristics chars = new CameraCharacteristics(metadata); 951 952 Object key = CameraCharacteristics.Key.class; 953 Class<CameraCharacteristics.Key<?>> keyTyped = 954 (Class<CameraCharacteristics.Key<?>>) key; 955 956 ret.addAll(chars.getAvailableKeyList(CameraCharacteristics.class, keyTyped, keys, 957 /*includeSynthetic*/ false)); 958 959 // Add synthetic keys to the available key list if they are part of the supported 960 // synthetic camera characteristic key list 961 for (CameraCharacteristics.Key charKey : 962 SUPPORTED_SYNTHETIC_CAMERA_CHARACTERISTICS) { 963 if (chars.get(charKey) != null) { 964 ret.add(charKey); 965 } 966 } 967 } 968 } catch (RemoteException e) { 969 Log.e(TAG, "Failed to query the extension for all available keys! Extension " 970 + "service does not respond!"); 971 } finally { 972 unregisterClient(mContext, token, extension); 973 } 974 return Collections.unmodifiableSet(ret); 975 } 976 977 /** 978 * Checks for postview support of still capture. 979 * 980 * <p>A postview is a preview version of the still capture that is available before the final 981 * image. For example, it can be used as a temporary placeholder for the requested capture 982 * while the final image is being processed. The supported sizes for a still capture's postview 983 * can be retrieved using 984 * {@link CameraExtensionCharacteristics#getPostviewSupportedSizes(int, Size, int)}.</p> 985 * 986 * <p>Starting with Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, 987 * the formats of the still capture and postview are not required to be equivalent upon capture 988 * request.</p> 989 * 990 * @param extension the extension type 991 * @return {@code true} in case postview is supported, {@code false} otherwise 992 * 993 * @throws IllegalArgumentException in case the extension type is not a 994 * supported device-specific extension 995 */ isPostviewAvailable(@xtension int extension)996 public boolean isPostviewAvailable(@Extension int extension) { 997 final IBinder token = new Binder(TAG + "#isPostviewAvailable:" + mCameraId); 998 boolean success = registerClient(mContext, token, extension, mCameraId, 999 mCharacteristicsMapNative); 1000 if (!success) { 1001 throw new IllegalArgumentException("Unsupported extensions"); 1002 } 1003 1004 try { 1005 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 1006 throw new IllegalArgumentException("Unsupported extension"); 1007 } 1008 1009 if (areAdvancedExtensionsSupported(extension)) { 1010 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 1011 extender.init(mCameraId, mCharacteristicsMapNative); 1012 return extender.isPostviewAvailable(); 1013 } else { 1014 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1015 initializeExtension(extension); 1016 extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); 1017 return extenders.second.isPostviewAvailable(); 1018 } 1019 } catch (RemoteException e) { 1020 Log.e(TAG, "Failed to query the extension for postview availability! Extension " 1021 + "service does not respond!"); 1022 } finally { 1023 unregisterClient(mContext, token, extension); 1024 } 1025 1026 return false; 1027 } 1028 1029 /** 1030 * Get a list of the postview sizes supported for a still capture, using its 1031 * capture size {@code captureSize}, to use as an output for the postview request. 1032 * 1033 * <p>Available postview sizes will always be either equal to or less than the still 1034 * capture size. When choosing the most applicable postview size for a usecase, it should 1035 * be noted that lower resolution postviews will generally be available more quickly 1036 * than larger resolution postviews. For example, when choosing a size for an optimized 1037 * postview that will be displayed as a placeholder while the final image is processed, 1038 * the resolution closest to the preview size may be most suitable.</p> 1039 * 1040 * <p>Note that device-specific extensions are allowed to support only a subset 1041 * of the camera resolutions advertised by 1042 * {@link StreamConfigurationMap#getOutputSizes}.</p> 1043 * 1044 * @param extension the extension type 1045 * @param captureSize size of the still capture for which the postview is requested 1046 * @param format device-specific extension output format of the postview 1047 * @return non-modifiable list of available sizes or an empty list if the format and 1048 * size is not supported. 1049 * @throws IllegalArgumentException in case of unsupported extension or if postview 1050 * feature is not supported by extension. 1051 */ 1052 @NonNull getPostviewSupportedSizes(@xtension int extension, @NonNull Size captureSize, int format)1053 public List<Size> getPostviewSupportedSizes(@Extension int extension, 1054 @NonNull Size captureSize, int format) { 1055 final IBinder token = new Binder(TAG + "#getPostviewSupportedSizes:" + mCameraId); 1056 boolean success = registerClient(mContext, token, extension, mCameraId, 1057 mCharacteristicsMapNative); 1058 if (!success) { 1059 throw new IllegalArgumentException("Unsupported extensions"); 1060 } 1061 1062 try { 1063 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 1064 throw new IllegalArgumentException("Unsupported extension"); 1065 } 1066 1067 android.hardware.camera2.extension.Size sz = 1068 new android.hardware.camera2.extension.Size(); 1069 sz.width = captureSize.getWidth(); 1070 sz.height = captureSize.getHeight(); 1071 1072 StreamConfigurationMap streamMap = mCharacteristicsMap.get(mCameraId).get( 1073 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1074 1075 if (areAdvancedExtensionsSupported(extension)) { 1076 switch(format) { 1077 case ImageFormat.YUV_420_888: 1078 case ImageFormat.JPEG: 1079 case ImageFormat.JPEG_R: 1080 case ImageFormat.YCBCR_P010: 1081 break; 1082 default: 1083 throw new IllegalArgumentException("Unsupported format: " + format); 1084 } 1085 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 1086 extender.init(mCameraId, mCharacteristicsMapNative); 1087 return getSupportedSizes(extender.getSupportedPostviewResolutions(sz), 1088 format); 1089 } else { 1090 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1091 initializeExtension(extension); 1092 extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); 1093 if ((extenders.second.getCaptureProcessor() == null) || 1094 !isPostviewAvailable(extension)) { 1095 // Extensions that don't implement any capture processor 1096 // and have processing occur in the HAL don't currently support the 1097 // postview feature 1098 throw new IllegalArgumentException("Extension does not support " 1099 + "postview feature"); 1100 } 1101 1102 if (format == ImageFormat.YUV_420_888) { 1103 return getSupportedSizes( 1104 extenders.second.getSupportedPostviewResolutions(sz), format); 1105 } else if (format == ImageFormat.JPEG) { 1106 // The framework will perform the additional encoding pass on the 1107 // processed YUV_420 buffers. 1108 return getSupportedSizes( 1109 extenders.second.getSupportedPostviewResolutions(sz), format); 1110 } else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) { 1111 // Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the basic 1112 // extension case 1113 return new ArrayList<>(); 1114 } else { 1115 throw new IllegalArgumentException("Unsupported format: " + format); 1116 } 1117 } 1118 } catch (RemoteException e) { 1119 Log.e(TAG, "Failed to query the extension postview supported sizes! Extension " 1120 + "service does not respond!"); 1121 return Collections.emptyList(); 1122 } finally { 1123 unregisterClient(mContext, token, extension); 1124 } 1125 } 1126 1127 /** 1128 * Get a list of sizes compatible with {@code klass} to use as an output for the 1129 * repeating request 1130 * {@link CameraExtensionSession#setRepeatingRequest}. 1131 * 1132 * <p>Note that device-specific extensions are allowed to support only a subset 1133 * of the camera output surfaces and resolutions. 1134 * The {@link android.graphics.SurfaceTexture} class is guaranteed at least one size for 1135 * backward compatible cameras whereas other output classes are not guaranteed to be supported. 1136 * </p> 1137 * 1138 * <p>Starting with Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 1139 * {@link android.view.SurfaceView} classes are also guaranteed to be supported and include 1140 * the same resolutions as {@link android.graphics.SurfaceTexture}. 1141 * Clients must set the desired SurfaceView resolution by calling 1142 * {@link android.view.SurfaceHolder#setFixedSize}.</p> 1143 * 1144 * @param extension the extension type 1145 * @param klass a non-{@code null} {@link Class} object reference 1146 * @return non-modifiable list of available sizes or an empty list if the Surface output is not 1147 * supported 1148 * @throws NullPointerException if {@code klass} was {@code null} 1149 * @throws IllegalArgumentException in case of unsupported extension. 1150 */ 1151 @NonNull getExtensionSupportedSizes(@xtension int extension, @NonNull Class<T> klass)1152 public <T> List<Size> getExtensionSupportedSizes(@Extension int extension, 1153 @NonNull Class<T> klass) { 1154 if (!isOutputSupportedFor(klass)) { 1155 return new ArrayList<>(); 1156 } 1157 // TODO: Revisit this code once the Extension preview processor output format 1158 // ambiguity is resolved in b/169799538. 1159 1160 final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId); 1161 boolean success = registerClient(mContext, token, extension, mCameraId, 1162 mCharacteristicsMapNative); 1163 if (!success) { 1164 throw new IllegalArgumentException("Unsupported extensions"); 1165 } 1166 1167 try { 1168 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 1169 throw new IllegalArgumentException("Unsupported extension"); 1170 } 1171 1172 StreamConfigurationMap streamMap = mCharacteristicsMap.get(mCameraId).get( 1173 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1174 if (areAdvancedExtensionsSupported(extension)) { 1175 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 1176 extender.init(mCameraId, mCharacteristicsMapNative); 1177 return generateSupportedSizes( 1178 extender.getSupportedPreviewOutputResolutions(mCameraId), 1179 ImageFormat.PRIVATE, streamMap); 1180 } else { 1181 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1182 initializeExtension(extension); 1183 extenders.first.init(mCameraId, 1184 mCharacteristicsMapNative.get(mCameraId)); 1185 return generateSupportedSizes(extenders.first.getSupportedResolutions(), 1186 ImageFormat.PRIVATE, streamMap); 1187 } 1188 } catch (RemoteException e) { 1189 Log.e(TAG, "Failed to query the extension supported sizes! Extension service does" 1190 + " not respond!"); 1191 return new ArrayList<>(); 1192 } finally { 1193 unregisterClient(mContext, token, extension); 1194 } 1195 } 1196 1197 /** 1198 * Check whether a given extension is available and return the 1199 * supported output surface resolutions that can be used for high-quality capture 1200 * requests via {@link CameraExtensionSession#capture}. 1201 * 1202 * <p>Note that device-specific extensions are allowed to support only a subset 1203 * of the camera resolutions advertised by 1204 * {@link StreamConfigurationMap#getOutputSizes}.</p> 1205 * 1206 * <p>Device-specific extensions currently support at most three 1207 * multi-frame capture surface formats. ImageFormat.JPEG will be supported by all 1208 * extensions while ImageFormat.YUV_420_888, ImageFormat.JPEG_R, or ImageFormat.YCBCR_P010 1209 * may or may not be supported.</p> 1210 * 1211 * @param extension the extension type 1212 * @param format device-specific extension output format 1213 * @return non-modifiable list of available sizes or an empty list if the format is not 1214 * supported. 1215 * @throws IllegalArgumentException in case of format different from ImageFormat.JPEG, 1216 * ImageFormat.YUV_420_888, ImageFormat.JPEG_R, 1217 * ImageFormat.YCBCR_P010; or unsupported extension. 1218 */ 1219 public @NonNull getExtensionSupportedSizes(@xtension int extension, int format)1220 List<Size> getExtensionSupportedSizes(@Extension int extension, int format) { 1221 try { 1222 final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId); 1223 boolean success = registerClient(mContext, token, extension, mCameraId, 1224 mCharacteristicsMapNative); 1225 if (!success) { 1226 throw new IllegalArgumentException("Unsupported extensions"); 1227 } 1228 1229 try { 1230 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 1231 throw new IllegalArgumentException("Unsupported extension"); 1232 } 1233 1234 StreamConfigurationMap streamMap = mCharacteristicsMap.get(mCameraId).get( 1235 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1236 if (areAdvancedExtensionsSupported(extension)) { 1237 switch(format) { 1238 case ImageFormat.YUV_420_888: 1239 case ImageFormat.JPEG: 1240 case ImageFormat.JPEG_R: 1241 case ImageFormat.YCBCR_P010: 1242 break; 1243 default: 1244 throw new IllegalArgumentException("Unsupported format: " + format); 1245 } 1246 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 1247 extender.init(mCameraId, mCharacteristicsMapNative); 1248 return generateSupportedSizes(extender.getSupportedCaptureOutputResolutions( 1249 mCameraId), format, streamMap); 1250 } else { 1251 if (format == ImageFormat.YUV_420_888) { 1252 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1253 initializeExtension(extension); 1254 extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); 1255 if (extenders.second.getCaptureProcessor() == null) { 1256 // Extensions that don't implement any capture processor are limited to 1257 // JPEG only! 1258 return new ArrayList<>(); 1259 } 1260 return generateSupportedSizes(extenders.second.getSupportedResolutions(), 1261 format, streamMap); 1262 } else if (format == ImageFormat.JPEG) { 1263 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1264 initializeExtension(extension); 1265 extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); 1266 if (extenders.second.getCaptureProcessor() != null) { 1267 // The framework will perform the additional encoding pass on the 1268 // processed YUV_420 buffers. 1269 return generateJpegSupportedSizes( 1270 extenders.second.getSupportedResolutions(), streamMap); 1271 } else { 1272 return generateSupportedSizes(null, format, streamMap); 1273 } 1274 } else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) { 1275 // Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the 1276 // basic extension case 1277 return new ArrayList<>(); 1278 } else { 1279 throw new IllegalArgumentException("Unsupported format: " + format); 1280 } 1281 } 1282 } finally { 1283 unregisterClient(mContext, token, extension); 1284 } 1285 } catch (RemoteException e) { 1286 Log.e(TAG, "Failed to query the extension supported sizes! Extension service does" 1287 + " not respond!"); 1288 return new ArrayList<>(); 1289 } 1290 } 1291 1292 /** 1293 * Returns the estimated capture latency range in milliseconds for the 1294 * target capture resolution during the calls to {@link CameraExtensionSession#capture}. This 1295 * includes the time spent processing the multi-frame capture request along with any additional 1296 * time for encoding of the processed buffer if necessary. 1297 * 1298 * @param extension the extension type 1299 * @param captureOutputSize size of the capture output surface. If it is not in the supported 1300 * output sizes, maximum capture output size is used for the estimation 1301 * @param format device-specific extension output format 1302 * @return the range of estimated minimal and maximal capture latency in milliseconds 1303 * or null if no capture latency info can be provided 1304 * @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG}, 1305 * {@link ImageFormat#YUV_420_888}, {@link ImageFormat#JPEG_R} 1306 * {@link ImageFormat#YCBCR_P010}; 1307 * or unsupported extension. 1308 */ getEstimatedCaptureLatencyRangeMillis(@xtension int extension, @NonNull Size captureOutputSize, @ImageFormat.Format int format)1309 public @Nullable Range<Long> getEstimatedCaptureLatencyRangeMillis(@Extension int extension, 1310 @NonNull Size captureOutputSize, @ImageFormat.Format int format) { 1311 switch (format) { 1312 case ImageFormat.YUV_420_888: 1313 case ImageFormat.JPEG: 1314 case ImageFormat.JPEG_R: 1315 case ImageFormat.YCBCR_P010: 1316 //No op 1317 break; 1318 default: 1319 throw new IllegalArgumentException("Unsupported format: " + format); 1320 } 1321 1322 final IBinder token = new Binder(TAG + "#getEstimatedCaptureLatencyRangeMillis:" + mCameraId); 1323 boolean success = registerClient(mContext, token, extension, mCameraId, 1324 mCharacteristicsMapNative); 1325 if (!success) { 1326 throw new IllegalArgumentException("Unsupported extensions"); 1327 } 1328 1329 try { 1330 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 1331 throw new IllegalArgumentException("Unsupported extension"); 1332 } 1333 1334 android.hardware.camera2.extension.Size sz = 1335 new android.hardware.camera2.extension.Size(); 1336 sz.width = captureOutputSize.getWidth(); 1337 sz.height = captureOutputSize.getHeight(); 1338 if (areAdvancedExtensionsSupported(extension)) { 1339 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 1340 extender.init(mCameraId, mCharacteristicsMapNative); 1341 LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId, 1342 sz, format); 1343 if (latencyRange != null) { 1344 return new Range(latencyRange.min, latencyRange.max); 1345 } 1346 } else { 1347 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1348 initializeExtension(extension); 1349 extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); 1350 if ((format == ImageFormat.YUV_420_888) && 1351 (extenders.second.getCaptureProcessor() == null) ){ 1352 // Extensions that don't implement any capture processor are limited to 1353 // JPEG only! 1354 return null; 1355 } 1356 if ((format == ImageFormat.JPEG) && 1357 (extenders.second.getCaptureProcessor() != null)) { 1358 // The framework will perform the additional encoding pass on the 1359 // processed YUV_420 buffers. Latency in this case is very device 1360 // specific and cannot be estimated accurately enough. 1361 return null; 1362 } 1363 if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) { 1364 // JpegR/UltraHDR + YCBCR_P010 is not supported for basic extensions 1365 return null; 1366 } 1367 1368 LatencyRange latencyRange = extenders.second.getEstimatedCaptureLatencyRange(sz); 1369 if (latencyRange != null) { 1370 return new Range(latencyRange.min, latencyRange.max); 1371 } 1372 } 1373 } catch (RemoteException e) { 1374 Log.e(TAG, "Failed to query the extension capture latency! Extension service does" 1375 + " not respond!"); 1376 } finally { 1377 unregisterClient(mContext, token, extension); 1378 } 1379 1380 return null; 1381 } 1382 1383 /** 1384 * Retrieve support for capture progress callbacks via 1385 * {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureProcessProgressed}. 1386 * 1387 * @param extension the extension type 1388 * @return {@code true} in case progress callbacks are supported, {@code false} otherwise 1389 * 1390 * @throws IllegalArgumentException in case of an unsupported extension. 1391 */ isCaptureProcessProgressAvailable(@xtension int extension)1392 public boolean isCaptureProcessProgressAvailable(@Extension int extension) { 1393 final IBinder token = new Binder(TAG + "#isCaptureProcessProgressAvailable:" + mCameraId); 1394 boolean success = registerClient(mContext, token, extension, mCameraId, 1395 mCharacteristicsMapNative); 1396 if (!success) { 1397 throw new IllegalArgumentException("Unsupported extensions"); 1398 } 1399 1400 try { 1401 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 1402 throw new IllegalArgumentException("Unsupported extension"); 1403 } 1404 1405 if (areAdvancedExtensionsSupported(extension)) { 1406 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 1407 extender.init(mCameraId, mCharacteristicsMapNative); 1408 return extender.isCaptureProcessProgressAvailable(); 1409 } else { 1410 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1411 initializeExtension(extension); 1412 extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); 1413 return extenders.second.isCaptureProcessProgressAvailable(); 1414 } 1415 } catch (RemoteException e) { 1416 Log.e(TAG, "Failed to query the extension progress callbacks! Extension service does" 1417 + " not respond!"); 1418 } finally { 1419 unregisterClient(mContext, token, extension); 1420 } 1421 1422 return false; 1423 } 1424 1425 /** 1426 * Returns the set of keys supported by a {@link CaptureRequest} submitted in a 1427 * {@link CameraExtensionSession} with a given extension type. 1428 * 1429 * <p>The set returned is not modifiable, so any attempts to modify it will throw 1430 * a {@code UnsupportedOperationException}.</p> 1431 * 1432 * <p>Devices launching on Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} 1433 * or newer versions are required to support {@link CaptureRequest#CONTROL_AF_MODE}, 1434 * {@link CaptureRequest#CONTROL_AF_REGIONS}, {@link CaptureRequest#CONTROL_AF_TRIGGER}, 1435 * {@link CaptureRequest#CONTROL_ZOOM_RATIO} for 1436 * {@link CameraExtensionCharacteristics#EXTENSION_NIGHT}.</p> 1437 * 1438 * @param extension the extension type 1439 * 1440 * @return non-modifiable set of capture keys supported by camera extension session initialized 1441 * with the given extension type. 1442 * @throws IllegalArgumentException in case of unsupported extension. 1443 */ 1444 @NonNull getAvailableCaptureRequestKeys(@xtension int extension)1445 public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) { 1446 final IBinder token = new Binder(TAG + "#getAvailableCaptureRequestKeys:" + mCameraId); 1447 boolean success = registerClient(mContext, token, extension, mCameraId, 1448 mCharacteristicsMapNative); 1449 if (!success) { 1450 throw new IllegalArgumentException("Unsupported extensions"); 1451 } 1452 1453 HashSet<CaptureRequest.Key> ret = new HashSet<>(); 1454 1455 try { 1456 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 1457 throw new IllegalArgumentException("Unsupported extension"); 1458 } 1459 1460 CameraMetadataNative captureRequestMeta = null; 1461 if (areAdvancedExtensionsSupported(extension)) { 1462 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 1463 extender.init(mCameraId, mCharacteristicsMapNative); 1464 captureRequestMeta = extender.getAvailableCaptureRequestKeys(mCameraId); 1465 } else { 1466 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1467 initializeExtension(extension); 1468 extenders.second.onInit(token, mCameraId, 1469 mCharacteristicsMapNative.get(mCameraId)); 1470 extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); 1471 captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys(); 1472 extenders.second.onDeInit(token); 1473 } 1474 1475 if (captureRequestMeta != null) { 1476 int[] requestKeys = captureRequestMeta.get( 1477 CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS); 1478 if (requestKeys == null) { 1479 throw new AssertionError( 1480 "android.request.availableRequestKeys must be non-null" 1481 + " in the characteristics"); 1482 } 1483 CameraCharacteristics requestChars = new CameraCharacteristics( 1484 captureRequestMeta); 1485 1486 Object crKey = CaptureRequest.Key.class; 1487 Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>) crKey; 1488 1489 ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped, 1490 requestKeys, /*includeSynthetic*/ true)); 1491 } 1492 1493 // Jpeg quality and orientation must always be supported 1494 if (!ret.contains(CaptureRequest.JPEG_QUALITY)) { 1495 ret.add(CaptureRequest.JPEG_QUALITY); 1496 } 1497 if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) { 1498 ret.add(CaptureRequest.JPEG_ORIENTATION); 1499 } 1500 } catch (RemoteException e) { 1501 throw new IllegalStateException("Failed to query the available capture request keys!"); 1502 } finally { 1503 unregisterClient(mContext, token, extension); 1504 } 1505 1506 return Collections.unmodifiableSet(ret); 1507 } 1508 1509 /** 1510 * Returns the set of keys supported by a {@link CaptureResult} passed as an argument to 1511 * {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}. 1512 * 1513 * <p>The set returned is not modifiable, so any attempts to modify it will throw 1514 * a {@code UnsupportedOperationException}.</p> 1515 * 1516 * <p>In case the set is empty, then the extension is not able to support any capture results 1517 * and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable} 1518 * callback will not be fired.</p> 1519 * 1520 * <p>Devices launching on Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} 1521 * or newer versions are required to support {@link CaptureResult#CONTROL_AF_MODE}, 1522 * {@link CaptureResult#CONTROL_AF_REGIONS}, {@link CaptureResult#CONTROL_AF_TRIGGER}, 1523 * {@link CaptureResult#CONTROL_AF_STATE}, {@link CaptureResult#CONTROL_ZOOM_RATIO} for 1524 * {@link CameraExtensionCharacteristics#EXTENSION_NIGHT}.</p> 1525 * 1526 * @param extension the extension type 1527 * 1528 * @return non-modifiable set of capture result keys supported by camera extension session 1529 * initialized with the given extension type. 1530 * @throws IllegalArgumentException in case of unsupported extension. 1531 */ 1532 @NonNull getAvailableCaptureResultKeys(@xtension int extension)1533 public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) { 1534 final IBinder token = new Binder(TAG + "#getAvailableCaptureResultKeys:" + mCameraId); 1535 boolean success = registerClient(mContext, token, extension, mCameraId, 1536 mCharacteristicsMapNative); 1537 if (!success) { 1538 throw new IllegalArgumentException("Unsupported extensions"); 1539 } 1540 1541 HashSet<CaptureResult.Key> ret = new HashSet<>(); 1542 try { 1543 if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) { 1544 throw new IllegalArgumentException("Unsupported extension"); 1545 } 1546 1547 CameraMetadataNative captureResultMeta = null; 1548 if (areAdvancedExtensionsSupported(extension)) { 1549 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 1550 extender.init(mCameraId, mCharacteristicsMapNative); 1551 captureResultMeta = extender.getAvailableCaptureResultKeys(mCameraId); 1552 } else { 1553 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 1554 initializeExtension(extension); 1555 extenders.second.onInit(token, mCameraId, 1556 mCharacteristicsMapNative.get(mCameraId)); 1557 extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId)); 1558 captureResultMeta = extenders.second.getAvailableCaptureResultKeys(); 1559 extenders.second.onDeInit(token); 1560 } 1561 1562 if (captureResultMeta != null) { 1563 int[] resultKeys = captureResultMeta.get( 1564 CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS); 1565 if (resultKeys == null) { 1566 throw new AssertionError("android.request.availableResultKeys must be non-null " 1567 + "in the characteristics"); 1568 } 1569 CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta); 1570 Object crKey = CaptureResult.Key.class; 1571 Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>) crKey; 1572 1573 ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped, 1574 resultKeys, /*includeSynthetic*/ true)); 1575 1576 // Jpeg quality, orientation and sensor timestamp must always be supported 1577 if (!ret.contains(CaptureResult.JPEG_QUALITY)) { 1578 ret.add(CaptureResult.JPEG_QUALITY); 1579 } 1580 if (!ret.contains(CaptureResult.JPEG_ORIENTATION)) { 1581 ret.add(CaptureResult.JPEG_ORIENTATION); 1582 } 1583 if (!ret.contains(CaptureResult.SENSOR_TIMESTAMP)) { 1584 ret.add(CaptureResult.SENSOR_TIMESTAMP); 1585 } 1586 } 1587 } catch (RemoteException e) { 1588 throw new IllegalStateException("Failed to query the available capture result keys!"); 1589 } finally { 1590 unregisterClient(mContext, token, extension); 1591 } 1592 1593 return Collections.unmodifiableSet(ret); 1594 } 1595 1596 1597 /** 1598 * <p>Minimum and maximum padding zoom factors supported by this camera device for 1599 * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for 1600 * the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } 1601 * extension.</p> 1602 * <p>The minimum and maximum padding zoom factors supported by the device for 1603 * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the 1604 * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } 1605 * extension feature. This extension specific camera characteristic can be queried using 1606 * {@link android.hardware.camera2.CameraExtensionCharacteristics#get}.</p> 1607 * <p><b>Units</b>: A pair of padding zoom factors in floating-points: 1608 * (minPaddingZoomFactor, maxPaddingZoomFactor)</p> 1609 * <p><b>Range of valid values:</b><br></p> 1610 * <p>1.0 < minPaddingZoomFactor <= maxPaddingZoomFactor</p> 1611 * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> 1612 */ 1613 @PublicKey 1614 @NonNull 1615 @ExtensionKey 1616 @FlaggedApi(Flags.FLAG_CONCERT_MODE_API) 1617 public static final Key<android.util.Range<Float>> EFV_PADDING_ZOOM_FACTOR_RANGE = 1618 CameraCharacteristics.EFV_PADDING_ZOOM_FACTOR_RANGE; 1619 } 1620