Home | History | Annotate | Download | only in policy
      1 /*
      2  * Copyright (C) 2014 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.systemui.statusbar.policy;
     18 
     19 import android.content.Context;
     20 import android.graphics.SurfaceTexture;
     21 import android.hardware.camera2.CameraAccessException;
     22 import android.hardware.camera2.CameraCaptureSession;
     23 import android.hardware.camera2.CameraCharacteristics;
     24 import android.hardware.camera2.CameraDevice;
     25 import android.hardware.camera2.CameraManager;
     26 import android.hardware.camera2.CameraMetadata;
     27 import android.hardware.camera2.CaptureRequest;
     28 import android.os.Handler;
     29 import android.os.HandlerThread;
     30 import android.os.Process;
     31 import android.os.SystemProperties;
     32 import android.util.Log;
     33 import android.util.Size;
     34 import android.view.Surface;
     35 
     36 import java.lang.ref.WeakReference;
     37 import java.util.ArrayList;
     38 
     39 /**
     40  * Manages the flashlight.
     41  */
     42 public class FlashlightController {
     43 
     44     private static final String TAG = "FlashlightController";
     45     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     46 
     47     private static final int DISPATCH_ERROR = 0;
     48     private static final int DISPATCH_OFF = 1;
     49     private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
     50 
     51     private final CameraManager mCameraManager;
     52     /** Call {@link #ensureHandler()} before using */
     53     private Handler mHandler;
     54 
     55     /** Lock on mListeners when accessing */
     56     private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
     57 
     58     /** Lock on {@code this} when accessing */
     59     private boolean mFlashlightEnabled;
     60 
     61     private String mCameraId;
     62     private boolean mCameraAvailable;
     63     private CameraDevice mCameraDevice;
     64     private CaptureRequest mFlashlightRequest;
     65     private CameraCaptureSession mSession;
     66     private SurfaceTexture mSurfaceTexture;
     67     private Surface mSurface;
     68 
     69     public FlashlightController(Context mContext) {
     70         mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
     71         initialize();
     72     }
     73 
     74     public void initialize() {
     75         try {
     76             mCameraId = getCameraId();
     77         } catch (Throwable e) {
     78             Log.e(TAG, "Couldn't initialize.", e);
     79             return;
     80         }
     81 
     82         if (mCameraId != null) {
     83             ensureHandler();
     84             mCameraManager.registerAvailabilityCallback(mAvailabilityCallback, mHandler);
     85         }
     86     }
     87 
     88     public synchronized void setFlashlight(boolean enabled) {
     89         if (mFlashlightEnabled != enabled) {
     90             mFlashlightEnabled = enabled;
     91             postUpdateFlashlight();
     92         }
     93     }
     94 
     95     public void killFlashlight() {
     96         boolean enabled;
     97         synchronized (this) {
     98             enabled = mFlashlightEnabled;
     99         }
    100         if (enabled) {
    101             mHandler.post(mKillFlashlightRunnable);
    102         }
    103     }
    104 
    105     public synchronized boolean isAvailable() {
    106         return mCameraAvailable;
    107     }
    108 
    109     public void addListener(FlashlightListener l) {
    110         synchronized (mListeners) {
    111             cleanUpListenersLocked(l);
    112             mListeners.add(new WeakReference<>(l));
    113         }
    114     }
    115 
    116     public void removeListener(FlashlightListener l) {
    117         synchronized (mListeners) {
    118             cleanUpListenersLocked(l);
    119         }
    120     }
    121 
    122     private synchronized void ensureHandler() {
    123         if (mHandler == null) {
    124             HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
    125             thread.start();
    126             mHandler = new Handler(thread.getLooper());
    127         }
    128     }
    129 
    130     private void startDevice() throws CameraAccessException {
    131         mCameraManager.openCamera(getCameraId(), mCameraListener, mHandler);
    132     }
    133 
    134     private void startSession() throws CameraAccessException {
    135         mSurfaceTexture = new SurfaceTexture(false);
    136         Size size = getSmallestSize(mCameraDevice.getId());
    137         mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
    138         mSurface = new Surface(mSurfaceTexture);
    139         ArrayList<Surface> outputs = new ArrayList<>(1);
    140         outputs.add(mSurface);
    141         mCameraDevice.createCaptureSession(outputs, mSessionListener, mHandler);
    142     }
    143 
    144     private Size getSmallestSize(String cameraId) throws CameraAccessException {
    145         Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
    146                 .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
    147                 .getOutputSizes(SurfaceTexture.class);
    148         if (outputSizes == null || outputSizes.length == 0) {
    149             throw new IllegalStateException(
    150                     "Camera " + cameraId + "doesn't support any outputSize.");
    151         }
    152         Size chosen = outputSizes[0];
    153         for (Size s : outputSizes) {
    154             if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
    155                 chosen = s;
    156             }
    157         }
    158         return chosen;
    159     }
    160 
    161     private void postUpdateFlashlight() {
    162         ensureHandler();
    163         mHandler.post(mUpdateFlashlightRunnable);
    164     }
    165 
    166     private String getCameraId() throws CameraAccessException {
    167         String[] ids = mCameraManager.getCameraIdList();
    168         for (String id : ids) {
    169             CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
    170             Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
    171             Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
    172             if (flashAvailable != null && flashAvailable
    173                     && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
    174                 return id;
    175             }
    176         }
    177         return null;
    178     }
    179 
    180     private void updateFlashlight(boolean forceDisable) {
    181         try {
    182             boolean enabled;
    183             synchronized (this) {
    184                 enabled = mFlashlightEnabled && !forceDisable;
    185             }
    186             if (enabled) {
    187                 if (mCameraDevice == null) {
    188                     startDevice();
    189                     return;
    190                 }
    191                 if (mSession == null) {
    192                     startSession();
    193                     return;
    194                 }
    195                 if (mFlashlightRequest == null) {
    196                     CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
    197                             CameraDevice.TEMPLATE_PREVIEW);
    198                     builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
    199                     builder.addTarget(mSurface);
    200                     CaptureRequest request = builder.build();
    201                     mSession.capture(request, null, mHandler);
    202                     mFlashlightRequest = request;
    203                 }
    204             } else {
    205                 if (mCameraDevice != null) {
    206                     mCameraDevice.close();
    207                     teardown();
    208                 }
    209             }
    210 
    211         } catch (CameraAccessException|IllegalStateException|UnsupportedOperationException e) {
    212             Log.e(TAG, "Error in updateFlashlight", e);
    213             handleError();
    214         }
    215     }
    216 
    217     private void teardown() {
    218         mCameraDevice = null;
    219         mSession = null;
    220         mFlashlightRequest = null;
    221         if (mSurface != null) {
    222             mSurface.release();
    223             mSurfaceTexture.release();
    224         }
    225         mSurface = null;
    226         mSurfaceTexture = null;
    227     }
    228 
    229     private void handleError() {
    230         synchronized (this) {
    231             mFlashlightEnabled = false;
    232         }
    233         dispatchError();
    234         dispatchOff();
    235         updateFlashlight(true /* forceDisable */);
    236     }
    237 
    238     private void dispatchOff() {
    239         dispatchListeners(DISPATCH_OFF, false /* argument (ignored) */);
    240     }
    241 
    242     private void dispatchError() {
    243         dispatchListeners(DISPATCH_ERROR, false /* argument (ignored) */);
    244     }
    245 
    246     private void dispatchAvailabilityChanged(boolean available) {
    247         dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
    248     }
    249 
    250     private void dispatchListeners(int message, boolean argument) {
    251         synchronized (mListeners) {
    252             final int N = mListeners.size();
    253             boolean cleanup = false;
    254             for (int i = 0; i < N; i++) {
    255                 FlashlightListener l = mListeners.get(i).get();
    256                 if (l != null) {
    257                     if (message == DISPATCH_ERROR) {
    258                         l.onFlashlightError();
    259                     } else if (message == DISPATCH_OFF) {
    260                         l.onFlashlightOff();
    261                     } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
    262                         l.onFlashlightAvailabilityChanged(argument);
    263                     }
    264                 } else {
    265                     cleanup = true;
    266                 }
    267             }
    268             if (cleanup) {
    269                 cleanUpListenersLocked(null);
    270             }
    271         }
    272     }
    273 
    274     private void cleanUpListenersLocked(FlashlightListener listener) {
    275         for (int i = mListeners.size() - 1; i >= 0; i--) {
    276             FlashlightListener found = mListeners.get(i).get();
    277             if (found == null || found == listener) {
    278                 mListeners.remove(i);
    279             }
    280         }
    281     }
    282 
    283     private final CameraDevice.StateListener mCameraListener = new CameraDevice.StateListener() {
    284         @Override
    285         public void onOpened(CameraDevice camera) {
    286             mCameraDevice = camera;
    287             postUpdateFlashlight();
    288         }
    289 
    290         @Override
    291         public void onDisconnected(CameraDevice camera) {
    292             if (mCameraDevice == camera) {
    293                 dispatchOff();
    294                 teardown();
    295             }
    296         }
    297 
    298         @Override
    299         public void onError(CameraDevice camera, int error) {
    300             Log.e(TAG, "Camera error: camera=" + camera + " error=" + error);
    301             if (camera == mCameraDevice || mCameraDevice == null) {
    302                 handleError();
    303             }
    304         }
    305     };
    306 
    307     private final CameraCaptureSession.StateListener mSessionListener =
    308             new CameraCaptureSession.StateListener() {
    309         @Override
    310         public void onConfigured(CameraCaptureSession session) {
    311             mSession = session;
    312             postUpdateFlashlight();
    313         }
    314 
    315         @Override
    316         public void onConfigureFailed(CameraCaptureSession session) {
    317             Log.e(TAG, "Configure failed.");
    318             if (mSession == null || mSession == session) {
    319                 handleError();
    320             }
    321         }
    322     };
    323 
    324     private final Runnable mUpdateFlashlightRunnable = new Runnable() {
    325         @Override
    326         public void run() {
    327             updateFlashlight(false /* forceDisable */);
    328         }
    329     };
    330 
    331     private final Runnable mKillFlashlightRunnable = new Runnable() {
    332         @Override
    333         public void run() {
    334             synchronized (this) {
    335                 mFlashlightEnabled = false;
    336             }
    337             updateFlashlight(true /* forceDisable */);
    338             dispatchOff();
    339         }
    340     };
    341 
    342     private final CameraManager.AvailabilityCallback mAvailabilityCallback =
    343             new CameraManager.AvailabilityCallback() {
    344         @Override
    345         public void onCameraAvailable(String cameraId) {
    346             if (DEBUG) Log.d(TAG, "onCameraAvailable(" + cameraId + ")");
    347             if (cameraId.equals(mCameraId)) {
    348                 setCameraAvailable(true);
    349             }
    350         }
    351 
    352         @Override
    353         public void onCameraUnavailable(String cameraId) {
    354             if (DEBUG) Log.d(TAG, "onCameraUnavailable(" + cameraId + ")");
    355             if (cameraId.equals(mCameraId)) {
    356                 setCameraAvailable(false);
    357             }
    358         }
    359 
    360         private void setCameraAvailable(boolean available) {
    361             boolean changed;
    362             synchronized (FlashlightController.this) {
    363                 changed = mCameraAvailable != available;
    364                 mCameraAvailable = available;
    365             }
    366             if (changed) {
    367                 if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
    368                 dispatchAvailabilityChanged(available);
    369             }
    370         }
    371     };
    372 
    373     public interface FlashlightListener {
    374 
    375         /**
    376          * Called when the flashlight turns off unexpectedly.
    377          */
    378         void onFlashlightOff();
    379 
    380         /**
    381          * Called when there is an error that turns the flashlight off.
    382          */
    383         void onFlashlightError();
    384 
    385         /**
    386          * Called when there is a change in availability of the flashlight functionality
    387          * @param available true if the flashlight is currently available.
    388          */
    389         void onFlashlightAvailabilityChanged(boolean available);
    390     }
    391 }
    392