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