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.content.pm.PackageManager;
     21 import android.hardware.camera2.CameraAccessException;
     22 import android.hardware.camera2.CameraCharacteristics;
     23 import android.hardware.camera2.CameraManager;
     24 import android.os.Handler;
     25 import android.os.HandlerThread;
     26 import android.os.Process;
     27 import android.text.TextUtils;
     28 import android.util.Log;
     29 
     30 import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
     31 
     32 import java.io.FileDescriptor;
     33 import java.io.PrintWriter;
     34 import java.lang.ref.WeakReference;
     35 import java.util.ArrayList;
     36 
     37 /**
     38  * Manages the flashlight.
     39  */
     40 public class FlashlightControllerImpl implements FlashlightController {
     41 
     42     private static final String TAG = "FlashlightController";
     43     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     44 
     45     private static final int DISPATCH_ERROR = 0;
     46     private static final int DISPATCH_CHANGED = 1;
     47     private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
     48 
     49     private final CameraManager mCameraManager;
     50     private final Context mContext;
     51     /** Call {@link #ensureHandler()} before using */
     52     private Handler mHandler;
     53 
     54     /** Lock on mListeners when accessing */
     55     private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
     56 
     57     /** Lock on {@code this} when accessing */
     58     private boolean mFlashlightEnabled;
     59 
     60     private String mCameraId;
     61     private boolean mTorchAvailable;
     62 
     63     public FlashlightControllerImpl(Context context) {
     64         mContext = context;
     65         mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
     66 
     67         tryInitCamera();
     68     }
     69 
     70     private void tryInitCamera() {
     71         try {
     72             mCameraId = getCameraId();
     73         } catch (Throwable e) {
     74             Log.e(TAG, "Couldn't initialize.", e);
     75             return;
     76         }
     77 
     78         if (mCameraId != null) {
     79             ensureHandler();
     80             mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
     81         }
     82     }
     83 
     84     public void setFlashlight(boolean enabled) {
     85         boolean pendingError = false;
     86         synchronized (this) {
     87             if (mCameraId == null) return;
     88             if (mFlashlightEnabled != enabled) {
     89                 mFlashlightEnabled = enabled;
     90                 try {
     91                     mCameraManager.setTorchMode(mCameraId, enabled);
     92                 } catch (CameraAccessException e) {
     93                     Log.e(TAG, "Couldn't set torch mode", e);
     94                     mFlashlightEnabled = false;
     95                     pendingError = true;
     96                 }
     97             }
     98         }
     99         dispatchModeChanged(mFlashlightEnabled);
    100         if (pendingError) {
    101             dispatchError();
    102         }
    103     }
    104 
    105     public boolean hasFlashlight() {
    106         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
    107     }
    108 
    109     public synchronized boolean isEnabled() {
    110         return mFlashlightEnabled;
    111     }
    112 
    113     public synchronized boolean isAvailable() {
    114         return mTorchAvailable;
    115     }
    116 
    117     public void addCallback(FlashlightListener l) {
    118         synchronized (mListeners) {
    119             if (mCameraId == null) {
    120                 tryInitCamera();
    121             }
    122             cleanUpListenersLocked(l);
    123             mListeners.add(new WeakReference<>(l));
    124             l.onFlashlightAvailabilityChanged(mTorchAvailable);
    125             l.onFlashlightChanged(mFlashlightEnabled);
    126         }
    127     }
    128 
    129     public void removeCallback(FlashlightListener l) {
    130         synchronized (mListeners) {
    131             cleanUpListenersLocked(l);
    132         }
    133     }
    134 
    135     private synchronized void ensureHandler() {
    136         if (mHandler == null) {
    137             HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
    138             thread.start();
    139             mHandler = new Handler(thread.getLooper());
    140         }
    141     }
    142 
    143     private String getCameraId() throws CameraAccessException {
    144         String[] ids = mCameraManager.getCameraIdList();
    145         for (String id : ids) {
    146             CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
    147             Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
    148             Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
    149             if (flashAvailable != null && flashAvailable
    150                     && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
    151                 return id;
    152             }
    153         }
    154         return null;
    155     }
    156 
    157     private void dispatchModeChanged(boolean enabled) {
    158         dispatchListeners(DISPATCH_CHANGED, enabled);
    159     }
    160 
    161     private void dispatchError() {
    162         dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
    163     }
    164 
    165     private void dispatchAvailabilityChanged(boolean available) {
    166         dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
    167     }
    168 
    169     private void dispatchListeners(int message, boolean argument) {
    170         synchronized (mListeners) {
    171             final int N = mListeners.size();
    172             boolean cleanup = false;
    173             for (int i = 0; i < N; i++) {
    174                 FlashlightListener l = mListeners.get(i).get();
    175                 if (l != null) {
    176                     if (message == DISPATCH_ERROR) {
    177                         l.onFlashlightError();
    178                     } else if (message == DISPATCH_CHANGED) {
    179                         l.onFlashlightChanged(argument);
    180                     } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
    181                         l.onFlashlightAvailabilityChanged(argument);
    182                     }
    183                 } else {
    184                     cleanup = true;
    185                 }
    186             }
    187             if (cleanup) {
    188                 cleanUpListenersLocked(null);
    189             }
    190         }
    191     }
    192 
    193     private void cleanUpListenersLocked(FlashlightListener listener) {
    194         for (int i = mListeners.size() - 1; i >= 0; i--) {
    195             FlashlightListener found = mListeners.get(i).get();
    196             if (found == null || found == listener) {
    197                 mListeners.remove(i);
    198             }
    199         }
    200     }
    201 
    202     private final CameraManager.TorchCallback mTorchCallback =
    203             new CameraManager.TorchCallback() {
    204 
    205         @Override
    206         public void onTorchModeUnavailable(String cameraId) {
    207             if (TextUtils.equals(cameraId, mCameraId)) {
    208                 setCameraAvailable(false);
    209             }
    210         }
    211 
    212         @Override
    213         public void onTorchModeChanged(String cameraId, boolean enabled) {
    214             if (TextUtils.equals(cameraId, mCameraId)) {
    215                 setCameraAvailable(true);
    216                 setTorchMode(enabled);
    217             }
    218         }
    219 
    220         private void setCameraAvailable(boolean available) {
    221             boolean changed;
    222             synchronized (FlashlightControllerImpl.this) {
    223                 changed = mTorchAvailable != available;
    224                 mTorchAvailable = available;
    225             }
    226             if (changed) {
    227                 if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
    228                 dispatchAvailabilityChanged(available);
    229             }
    230         }
    231 
    232         private void setTorchMode(boolean enabled) {
    233             boolean changed;
    234             synchronized (FlashlightControllerImpl.this) {
    235                 changed = mFlashlightEnabled != enabled;
    236                 mFlashlightEnabled = enabled;
    237             }
    238             if (changed) {
    239                 if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
    240                 dispatchModeChanged(enabled);
    241             }
    242         }
    243     };
    244 
    245     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    246         pw.println("FlashlightController state:");
    247 
    248         pw.print("  mCameraId=");
    249         pw.println(mCameraId);
    250         pw.print("  mFlashlightEnabled=");
    251         pw.println(mFlashlightEnabled);
    252         pw.print("  mTorchAvailable=");
    253         pw.println(mTorchAvailable);
    254     }
    255 }
    256