• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.graphics.ImageFormat;
26 import android.hardware.camera2.extension.IAdvancedExtenderImpl;
27 import android.hardware.camera2.extension.ICameraExtensionsProxyService;
28 import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
29 import android.hardware.camera2.extension.IInitializeSessionCallback;
30 import android.hardware.camera2.extension.IPreviewExtenderImpl;
31 import android.hardware.camera2.extension.LatencyRange;
32 import android.hardware.camera2.extension.SizeList;
33 import android.hardware.camera2.impl.CameraMetadataNative;
34 import android.hardware.camera2.params.ExtensionSessionConfiguration;
35 import android.hardware.camera2.params.StreamConfigurationMap;
36 import android.os.ConditionVariable;
37 import android.os.IBinder;
38 import android.os.RemoteException;
39 import android.os.SystemProperties;
40 import android.util.Log;
41 import android.util.Pair;
42 import android.util.Range;
43 import android.util.Size;
44 
45 import java.lang.annotation.Retention;
46 import java.lang.annotation.RetentionPolicy;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.HashSet;
51 import java.util.List;
52 import java.util.Objects;
53 import java.util.Set;
54 import java.util.concurrent.Future;
55 import java.util.concurrent.TimeUnit;
56 import java.util.concurrent.TimeoutException;
57 
58 /**
59  * <p>Allows clients to query availability and supported resolutions of camera extensions.</p>
60  *
61  * <p>Camera extensions give camera clients access to device-specific algorithms and sequences that
62  * can improve the overall image quality of snapshots in various cases such as low light, selfies,
63  * portraits, and scenes that can benefit from enhanced dynamic range. Often such sophisticated
64  * processing sequences will rely on multiple camera frames as input and will produce a single
65  * output.</p>
66  *
67  * <p>Camera extensions are not guaranteed to be present on all devices so camera clients must
68  * query for their availability via {@link CameraExtensionCharacteristics#getSupportedExtensions()}.
69  * </p>
70  *
71  * <p>In order to use any available camera extension, camera clients must create a corresponding
72  * {@link CameraExtensionSession} via
73  * {@link CameraDevice#createExtensionSession(ExtensionSessionConfiguration)}</p>
74  *
75  * <p>Camera clients must be aware that device-specific camera extensions may support only a
76  * subset of the available camera resolutions and must first query
77  * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)} for supported
78  * single high-quality request output sizes and
79  * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, Class)} for supported
80  * repeating request output sizes.</p>
81  *
82  * <p>The extension characteristics for a given device are expected to remain static under
83  * normal operating conditions.</p>
84  *
85  * @see CameraManager#getCameraExtensionCharacteristics(String)
86  */
87 public final class CameraExtensionCharacteristics {
88     private static final String TAG = "CameraExtensionCharacteristics";
89 
90     /**
91      * Device-specific extension implementation for automatic selection of particular extension
92      * such as HDR or NIGHT depending on the current lighting and environment conditions.
93      */
94     public static final int EXTENSION_AUTOMATIC = 0;
95 
96     /**
97      * Device-specific extension implementation which tends to smooth the skin and apply other
98      * cosmetic effects to people's faces.
99      */
100     public static final int EXTENSION_FACE_RETOUCH = 1;
101 
102     /**
103      * Device-specific extension implementation which tends to smooth the skin and apply other
104      * cosmetic effects to people's faces.
105      *
106      * @deprecated Use {@link #EXTENSION_FACE_RETOUCH} instead.
107      */
108     public @Deprecated static final int EXTENSION_BEAUTY = EXTENSION_FACE_RETOUCH;
109 
110     /**
111      * Device-specific extension implementation which can blur certain regions of the final image
112      * thereby "enhancing" focus for all remaining non-blurred parts.
113      */
114     public static final int EXTENSION_BOKEH = 2;
115 
116     /**
117      * Device-specific extension implementation for enhancing the dynamic range of the
118      * final image.
119      */
120     public static final int EXTENSION_HDR = 3;
121 
122     /**
123      * Device-specific extension implementation that aims to suppress noise and improve the
124      * overall image quality under low light conditions.
125      */
126     public static final int EXTENSION_NIGHT = 4;
127 
128     /**
129      * @hide
130      */
131     @Retention(RetentionPolicy.SOURCE)
132     @IntDef(flag = true, value = {EXTENSION_AUTOMATIC,
133                 EXTENSION_FACE_RETOUCH,
134                 EXTENSION_BOKEH,
135                 EXTENSION_HDR,
136                 EXTENSION_NIGHT})
137     public @interface Extension {
138     }
139 
140     /**
141      * Default camera output in case additional processing from CameraX extensions is not needed
142      *
143      * @hide
144      */
145     public static final int NON_PROCESSING_INPUT_FORMAT = ImageFormat.PRIVATE;
146 
147     /**
148      * CameraX extensions require YUV_420_888 as default input for processing at the moment
149      *
150      * @hide
151      */
152     public static final int PROCESSING_INPUT_FORMAT = ImageFormat.YUV_420_888;
153 
154     private static final @Extension
155     int[] EXTENSION_LIST = new int[]{
156             EXTENSION_AUTOMATIC,
157             EXTENSION_FACE_RETOUCH,
158             EXTENSION_BOKEH,
159             EXTENSION_HDR,
160             EXTENSION_NIGHT};
161 
162     private final Context mContext;
163     private final String mCameraId;
164     private final CameraCharacteristics mChars;
165 
166     /**
167      * @hide
168      */
CameraExtensionCharacteristics(Context context, String cameraId, CameraCharacteristics chars)169     public CameraExtensionCharacteristics(Context context, String cameraId,
170             CameraCharacteristics chars) {
171         mContext = context;
172         mCameraId = cameraId;
173         mChars = chars;
174     }
175 
getSupportedSizes(List<SizeList> sizesList, Integer format)176     private static ArrayList<Size> getSupportedSizes(List<SizeList> sizesList,
177             Integer format) {
178         ArrayList<Size> ret = new ArrayList<>();
179         if ((sizesList != null) && (!sizesList.isEmpty())) {
180             for (SizeList entry : sizesList) {
181                 if ((entry.format == format) && !entry.sizes.isEmpty()) {
182                     for (android.hardware.camera2.extension.Size sz : entry.sizes) {
183                         ret.add(new Size(sz.width, sz.height));
184                     }
185                     return ret;
186                 }
187             }
188         }
189 
190         return ret;
191     }
192 
generateSupportedSizes(List<SizeList> sizesList, Integer format, StreamConfigurationMap streamMap)193     private static List<Size> generateSupportedSizes(List<SizeList> sizesList,
194                                                      Integer format,
195                                                      StreamConfigurationMap streamMap) {
196         // Per API contract it is assumed that the extension is able to support all
197         // camera advertised sizes for a given format in case it doesn't return
198         // a valid non-empty size list.
199         ArrayList<Size> ret = getSupportedSizes(sizesList, format);
200         Size[] supportedSizes = streamMap.getOutputSizes(format);
201         if ((ret.isEmpty()) && (supportedSizes != null)) {
202             ret.addAll(Arrays.asList(supportedSizes));
203         }
204         return ret;
205     }
206 
generateJpegSupportedSizes(List<SizeList> sizesList, StreamConfigurationMap streamMap)207     private static List<Size> generateJpegSupportedSizes(List<SizeList> sizesList,
208             StreamConfigurationMap streamMap) {
209         ArrayList<Size> extensionSizes = getSupportedSizes(sizesList, ImageFormat.YUV_420_888);
210         HashSet<Size> supportedSizes = extensionSizes.isEmpty() ? new HashSet<>(Arrays.asList(
211                 streamMap.getOutputSizes(ImageFormat.YUV_420_888))) : new HashSet<>(extensionSizes);
212         HashSet<Size> supportedJpegSizes = new HashSet<>(Arrays.asList(streamMap.getOutputSizes(
213                 ImageFormat.JPEG)));
214         supportedSizes.retainAll(supportedJpegSizes);
215 
216         return new ArrayList<>(supportedSizes);
217     }
218 
219     /**
220      * A per-process global camera extension manager instance, to track and
221      * initialize/release extensions depending on client activity.
222      */
223     private static final class CameraExtensionManagerGlobal {
224         private static final String TAG = "CameraExtensionManagerGlobal";
225         private static final String PROXY_PACKAGE_NAME = "com.android.cameraextensions";
226         private static final String PROXY_SERVICE_NAME =
227                 "com.android.cameraextensions.CameraExtensionsProxyService";
228 
229         // Singleton instance
230         private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
231                 new CameraExtensionManagerGlobal();
232         private final Object mLock = new Object();
233         private final int PROXY_SERVICE_DELAY_MS = 1000;
234         private InitializerFuture mInitFuture = null;
235         private ServiceConnection mConnection = null;
236         private ICameraExtensionsProxyService mProxy = null;
237         private boolean mSupportsAdvancedExtensions = false;
238 
239         // Singleton, don't allow construction
CameraExtensionManagerGlobal()240         private CameraExtensionManagerGlobal() {}
241 
get()242         public static CameraExtensionManagerGlobal get() {
243             return GLOBAL_CAMERA_MANAGER;
244         }
245 
connectToProxyLocked(Context ctx)246         private void connectToProxyLocked(Context ctx) {
247             if (mConnection == null) {
248                 Intent intent = new Intent();
249                 intent.setClassName(PROXY_PACKAGE_NAME, PROXY_SERVICE_NAME);
250                 String vendorProxyPackage = SystemProperties.get(
251                     "ro.vendor.camera.extensions.package");
252                 String vendorProxyService = SystemProperties.get(
253                     "ro.vendor.camera.extensions.service");
254                 if (!vendorProxyPackage.isEmpty() && !vendorProxyService.isEmpty()) {
255                   Log.v(TAG,
256                       "Choosing the vendor camera extensions proxy package: "
257                       + vendorProxyPackage);
258                   Log.v(TAG,
259                       "Choosing the vendor camera extensions proxy service: "
260                       + vendorProxyService);
261                   intent.setClassName(vendorProxyPackage, vendorProxyService);
262                 }
263                 mInitFuture = new InitializerFuture();
264                 mConnection = new ServiceConnection() {
265                     @Override
266                     public void onServiceDisconnected(ComponentName component) {
267                         mInitFuture.setStatus(false);
268                         mConnection = null;
269                         mProxy = null;
270                     }
271 
272                     @Override
273                     public void onServiceConnected(ComponentName component, IBinder binder) {
274                         mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder);
275                         if (mProxy == null) {
276                             throw new IllegalStateException("Camera Proxy service is null");
277                         }
278                         try {
279                             mSupportsAdvancedExtensions = mProxy.advancedExtensionsSupported();
280                         } catch (RemoteException e) {
281                             Log.e(TAG, "Remote IPC failed!");
282                         }
283                         mInitFuture.setStatus(true);
284                     }
285                 };
286                 ctx.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT |
287                         Context.BIND_ABOVE_CLIENT | Context.BIND_NOT_VISIBLE,
288                         android.os.AsyncTask.THREAD_POOL_EXECUTOR, mConnection);
289 
290                 try {
291                     mInitFuture.get(PROXY_SERVICE_DELAY_MS, TimeUnit.MILLISECONDS);
292                 } catch (TimeoutException e) {
293                     Log.e(TAG, "Timed out while initializing proxy service!");
294                 }
295             }
296         }
297 
298         private static class InitializerFuture implements Future<Boolean> {
299             private volatile Boolean mStatus;
300             ConditionVariable mCondVar = new ConditionVariable(/*opened*/false);
301 
setStatus(boolean status)302             public void setStatus(boolean status) {
303                 mStatus = status;
304                 mCondVar.open();
305             }
306 
307             @Override
cancel(boolean mayInterruptIfRunning)308             public boolean cancel(boolean mayInterruptIfRunning) {
309                 return false; // don't allow canceling this task
310             }
311 
312             @Override
isCancelled()313             public boolean isCancelled() {
314                 return false; // can never cancel this task
315             }
316 
317             @Override
isDone()318             public boolean isDone() {
319                 return mStatus != null;
320             }
321 
322             @Override
get()323             public Boolean get() {
324                 mCondVar.block();
325                 return mStatus;
326             }
327 
328             @Override
get(long timeout, TimeUnit unit)329             public Boolean get(long timeout, TimeUnit unit) throws TimeoutException {
330                 long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS);
331                 if (!mCondVar.block(timeoutMs)) {
332                     throw new TimeoutException(
333                             "Failed to receive status after " + timeout + " " + unit);
334                 }
335 
336                 if (mStatus == null) {
337                     throw new AssertionError();
338                 }
339                 return mStatus;
340             }
341         }
342 
registerClient(Context ctx)343         public long registerClient(Context ctx) {
344             synchronized (mLock) {
345                 connectToProxyLocked(ctx);
346                 if (mProxy != null) {
347                     try {
348                         return mProxy.registerClient();
349                     } catch (RemoteException e) {
350                         Log.e(TAG, "Failed to initialize extension! Extension service does "
351                                 + " not respond!");
352                         return -1;
353                     }
354                 } else {
355                     return -1;
356                 }
357             }
358         }
359 
unregisterClient(long clientId)360         public void unregisterClient(long clientId) {
361             synchronized (mLock) {
362                 if (mProxy != null) {
363                     try {
364                         mProxy.unregisterClient(clientId);
365                     } catch (RemoteException e) {
366                         Log.e(TAG, "Failed to de-initialize extension! Extension service does"
367                                 + " not respond!");
368                     }
369                 }
370             }
371         }
372 
initializeSession(IInitializeSessionCallback cb)373         public void initializeSession(IInitializeSessionCallback cb) throws RemoteException {
374             synchronized (mLock) {
375                 if (mProxy != null) {
376                     mProxy.initializeSession(cb);
377                 }
378             }
379         }
380 
releaseSession()381         public void releaseSession() {
382             synchronized (mLock) {
383                 if (mProxy != null) {
384                     try {
385                         mProxy.releaseSession();
386                     } catch (RemoteException e) {
387                         Log.e(TAG, "Failed to release session! Extension service does"
388                                 + " not respond!");
389                     }
390                 }
391             }
392         }
393 
areAdvancedExtensionsSupported()394         public boolean areAdvancedExtensionsSupported() {
395             return mSupportsAdvancedExtensions;
396         }
397 
initializePreviewExtension(int extensionType)398         public IPreviewExtenderImpl initializePreviewExtension(int extensionType)
399                 throws RemoteException {
400             synchronized (mLock) {
401                 if (mProxy != null) {
402                     return mProxy.initializePreviewExtension(extensionType);
403                 } else {
404                     return null;
405                 }
406             }
407         }
408 
initializeImageExtension(int extensionType)409         public IImageCaptureExtenderImpl initializeImageExtension(int extensionType)
410                 throws RemoteException {
411             synchronized (mLock) {
412                 if (mProxy != null) {
413                     return mProxy.initializeImageExtension(extensionType);
414                 } else {
415                     return null;
416                 }
417             }
418         }
419 
initializeAdvancedExtension(int extensionType)420         public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType)
421                 throws RemoteException {
422             synchronized (mLock) {
423                 if (mProxy != null) {
424                     return mProxy.initializeAdvancedExtension(extensionType);
425                 } else {
426                     return null;
427                 }
428             }
429         }
430     }
431 
432     /**
433      * @hide
434      */
registerClient(Context ctx)435     public static long registerClient(Context ctx) {
436         return CameraExtensionManagerGlobal.get().registerClient(ctx);
437     }
438 
439     /**
440      * @hide
441      */
unregisterClient(long clientId)442     public static void unregisterClient(long clientId) {
443         CameraExtensionManagerGlobal.get().unregisterClient(clientId);
444     }
445 
446     /**
447      * @hide
448      */
initializeSession(IInitializeSessionCallback cb)449     public static void initializeSession(IInitializeSessionCallback cb) throws RemoteException {
450         CameraExtensionManagerGlobal.get().initializeSession(cb);
451     }
452 
453     /**
454      * @hide
455      */
releaseSession()456     public static void releaseSession() {
457         CameraExtensionManagerGlobal.get().releaseSession();
458     }
459 
460     /**
461      * @hide
462      */
areAdvancedExtensionsSupported()463     public static boolean areAdvancedExtensionsSupported() {
464         return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported();
465     }
466 
467     /**
468      * @hide
469      */
isExtensionSupported(String cameraId, int extensionType, CameraCharacteristics chars)470     public static boolean isExtensionSupported(String cameraId, int extensionType,
471             CameraCharacteristics chars) {
472         if (areAdvancedExtensionsSupported()) {
473             try {
474                 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extensionType);
475                 return extender.isExtensionAvailable(cameraId);
476             } catch (RemoteException e) {
477                 Log.e(TAG, "Failed to query extension availability! Extension service does not"
478                         + " respond!");
479                 return false;
480             }
481         } else {
482             Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
483             try {
484                 extenders = initializeExtension(extensionType);
485             } catch (IllegalArgumentException e) {
486                 return false;
487             }
488 
489             try {
490                 return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) &&
491                         extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata());
492             } catch (RemoteException e) {
493                 Log.e(TAG, "Failed to query extension availability! Extension service does not"
494                         + " respond!");
495                 return false;
496             }
497         }
498     }
499 
500     /**
501      * @hide
502      */
initializeAdvancedExtension(@xtension int extensionType)503     public static IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) {
504         IAdvancedExtenderImpl extender;
505         try {
506             extender = CameraExtensionManagerGlobal.get().initializeAdvancedExtension(
507                     extensionType);
508         } catch (RemoteException e) {
509             throw new IllegalStateException("Failed to initialize extension: " + extensionType);
510         }
511 
512         if (extender == null) {
513             throw new IllegalArgumentException("Unknown extension: " + extensionType);
514         }
515 
516         return extender;
517     }
518 
519     /**
520      * @hide
521      */
initializeExtension( @xtension int extensionType)522     public static Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> initializeExtension(
523             @Extension int extensionType) {
524         IPreviewExtenderImpl previewExtender;
525         IImageCaptureExtenderImpl imageExtender;
526         try {
527             previewExtender =
528                     CameraExtensionManagerGlobal.get().initializePreviewExtension(extensionType);
529             imageExtender =
530                     CameraExtensionManagerGlobal.get().initializeImageExtension(extensionType);
531         } catch (RemoteException e) {
532             throw new IllegalStateException("Failed to initialize extension: " + extensionType);
533         }
534         if ((imageExtender == null) || (previewExtender == null)) {
535             throw new IllegalArgumentException("Unknown extension: " + extensionType);
536         }
537 
538         return new Pair<>(previewExtender, imageExtender);
539     }
540 
isOutputSupportedFor(Class<T> klass)541     private static <T> boolean isOutputSupportedFor(Class<T> klass) {
542         Objects.requireNonNull(klass, "klass must not be null");
543 
544         if (klass == android.graphics.SurfaceTexture.class) {
545             return true;
546         }
547 
548         return false;
549     }
550 
551     /**
552      * Return a list of supported device-specific extensions for a given camera device.
553      *
554      * @return non-modifiable list of available extensions
555      */
getSupportedExtensions()556     public @NonNull List<Integer> getSupportedExtensions() {
557         ArrayList<Integer> ret = new ArrayList<>();
558         long clientId = registerClient(mContext);
559         if (clientId < 0) {
560             return Collections.unmodifiableList(ret);
561         }
562 
563         try {
564             for (int extensionType : EXTENSION_LIST) {
565                 if (isExtensionSupported(mCameraId, extensionType, mChars)) {
566                     ret.add(extensionType);
567                 }
568             }
569         } finally {
570             unregisterClient(clientId);
571         }
572 
573         return Collections.unmodifiableList(ret);
574     }
575 
576     /**
577      * Get a list of sizes compatible with {@code klass} to use as an output for the
578      * repeating request
579      * {@link CameraExtensionSession#setRepeatingRequest}.
580      *
581      * <p>Note that device-specific extensions are allowed to support only a subset
582      * of the camera output surfaces and resolutions.
583      * The {@link android.graphics.SurfaceTexture} class is guaranteed at least one size for
584      * backward compatible cameras whereas other output classes are not guaranteed to be supported.
585      * </p>
586      *
587      * @param extension the extension type
588      * @param klass     a non-{@code null} {@link Class} object reference
589      * @return non-modifiable list of available sizes or an empty list if the Surface output is not
590      * supported
591      * @throws NullPointerException     if {@code klass} was {@code null}
592      * @throws IllegalArgumentException in case of  unsupported extension.
593      */
594     @NonNull
getExtensionSupportedSizes(@xtension int extension, @NonNull Class<T> klass)595     public <T> List<Size> getExtensionSupportedSizes(@Extension int extension,
596             @NonNull Class<T> klass) {
597         if (!isOutputSupportedFor(klass)) {
598             return new ArrayList<>();
599         }
600         // TODO: Revisit this code once the Extension preview processor output format
601         //       ambiguity is resolved in b/169799538.
602 
603         long clientId = registerClient(mContext);
604         if (clientId < 0) {
605             throw new IllegalArgumentException("Unsupported extensions");
606         }
607 
608         try {
609             if (!isExtensionSupported(mCameraId, extension, mChars)) {
610                 throw new IllegalArgumentException("Unsupported extension");
611             }
612 
613             StreamConfigurationMap streamMap = mChars.get(
614                     CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
615             if (areAdvancedExtensionsSupported()) {
616                 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
617                 extender.init(mCameraId);
618                 return generateSupportedSizes(
619                         extender.getSupportedPreviewOutputResolutions(mCameraId),
620                         ImageFormat.PRIVATE, streamMap);
621             } else {
622                 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
623                         initializeExtension(extension);
624                 extenders.first.init(mCameraId, mChars.getNativeMetadata());
625                 return generateSupportedSizes(extenders.first.getSupportedResolutions(),
626                         ImageFormat.PRIVATE, streamMap);
627             }
628         } catch (RemoteException e) {
629             Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
630                     + " not respond!");
631             return new ArrayList<>();
632         } finally {
633             unregisterClient(clientId);
634         }
635     }
636 
637     /**
638      * Check whether a given extension is available and return the
639      * supported output surface resolutions that can be used for high-quality capture
640      * requests via {@link CameraExtensionSession#capture}.
641      *
642      * <p>Note that device-specific extensions are allowed to support only a subset
643      * of the camera resolutions advertised by
644      * {@link StreamConfigurationMap#getOutputSizes}.</p>
645      *
646      * <p>Device-specific extensions currently support at most two
647      * multi-frame capture surface formats. ImageFormat.JPEG will be supported by all
648      * extensions and ImageFormat.YUV_420_888 may or may not be supported.</p>
649      *
650      * @param extension the extension type
651      * @param format    device-specific extension output format
652      * @return non-modifiable list of available sizes or an empty list if the format is not
653      * supported.
654      * @throws IllegalArgumentException in case of format different from ImageFormat.JPEG /
655      *                                  ImageFormat.YUV_420_888; or unsupported extension.
656      */
657     public @NonNull
getExtensionSupportedSizes(@xtension int extension, int format)658     List<Size> getExtensionSupportedSizes(@Extension int extension, int format) {
659         try {
660             long clientId = registerClient(mContext);
661             if (clientId < 0) {
662                 throw new IllegalArgumentException("Unsupported extensions");
663             }
664 
665             try {
666                 if (!isExtensionSupported(mCameraId, extension, mChars)) {
667                     throw new IllegalArgumentException("Unsupported extension");
668                 }
669 
670                 StreamConfigurationMap streamMap = mChars.get(
671                         CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
672                 if (areAdvancedExtensionsSupported()) {
673                     switch(format) {
674                         case ImageFormat.YUV_420_888:
675                         case ImageFormat.JPEG:
676                             break;
677                         default:
678                             throw new IllegalArgumentException("Unsupported format: " + format);
679                     }
680                     IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
681                     extender.init(mCameraId);
682                     return generateSupportedSizes(extender.getSupportedCaptureOutputResolutions(
683                             mCameraId), format, streamMap);
684                 } else {
685                     if (format == ImageFormat.YUV_420_888) {
686                         Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
687                                 initializeExtension(extension);
688                         extenders.second.init(mCameraId, mChars.getNativeMetadata());
689                         if (extenders.second.getCaptureProcessor() == null) {
690                             // Extensions that don't implement any capture processor are limited to
691                             // JPEG only!
692                             return new ArrayList<>();
693                         }
694                         return generateSupportedSizes(extenders.second.getSupportedResolutions(),
695                                 format, streamMap);
696                     } else if (format == ImageFormat.JPEG) {
697                         Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
698                                 initializeExtension(extension);
699                         extenders.second.init(mCameraId, mChars.getNativeMetadata());
700                         if (extenders.second.getCaptureProcessor() != null) {
701                             // The framework will perform the additional encoding pass on the
702                             // processed YUV_420 buffers.
703                             return generateJpegSupportedSizes(
704                                     extenders.second.getSupportedResolutions(), streamMap);
705                         } else {
706                             return generateSupportedSizes(null, format, streamMap);
707                         }
708                     } else {
709                         throw new IllegalArgumentException("Unsupported format: " + format);
710                     }
711                 }
712             } finally {
713                 unregisterClient(clientId);
714             }
715         } catch (RemoteException e) {
716             Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
717                     + " not respond!");
718             return new ArrayList<>();
719         }
720     }
721 
722     /**
723      * Returns the estimated capture latency range in milliseconds for the
724      * target capture resolution during the calls to {@link CameraExtensionSession#capture}. This
725      * includes the time spent processing the multi-frame capture request along with any additional
726      * time for encoding of the processed buffer if necessary.
727      *
728      * @param extension         the extension type
729      * @param captureOutputSize size of the capture output surface. If it is not in the supported
730      *                          output sizes, maximum capture output size is used for the estimation
731      * @param format            device-specific extension output format
732      * @return the range of estimated minimal and maximal capture latency in milliseconds
733      * or null if no capture latency info can be provided
734      *
735      * @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG} /
736      *                                  {@link ImageFormat#YUV_420_888}; or unsupported extension.
737      */
getEstimatedCaptureLatencyRangeMillis(@xtension int extension, @NonNull Size captureOutputSize, @ImageFormat.Format int format)738     public @Nullable Range<Long> getEstimatedCaptureLatencyRangeMillis(@Extension int extension,
739             @NonNull Size captureOutputSize, @ImageFormat.Format int format) {
740         switch (format) {
741             case ImageFormat.YUV_420_888:
742             case ImageFormat.JPEG:
743                 //No op
744                 break;
745             default:
746                 throw new IllegalArgumentException("Unsupported format: " + format);
747         }
748 
749         long clientId = registerClient(mContext);
750         if (clientId < 0) {
751             throw new IllegalArgumentException("Unsupported extensions");
752         }
753 
754         try {
755             if (!isExtensionSupported(mCameraId, extension, mChars)) {
756                 throw new IllegalArgumentException("Unsupported extension");
757             }
758 
759             android.hardware.camera2.extension.Size sz =
760                     new android.hardware.camera2.extension.Size();
761             sz.width = captureOutputSize.getWidth();
762             sz.height = captureOutputSize.getHeight();
763             if (areAdvancedExtensionsSupported()) {
764                 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
765                 extender.init(mCameraId);
766                 LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId,
767                         sz, format);
768                 if (latencyRange != null) {
769                     return new Range(latencyRange.min, latencyRange.max);
770                 }
771             } else {
772                 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
773                         initializeExtension(extension);
774                 extenders.second.init(mCameraId, mChars.getNativeMetadata());
775                 if ((format == ImageFormat.YUV_420_888) &&
776                         (extenders.second.getCaptureProcessor() == null) ){
777                     // Extensions that don't implement any capture processor are limited to
778                     // JPEG only!
779                     return null;
780                 }
781                 if ((format == ImageFormat.JPEG) &&
782                         (extenders.second.getCaptureProcessor() != null)) {
783                     // The framework will perform the additional encoding pass on the
784                     // processed YUV_420 buffers. Latency in this case is very device
785                     // specific and cannot be estimated accurately enough.
786                     return  null;
787                 }
788 
789                 LatencyRange latencyRange = extenders.second.getEstimatedCaptureLatencyRange(sz);
790                 if (latencyRange != null) {
791                     return new Range(latencyRange.min, latencyRange.max);
792                 }
793             }
794         } catch (RemoteException e) {
795             Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
796                     + " not respond!");
797         } finally {
798             unregisterClient(clientId);
799         }
800 
801         return null;
802     }
803 
804     /**
805      * Returns the set of keys supported by a {@link CaptureRequest} submitted in a
806      * {@link CameraExtensionSession} with a given extension type.
807      *
808      * <p>The set returned is not modifiable, so any attempts to modify it will throw
809      * a {@code UnsupportedOperationException}.</p>
810      *
811      * @param extension the extension type
812      *
813      * @return non-modifiable set of capture keys supported by camera extension session initialized
814      *         with the given extension type.
815      * @throws IllegalArgumentException in case of unsupported extension.
816      */
817     @NonNull
getAvailableCaptureRequestKeys(@xtension int extension)818     public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) {
819         long clientId = registerClient(mContext);
820         if (clientId < 0) {
821             throw new IllegalArgumentException("Unsupported extensions");
822         }
823 
824         HashSet<CaptureRequest.Key> ret = new HashSet<>();
825 
826         try {
827             if (!isExtensionSupported(mCameraId, extension, mChars)) {
828                 throw new IllegalArgumentException("Unsupported extension");
829             }
830 
831             CameraMetadataNative captureRequestMeta = null;
832             if (areAdvancedExtensionsSupported()) {
833                 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
834                 extender.init(mCameraId);
835                 captureRequestMeta = extender.getAvailableCaptureRequestKeys(mCameraId);
836             } else {
837                 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
838                         initializeExtension(extension);
839                 extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
840                 extenders.second.init(mCameraId, mChars.getNativeMetadata());
841                 captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys();
842                 extenders.second.onDeInit();
843             }
844 
845             if (captureRequestMeta != null) {
846                 int[] requestKeys = captureRequestMeta.get(
847                         CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS);
848                 if (requestKeys == null) {
849                     throw new AssertionError(
850                             "android.request.availableRequestKeys must be non-null"
851                                     + " in the characteristics");
852                 }
853                 CameraCharacteristics requestChars = new CameraCharacteristics(
854                         captureRequestMeta);
855 
856                 Object crKey = CaptureRequest.Key.class;
857                 Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>) crKey;
858 
859                 ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped,
860                         requestKeys, /*includeSynthetic*/ true));
861             }
862 
863             // Jpeg quality and orientation must always be supported
864             if (!ret.contains(CaptureRequest.JPEG_QUALITY)) {
865                 ret.add(CaptureRequest.JPEG_QUALITY);
866             }
867             if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) {
868                 ret.add(CaptureRequest.JPEG_ORIENTATION);
869             }
870         } catch (RemoteException e) {
871             throw new IllegalStateException("Failed to query the available capture request keys!");
872         } finally {
873             unregisterClient(clientId);
874         }
875 
876         return Collections.unmodifiableSet(ret);
877     }
878 
879     /**
880      * Returns the set of keys supported by a {@link CaptureResult} passed as an argument to
881      * {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}.
882      *
883      * <p>The set returned is not modifiable, so any attempts to modify it will throw
884      * a {@code UnsupportedOperationException}.</p>
885      *
886      * <p>In case the set is empty, then the extension is not able to support any capture results
887      * and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}
888      * callback will not be fired.</p>
889      *
890      * @param extension the extension type
891      *
892      * @return non-modifiable set of capture result keys supported by camera extension session
893      *         initialized with the given extension type.
894      * @throws IllegalArgumentException in case of unsupported extension.
895      */
896     @NonNull
getAvailableCaptureResultKeys(@xtension int extension)897     public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) {
898         long clientId = registerClient(mContext);
899         if (clientId < 0) {
900             throw new IllegalArgumentException("Unsupported extensions");
901         }
902 
903         HashSet<CaptureResult.Key> ret = new HashSet<>();
904         try {
905             if (!isExtensionSupported(mCameraId, extension, mChars)) {
906                 throw new IllegalArgumentException("Unsupported extension");
907             }
908 
909             CameraMetadataNative captureResultMeta = null;
910             if (areAdvancedExtensionsSupported()) {
911                 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
912                 extender.init(mCameraId);
913                 captureResultMeta = extender.getAvailableCaptureResultKeys(mCameraId);
914             } else {
915                 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
916                         initializeExtension(extension);
917                 extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
918                 extenders.second.init(mCameraId, mChars.getNativeMetadata());
919                 captureResultMeta = extenders.second.getAvailableCaptureResultKeys();
920                 extenders.second.onDeInit();
921             }
922 
923             if (captureResultMeta != null) {
924                 int[] resultKeys = captureResultMeta.get(
925                         CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS);
926                 if (resultKeys == null) {
927                     throw new AssertionError("android.request.availableResultKeys must be non-null "
928                             + "in the characteristics");
929                 }
930                 CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta);
931                 Object crKey = CaptureResult.Key.class;
932                 Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
933 
934                 ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped,
935                         resultKeys, /*includeSynthetic*/ true));
936 
937                 // Jpeg quality, orientation and sensor timestamp must always be supported
938                 if (!ret.contains(CaptureResult.JPEG_QUALITY)) {
939                     ret.add(CaptureResult.JPEG_QUALITY);
940                 }
941                 if (!ret.contains(CaptureResult.JPEG_ORIENTATION)) {
942                     ret.add(CaptureResult.JPEG_ORIENTATION);
943                 }
944                 if (!ret.contains(CaptureResult.SENSOR_TIMESTAMP)) {
945                     ret.add(CaptureResult.SENSOR_TIMESTAMP);
946                 }
947             }
948         } catch (RemoteException e) {
949             throw new IllegalStateException("Failed to query the available capture result keys!");
950         } finally {
951             unregisterClient(clientId);
952         }
953 
954         return Collections.unmodifiableSet(ret);
955     }
956 }
957