1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ex.camera2.portability;
18 
19 import android.content.Context;
20 import android.os.Build;
21 
22 import com.android.ex.camera2.portability.debug.Log;
23 import com.android.ex.camera2.portability.util.SystemProperties;
24 
25 /**
26  * A factory class for {@link CameraAgent}.
27  *
28  * <p>The choice of framework API to use can be made automatically based on the
29  * system API level, explicitly forced by the client app, or overridden entirely
30  * by setting the system property com.camera2.portability.fwk_api to 1 or 2.</p>
31  */
32 public class CameraAgentFactory {
33     private static final Log.Tag TAG = new Log.Tag("CamAgntFact");
34 
35     /** Android release replacing the Camera class with the camera2 package. */
36     private static final int FIRST_SDK_WITH_API_2 = 21;
37 
38     // The debugging override, which overrides *all* API level selections if set
39     // to API_LEVEL_OVERRIDE_API{1,2}; otherwise, this has no effect. Note that
40     // we check this once when the library is first loaded so that #recycle()
41     // doesn't try to clean up the wrong type of CameraAgent.
42     private static final String API_LEVEL_OVERRIDE_KEY = "camera2.portability.force_api";
43     private static final String API_LEVEL_OVERRIDE_DEFAULT = "0";
44     private static final String API_LEVEL_OVERRIDE_API1 = "1";
45     private static final String API_LEVEL_OVERRIDE_API2 = "2";
46     private static final String API_LEVEL_OVERRIDE_VALUE =
47             SystemProperties.get(API_LEVEL_OVERRIDE_KEY, API_LEVEL_OVERRIDE_DEFAULT);
48 
49     private static CameraAgent sAndroidCameraAgent;
50     private static CameraAgent sAndroidCamera2Agent;
51     private static int sAndroidCameraAgentClientCount;
52     private static int sAndroidCamera2AgentClientCount;
53 
54     /**
55      * Used to indicate which camera framework should be used.
56      */
57     public static enum CameraApi {
58         /** Automatically select based on the device's SDK level. */
59         AUTO,
60 
61         /** Use the {@link android.hardware.Camera} class. */
62         API_1,
63 
64         /** Use the {@link android.hardware.camera2} package. */
65         API_2
66     };
67 
highestSupportedApi()68     private static CameraApi highestSupportedApi() {
69         // TODO: Check SDK_INT instead of RELEASE before L launch
70         if (Build.VERSION.SDK_INT >= FIRST_SDK_WITH_API_2 || Build.VERSION.CODENAME.equals("L")) {
71             return CameraApi.API_2;
72         } else {
73             return CameraApi.API_1;
74         }
75     }
76 
validateApiChoice(CameraApi choice)77     private static CameraApi validateApiChoice(CameraApi choice) {
78         if (API_LEVEL_OVERRIDE_VALUE.equals(API_LEVEL_OVERRIDE_API1)) {
79             Log.d(TAG, "API level overridden by system property: forced to 1");
80             return CameraApi.API_1;
81         } else if (API_LEVEL_OVERRIDE_VALUE.equals(API_LEVEL_OVERRIDE_API2)) {
82             Log.d(TAG, "API level overridden by system property: forced to 2");
83             return CameraApi.API_2;
84         }
85 
86         if (choice == null) {
87             Log.w(TAG, "null API level request, so assuming AUTO");
88             choice = CameraApi.AUTO;
89         }
90         if (choice == CameraApi.AUTO) {
91             choice = highestSupportedApi();
92         }
93 
94         return choice;
95     }
96 
97     /**
98      * Returns the android camera implementation of
99      * {@link com.android.camera.cameradevice.CameraAgent}.
100      *
101      * <p>To clean up the resources allocated by this call, be sure to invoke
102      * {@link #recycle(boolean)} with the same {@code api} value provided
103      * here.</p>
104      *
105      * @param context The application context.
106      * @param api Which camera framework to use.
107      * @return The {@link CameraAgent} to control the camera device.
108      *
109      * @throws UnsupportedOperationException If {@code CameraApi.API_2} was
110      *                                       requested on an unsupported device.
111      */
getAndroidCameraAgent(Context context, CameraApi api)112     public static synchronized CameraAgent getAndroidCameraAgent(Context context, CameraApi api) {
113         api = validateApiChoice(api);
114 
115         if (api == CameraApi.API_1) {
116             if (sAndroidCameraAgent == null) {
117                 sAndroidCameraAgent = new AndroidCameraAgentImpl();
118                 sAndroidCameraAgentClientCount = 1;
119             } else {
120                 ++sAndroidCameraAgentClientCount;
121             }
122             return sAndroidCameraAgent;
123         } else { // API_2
124             if (highestSupportedApi() == CameraApi.API_1) {
125                 throw new UnsupportedOperationException("Camera API_2 unavailable on this device");
126             }
127 
128             if (sAndroidCamera2Agent == null) {
129                 sAndroidCamera2Agent = new AndroidCamera2AgentImpl(context);
130                 sAndroidCamera2AgentClientCount = 1;
131             } else {
132                 ++sAndroidCamera2AgentClientCount;
133             }
134             return sAndroidCamera2Agent;
135         }
136     }
137 
138     /**
139      * Recycles the resources. Always call this method when the activity is
140      * stopped.
141      *
142      * @param api Which camera framework handle to recycle.
143      *
144      * @throws UnsupportedOperationException If {@code CameraApi.API_2} was
145      *                                       requested on an unsupported device.
146      */
recycle(CameraApi api)147     public static synchronized void recycle(CameraApi api) {
148         api = validateApiChoice(api);
149 
150         if (api == CameraApi.API_1) {
151             if (--sAndroidCameraAgentClientCount == 0 && sAndroidCameraAgent != null) {
152                 sAndroidCameraAgent.recycle();
153                 sAndroidCameraAgent = null;
154             }
155         } else { // API_2
156             if (highestSupportedApi() == CameraApi.API_1) {
157                 throw new UnsupportedOperationException("Camera API_2 unavailable on this device");
158             }
159 
160             if (--sAndroidCamera2AgentClientCount == 0 && sAndroidCamera2Agent != null) {
161                 sAndroidCamera2Agent.recycle();
162                 sAndroidCamera2Agent = null;
163             }
164         }
165     }
166 }
167