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 &lt; minPaddingZoomFactor &lt;= 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