1bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong/*
2bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * Copyright (C) 2013 The Android Open Source Project
3bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong *
4bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * Licensed under the Apache License, Version 2.0 (the "License");
5bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * you may not use this file except in compliance with the License.
6bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * You may obtain a copy of the License at
7bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong *
8bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong *      http://www.apache.org/licenses/LICENSE-2.0
9bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong *
10bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * Unless required by applicable law or agreed to in writing, software
11bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * distributed under the License is distributed on an "AS IS" BASIS,
12bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * See the License for the specific language governing permissions and
14bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong * limitations under the License.
15bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong */
16bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong
17bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kongpackage com.android.ex.camera2.portability;
18bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong
19a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.content.Context;
2050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucherimport android.os.Build;
2150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
2250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucherimport com.android.ex.camera2.portability.debug.Log;
2350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucherimport com.android.ex.camera2.portability.util.SystemProperties;
24a0842b40441db5332a5290f941021636b1182761Sol Boucher
25bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong/**
264f425ba476d62b4be7078f2084af37cf306b31c6Sol Boucher * A factory class for {@link CameraAgent}.
2750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher *
2850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher * <p>The choice of framework API to use can be made automatically based on the
2950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher * system API level, explicitly forced by the client app, or overridden entirely
3050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher * by setting the system property com.camera2.portability.fwk_api to 1 or 2.</p>
31bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong */
324f425ba476d62b4be7078f2084af37cf306b31c6Sol Boucherpublic class CameraAgentFactory {
3350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static final Log.Tag TAG = new Log.Tag("CamAgntFact");
3450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
3550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    /** Android release replacing the Camera class with the camera2 package. */
3650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static final int FIRST_SDK_WITH_API_2 = 21;
3750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
3850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    // The debugging override, which overrides *all* API level selections if set
3950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    // to API_LEVEL_OVERRIDE_API{1,2}; otherwise, this has no effect. Note that
4050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    // we check this once when the library is first loaded so that #recycle()
4150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    // doesn't try to clean up the wrong type of CameraAgent.
4250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static final String API_LEVEL_OVERRIDE_KEY = "camera2.portability.force_api";
4350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static final String API_LEVEL_OVERRIDE_DEFAULT = "0";
4450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static final String API_LEVEL_OVERRIDE_API1 = "1";
4550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static final String API_LEVEL_OVERRIDE_API2 = "2";
4650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static final String API_LEVEL_OVERRIDE_VALUE =
4750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            SystemProperties.get(API_LEVEL_OVERRIDE_KEY, API_LEVEL_OVERRIDE_DEFAULT);
48bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong
49a0842b40441db5332a5290f941021636b1182761Sol Boucher    private static CameraAgent sAndroidCameraAgent;
5050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static CameraAgent sAndroidCamera2Agent;
514f425ba476d62b4be7078f2084af37cf306b31c6Sol Boucher    private static int sAndroidCameraAgentClientCount;
5250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static int sAndroidCamera2AgentClientCount;
53bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong
54bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong    /**
5550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * Used to indicate which camera framework should be used.
5650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     */
5750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    public static enum CameraApi {
5850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        /** Automatically select based on the device's SDK level. */
5950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        AUTO,
6050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
6150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        /** Use the {@link android.hardware.Camera} class. */
6250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        API_1,
6350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
6450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        /** Use the {@link android.hardware.camera2} package. */
6550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        API_2
6650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    };
6750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
6850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static CameraApi highestSupportedApi() {
6950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        // TODO: Check SDK_INT instead of RELEASE before L launch
7050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        if (Build.VERSION.SDK_INT >= FIRST_SDK_WITH_API_2 || Build.VERSION.CODENAME.equals("L")) {
7150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            return CameraApi.API_2;
7250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        } else {
7350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            return CameraApi.API_1;
7450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        }
7550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    }
7650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
7750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    private static CameraApi validateApiChoice(CameraApi choice) {
7850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        if (API_LEVEL_OVERRIDE_VALUE.equals(API_LEVEL_OVERRIDE_API1)) {
7950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            Log.d(TAG, "API level overridden by system property: forced to 1");
8050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            return CameraApi.API_1;
8150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        } else if (API_LEVEL_OVERRIDE_VALUE.equals(API_LEVEL_OVERRIDE_API2)) {
8250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            Log.d(TAG, "API level overridden by system property: forced to 2");
8350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            return CameraApi.API_2;
8450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        }
8550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
8650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        if (choice == null) {
8750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            Log.w(TAG, "null API level request, so assuming AUTO");
8850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            choice = CameraApi.AUTO;
8950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        }
9050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        if (choice == CameraApi.AUTO) {
9150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            choice = highestSupportedApi();
9250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        }
9350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
9450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        return choice;
9550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    }
9650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
9750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    /**
9850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * Returns the android camera implementation of
9950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * {@link com.android.camera.cameradevice.CameraAgent}.
10050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     *
10150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * <p>To clean up the resources allocated by this call, be sure to invoke
10250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * {@link #recycle(boolean)} with the same {@code api} value provided
10350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * here.</p>
104bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong     *
10550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * @param context The application context.
10650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * @param api Which camera framework to use.
1074f425ba476d62b4be7078f2084af37cf306b31c6Sol Boucher     * @return The {@link CameraAgent} to control the camera device.
10850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     *
10950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * @throws UnsupportedOperationException If {@code CameraApi.API_2} was
11050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     *                                       requested on an unsupported device.
111bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong     */
11250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    public static synchronized CameraAgent getAndroidCameraAgent(Context context, CameraApi api) {
11350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        api = validateApiChoice(api);
11450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
11550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        if (api == CameraApi.API_1) {
11650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            if (sAndroidCameraAgent == null) {
117a0842b40441db5332a5290f941021636b1182761Sol Boucher                sAndroidCameraAgent = new AndroidCameraAgentImpl();
11850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                sAndroidCameraAgentClientCount = 1;
11950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            } else {
12050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                ++sAndroidCameraAgentClientCount;
121a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
12250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            return sAndroidCameraAgent;
12350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        } else { // API_2
12450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            if (highestSupportedApi() == CameraApi.API_1) {
12550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                throw new UnsupportedOperationException("Camera API_2 unavailable on this device");
12650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            }
12750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
12850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            if (sAndroidCamera2Agent == null) {
12950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                sAndroidCamera2Agent = new AndroidCamera2AgentImpl(context);
13050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                sAndroidCamera2AgentClientCount = 1;
13150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            } else {
13250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                ++sAndroidCamera2AgentClientCount;
13350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            }
13450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            return sAndroidCamera2Agent;
135bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong        }
136bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong    }
137bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong
138bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong    /**
139bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong     * Recycles the resources. Always call this method when the activity is
140bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong     * stopped.
14150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     *
14250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * @param api Which camera framework handle to recycle.
14350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     *
14450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     * @throws UnsupportedOperationException If {@code CameraApi.API_2} was
14550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher     *                                       requested on an unsupported device.
146bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong     */
14750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher    public static synchronized void recycle(CameraApi api) {
14850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        api = validateApiChoice(api);
14950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
15050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        if (api == CameraApi.API_1) {
15150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            if (--sAndroidCameraAgentClientCount == 0 && sAndroidCameraAgent != null) {
15250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                sAndroidCameraAgent.recycle();
15350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                sAndroidCameraAgent = null;
15450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            }
15550f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        } else { // API_2
15650f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            if (highestSupportedApi() == CameraApi.API_1) {
15750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                throw new UnsupportedOperationException("Camera API_2 unavailable on this device");
15850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            }
15950f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
16050f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            if (--sAndroidCamera2AgentClientCount == 0 && sAndroidCamera2Agent != null) {
16150f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                sAndroidCamera2Agent.recycle();
16250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                sAndroidCamera2Agent = null;
16350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            }
164bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong        }
165bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong    }
166bdaaaf5f0257168590fa8965e4d59b054636e6dfAngus Kong}
167