Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright 2015 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 com.android.server.camera;
     17 
     18 import android.content.BroadcastReceiver;
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.content.IntentFilter;
     22 import android.hardware.ICameraService;
     23 import android.hardware.ICameraServiceProxy;
     24 import android.metrics.LogMaker;
     25 import android.nfc.INfcAdapter;
     26 import android.os.Binder;
     27 import android.os.Handler;
     28 import android.os.IBinder;
     29 import android.os.Message;
     30 import android.os.Process;
     31 import android.os.RemoteException;
     32 import android.os.SystemClock;
     33 import android.os.SystemProperties;
     34 import android.os.UserManager;
     35 import android.util.ArrayMap;
     36 import android.util.ArraySet;
     37 import android.util.Slog;
     38 
     39 import com.android.internal.logging.MetricsLogger;
     40 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     41 import com.android.server.LocalServices;
     42 import com.android.server.ServiceThread;
     43 import com.android.server.SystemService;
     44 
     45 import java.util.ArrayList;
     46 import java.util.Collection;
     47 import java.util.Collections;
     48 import java.util.List;
     49 import java.util.Set;
     50 
     51 /**
     52  * CameraServiceProxy is the system_server analog to the camera service running in mediaserver.
     53  *
     54  * @hide
     55  */
     56 public class CameraServiceProxy extends SystemService
     57         implements Handler.Callback, IBinder.DeathRecipient {
     58     private static final String TAG = "CameraService_proxy";
     59     private static final boolean DEBUG = false;
     60 
     61     /**
     62      * This must match the ICameraService.aidl definition
     63      */
     64     private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
     65 
     66     public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy";
     67 
     68     // Flags arguments to NFC adapter to enable/disable NFC
     69     public static final int DISABLE_POLLING_FLAGS = 0x1000;
     70     public static final int ENABLE_POLLING_FLAGS = 0x0000;
     71 
     72     // Handler message codes
     73     private static final int MSG_SWITCH_USER = 1;
     74 
     75     private static final int RETRY_DELAY_TIME = 20; //ms
     76 
     77     // Maximum entries to keep in usage history before dumping out
     78     private static final int MAX_USAGE_HISTORY = 100;
     79 
     80     private final Context mContext;
     81     private final ServiceThread mHandlerThread;
     82     private final Handler mHandler;
     83     private UserManager mUserManager;
     84 
     85     private final Object mLock = new Object();
     86     private Set<Integer> mEnabledCameraUsers;
     87     private int mLastUser;
     88 
     89     private ICameraService mCameraServiceRaw;
     90 
     91     private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
     92     private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
     93     private final MetricsLogger mLogger = new MetricsLogger();
     94     private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
     95     private static final String NFC_SERVICE_BINDER_NAME = "nfc";
     96     private static final IBinder nfcInterfaceToken = new Binder();
     97 
     98     private final boolean mNotifyNfc;
     99 
    100     /**
    101      * Structure to track camera usage
    102      */
    103     private static class CameraUsageEvent {
    104         public final int mCameraFacing;
    105         public final String mClientName;
    106         public final int mAPILevel;
    107 
    108         private boolean mCompleted;
    109         private long mDurationOrStartTimeMs;  // Either start time, or duration once completed
    110 
    111         public CameraUsageEvent(int facing, String clientName, int apiLevel) {
    112             mCameraFacing = facing;
    113             mClientName = clientName;
    114             mAPILevel = apiLevel;
    115             mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
    116             mCompleted = false;
    117         }
    118 
    119         public void markCompleted() {
    120             if (mCompleted) {
    121                 return;
    122             }
    123             mCompleted = true;
    124             mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs;
    125             if (CameraServiceProxy.DEBUG) {
    126                 Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
    127                         " was in use by " + mClientName + " for " +
    128                         mDurationOrStartTimeMs + " ms");
    129             }
    130         }
    131 
    132         /**
    133          * Return duration of camera usage event, or 0 if the event is not done
    134          */
    135         public long getDuration() {
    136             return mCompleted ? mDurationOrStartTimeMs : 0;
    137         }
    138     }
    139 
    140     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    141         @Override
    142         public void onReceive(Context context, Intent intent) {
    143             final String action = intent.getAction();
    144             if (action == null) return;
    145 
    146             switch (action) {
    147                 case Intent.ACTION_USER_ADDED:
    148                 case Intent.ACTION_USER_REMOVED:
    149                 case Intent.ACTION_USER_INFO_CHANGED:
    150                 case Intent.ACTION_MANAGED_PROFILE_ADDED:
    151                 case Intent.ACTION_MANAGED_PROFILE_REMOVED:
    152                     synchronized(mLock) {
    153                         // Return immediately if we haven't seen any users start yet
    154                         if (mEnabledCameraUsers == null) return;
    155                         switchUserLocked(mLastUser);
    156                     }
    157                     break;
    158                 default:
    159                     break; // do nothing
    160             }
    161 
    162         }
    163     };
    164 
    165     private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
    166         @Override
    167         public void pingForUserUpdate() {
    168             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
    169                 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
    170                         " camera service UID!");
    171                 return;
    172             }
    173             notifySwitchWithRetries(30);
    174         }
    175 
    176         @Override
    177         public void notifyCameraState(String cameraId, int newCameraState, int facing,
    178                 String clientName, int apiLevel) {
    179             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
    180                 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
    181                         " camera service UID!");
    182                 return;
    183             }
    184             String state = cameraStateToString(newCameraState);
    185             String facingStr = cameraFacingToString(facing);
    186             if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
    187                     state + " for client " + clientName + " API Level " + apiLevel);
    188 
    189             updateActivityCount(cameraId, newCameraState, facing, clientName, apiLevel);
    190         }
    191     };
    192 
    193     public CameraServiceProxy(Context context) {
    194         super(context);
    195         mContext = context;
    196         mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false);
    197         mHandlerThread.start();
    198         mHandler = new Handler(mHandlerThread.getLooper(), this);
    199 
    200         mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0;
    201         if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled"));
    202     }
    203 
    204     @Override
    205     public boolean handleMessage(Message msg) {
    206         switch(msg.what) {
    207             case MSG_SWITCH_USER: {
    208                 notifySwitchWithRetries(msg.arg1);
    209             } break;
    210             default: {
    211                 Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what);
    212             } break;
    213         }
    214         return true;
    215     }
    216 
    217     @Override
    218     public void onStart() {
    219         mUserManager = UserManager.get(mContext);
    220         if (mUserManager == null) {
    221             // Should never see this unless someone messes up the SystemServer service boot order.
    222             throw new IllegalStateException("UserManagerService must start before" +
    223                     " CameraServiceProxy!");
    224         }
    225 
    226         IntentFilter filter = new IntentFilter();
    227         filter.addAction(Intent.ACTION_USER_ADDED);
    228         filter.addAction(Intent.ACTION_USER_REMOVED);
    229         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
    230         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
    231         filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
    232         mContext.registerReceiver(mIntentReceiver, filter);
    233 
    234         publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
    235         publishLocalService(CameraServiceProxy.class, this);
    236 
    237         CameraStatsJobService.schedule(mContext);
    238     }
    239 
    240     @Override
    241     public void onStartUser(int userHandle) {
    242         synchronized(mLock) {
    243             if (mEnabledCameraUsers == null) {
    244                 // Initialize mediaserver, or update mediaserver if we are recovering from a crash.
    245                 switchUserLocked(userHandle);
    246             }
    247         }
    248     }
    249 
    250     @Override
    251     public void onSwitchUser(int userHandle) {
    252         synchronized(mLock) {
    253             switchUserLocked(userHandle);
    254         }
    255     }
    256 
    257     /**
    258      * Handle the death of the native camera service
    259      */
    260     @Override
    261     public void binderDied() {
    262         if (DEBUG) Slog.w(TAG, "Native camera service has died");
    263         synchronized(mLock) {
    264             mCameraServiceRaw = null;
    265 
    266             // All cameras reset to idle on camera service death
    267             boolean wasEmpty = mActiveCameraUsage.isEmpty();
    268             mActiveCameraUsage.clear();
    269 
    270             if ( mNotifyNfc && !wasEmpty ) {
    271                 notifyNfcService(/*enablePolling*/ true);
    272             }
    273         }
    274     }
    275 
    276     /**
    277      * Dump camera usage events to log.
    278      * Package-private
    279      */
    280     void dumpUsageEvents() {
    281         synchronized(mLock) {
    282             // Randomize order of events so that it's not meaningful
    283             Collections.shuffle(mCameraUsageHistory);
    284             for (CameraUsageEvent e : mCameraUsageHistory) {
    285                 if (DEBUG) {
    286                     Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " +
    287                             cameraFacingToString(e.mCameraFacing) + " for " +
    288                             e.getDuration() + " ms");
    289                 }
    290                 int subtype = 0;
    291                 switch(e.mCameraFacing) {
    292                     case ICameraServiceProxy.CAMERA_FACING_BACK:
    293                         subtype = MetricsEvent.CAMERA_BACK_USED;
    294                         break;
    295                     case ICameraServiceProxy.CAMERA_FACING_FRONT:
    296                         subtype = MetricsEvent.CAMERA_FRONT_USED;
    297                         break;
    298                     case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
    299                         subtype = MetricsEvent.CAMERA_EXTERNAL_USED;
    300                         break;
    301                     default:
    302                         continue;
    303                 }
    304                 LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT)
    305                         .setType(MetricsEvent.TYPE_ACTION)
    306                         .setSubtype(subtype)
    307                         .setLatency(e.getDuration())
    308                         .addTaggedData(MetricsEvent.FIELD_CAMERA_API_LEVEL, e.mAPILevel)
    309                         .setPackageName(e.mClientName);
    310                 mLogger.write(l);
    311             }
    312             mCameraUsageHistory.clear();
    313         }
    314         final long ident = Binder.clearCallingIdentity();
    315         try {
    316             CameraStatsJobService.schedule(mContext);
    317         } finally {
    318             Binder.restoreCallingIdentity(ident);
    319         }
    320     }
    321 
    322     private void switchUserLocked(int userHandle) {
    323         Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
    324         mLastUser = userHandle;
    325         if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
    326             // Some user handles have been added or removed, update mediaserver.
    327             mEnabledCameraUsers = currentUserHandles;
    328             notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, currentUserHandles);
    329         }
    330     }
    331 
    332     private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
    333         int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle);
    334         Set<Integer> handles = new ArraySet<>(userProfiles.length);
    335 
    336         for (int id : userProfiles) {
    337             handles.add(id);
    338         }
    339 
    340         return handles;
    341     }
    342 
    343     private void notifySwitchWithRetries(int retries) {
    344         synchronized(mLock) {
    345             if (mEnabledCameraUsers == null) {
    346                 return;
    347             }
    348             if (notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) {
    349                 retries = 0;
    350             }
    351         }
    352         if (retries <= 0) {
    353             return;
    354         }
    355         Slog.i(TAG, "Could not notify camera service of user switch, retrying...");
    356         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWITCH_USER, retries - 1, 0, null),
    357                 RETRY_DELAY_TIME);
    358     }
    359 
    360     private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) {
    361         // Forward the user switch event to the native camera service running in the mediaserver
    362         // process.
    363         if (mCameraServiceRaw == null) {
    364             IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
    365             if (cameraServiceBinder == null) {
    366                 Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
    367                 return false; // Camera service not active, cannot evict user clients.
    368             }
    369             try {
    370                 cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
    371             } catch (RemoteException e) {
    372                 Slog.w(TAG, "Could not link to death of native camera service");
    373                 return false;
    374             }
    375 
    376             mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
    377         }
    378 
    379         try {
    380             mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
    381         } catch (RemoteException e) {
    382             Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e);
    383             // Not much we can do if camera service is dead.
    384             return false;
    385         }
    386         return true;
    387     }
    388 
    389     private void updateActivityCount(String cameraId, int newCameraState, int facing,
    390             String clientName, int apiLevel) {
    391         synchronized(mLock) {
    392             // Update active camera list and notify NFC if necessary
    393             boolean wasEmpty = mActiveCameraUsage.isEmpty();
    394             switch (newCameraState) {
    395                 case ICameraServiceProxy.CAMERA_STATE_OPEN:
    396                     break;
    397                 case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
    398                     CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel);
    399                     CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
    400                     if (oldEvent != null) {
    401                         Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
    402                         oldEvent.markCompleted();
    403                         mCameraUsageHistory.add(oldEvent);
    404                     }
    405                     break;
    406                 case ICameraServiceProxy.CAMERA_STATE_IDLE:
    407                 case ICameraServiceProxy.CAMERA_STATE_CLOSED:
    408                     CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId);
    409                     if (doneEvent != null) {
    410                         doneEvent.markCompleted();
    411                         mCameraUsageHistory.add(doneEvent);
    412                         if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
    413                             dumpUsageEvents();
    414                         }
    415                     }
    416                     break;
    417             }
    418             boolean isEmpty = mActiveCameraUsage.isEmpty();
    419             if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
    420                 notifyNfcService(isEmpty);
    421             }
    422         }
    423     }
    424 
    425     private void notifyNfcService(boolean enablePolling) {
    426 
    427         IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
    428         if (nfcServiceBinder == null) {
    429             Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
    430             return;
    431         }
    432         INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
    433         int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
    434         if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
    435         try {
    436             nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
    437         } catch (RemoteException e) {
    438             Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
    439         }
    440     }
    441 
    442     private static int[] toArray(Collection<Integer> c) {
    443         int len = c.size();
    444         int[] ret = new int[len];
    445         int idx = 0;
    446         for (Integer i : c) {
    447             ret[idx++] = i;
    448         }
    449         return ret;
    450     }
    451 
    452     private static String cameraStateToString(int newCameraState) {
    453         switch (newCameraState) {
    454             case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
    455             case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
    456             case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
    457             case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
    458             default: break;
    459         }
    460         return "CAMERA_STATE_UNKNOWN";
    461     }
    462 
    463     private static String cameraFacingToString(int cameraFacing) {
    464         switch (cameraFacing) {
    465             case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
    466             case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
    467             case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
    468             default: break;
    469         }
    470         return "CAMERA_FACING_UNKNOWN";
    471     }
    472 
    473 }
    474