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 
    107         private boolean mCompleted;
    108         private long mDurationOrStartTimeMs;  // Either start time, or duration once completed
    109 
    110         public CameraUsageEvent(int facing, String clientName) {
    111             mCameraFacing = facing;
    112             mClientName = clientName;
    113             mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
    114             mCompleted = false;
    115         }
    116 
    117         public void markCompleted() {
    118             if (mCompleted) {
    119                 return;
    120             }
    121             mCompleted = true;
    122             mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs;
    123             if (CameraServiceProxy.DEBUG) {
    124                 Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
    125                         " was in use by " + mClientName + " for " +
    126                         mDurationOrStartTimeMs + " ms");
    127             }
    128         }
    129 
    130         /**
    131          * Return duration of camera usage event, or 0 if the event is not done
    132          */
    133         public long getDuration() {
    134             return mCompleted ? mDurationOrStartTimeMs : 0;
    135         }
    136     }
    137 
    138     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    139         @Override
    140         public void onReceive(Context context, Intent intent) {
    141             final String action = intent.getAction();
    142             if (action == null) return;
    143 
    144             switch (action) {
    145                 case Intent.ACTION_USER_ADDED:
    146                 case Intent.ACTION_USER_REMOVED:
    147                 case Intent.ACTION_USER_INFO_CHANGED:
    148                 case Intent.ACTION_MANAGED_PROFILE_ADDED:
    149                 case Intent.ACTION_MANAGED_PROFILE_REMOVED:
    150                     synchronized(mLock) {
    151                         // Return immediately if we haven't seen any users start yet
    152                         if (mEnabledCameraUsers == null) return;
    153                         switchUserLocked(mLastUser);
    154                     }
    155                     break;
    156                 default:
    157                     break; // do nothing
    158             }
    159 
    160         }
    161     };
    162 
    163     private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
    164         @Override
    165         public void pingForUserUpdate() {
    166             notifySwitchWithRetries(30);
    167         }
    168 
    169         @Override
    170         public void notifyCameraState(String cameraId, int newCameraState, int facing,
    171                 String clientName) {
    172             String state = cameraStateToString(newCameraState);
    173             String facingStr = cameraFacingToString(facing);
    174             if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
    175                     state + " for client " + clientName);
    176 
    177             updateActivityCount(cameraId, newCameraState, facing, clientName);
    178         }
    179     };
    180 
    181     public CameraServiceProxy(Context context) {
    182         super(context);
    183         mContext = context;
    184         mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false);
    185         mHandlerThread.start();
    186         mHandler = new Handler(mHandlerThread.getLooper(), this);
    187 
    188         mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0;
    189         if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled"));
    190     }
    191 
    192     @Override
    193     public boolean handleMessage(Message msg) {
    194         switch(msg.what) {
    195             case MSG_SWITCH_USER: {
    196                 notifySwitchWithRetries(msg.arg1);
    197             } break;
    198             default: {
    199                 Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what);
    200             } break;
    201         }
    202         return true;
    203     }
    204 
    205     @Override
    206     public void onStart() {
    207         mUserManager = UserManager.get(mContext);
    208         if (mUserManager == null) {
    209             // Should never see this unless someone messes up the SystemServer service boot order.
    210             throw new IllegalStateException("UserManagerService must start before" +
    211                     " CameraServiceProxy!");
    212         }
    213 
    214         IntentFilter filter = new IntentFilter();
    215         filter.addAction(Intent.ACTION_USER_ADDED);
    216         filter.addAction(Intent.ACTION_USER_REMOVED);
    217         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
    218         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
    219         filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
    220         mContext.registerReceiver(mIntentReceiver, filter);
    221 
    222         publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
    223         publishLocalService(CameraServiceProxy.class, this);
    224 
    225         CameraStatsJobService.schedule(mContext);
    226     }
    227 
    228     @Override
    229     public void onStartUser(int userHandle) {
    230         synchronized(mLock) {
    231             if (mEnabledCameraUsers == null) {
    232                 // Initialize mediaserver, or update mediaserver if we are recovering from a crash.
    233                 switchUserLocked(userHandle);
    234             }
    235         }
    236     }
    237 
    238     @Override
    239     public void onSwitchUser(int userHandle) {
    240         synchronized(mLock) {
    241             switchUserLocked(userHandle);
    242         }
    243     }
    244 
    245     /**
    246      * Handle the death of the native camera service
    247      */
    248     @Override
    249     public void binderDied() {
    250         if (DEBUG) Slog.w(TAG, "Native camera service has died");
    251         synchronized(mLock) {
    252             mCameraServiceRaw = null;
    253 
    254             // All cameras reset to idle on camera service death
    255             boolean wasEmpty = mActiveCameraUsage.isEmpty();
    256             mActiveCameraUsage.clear();
    257 
    258             if ( mNotifyNfc && !wasEmpty ) {
    259                 notifyNfcService(/*enablePolling*/ true);
    260             }
    261         }
    262     }
    263 
    264     /**
    265      * Dump camera usage events to log.
    266      * Package-private
    267      */
    268     void dumpUsageEvents() {
    269         synchronized(mLock) {
    270             // Randomize order of events so that it's not meaningful
    271             Collections.shuffle(mCameraUsageHistory);
    272             for (CameraUsageEvent e : mCameraUsageHistory) {
    273                 if (DEBUG) {
    274                     Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " +
    275                             cameraFacingToString(e.mCameraFacing) + " for " +
    276                             e.getDuration() + " ms");
    277                 }
    278                 int subtype = 0;
    279                 switch(e.mCameraFacing) {
    280                     case ICameraServiceProxy.CAMERA_FACING_BACK:
    281                         subtype = MetricsEvent.CAMERA_BACK_USED;
    282                         break;
    283                     case ICameraServiceProxy.CAMERA_FACING_FRONT:
    284                         subtype = MetricsEvent.CAMERA_FRONT_USED;
    285                         break;
    286                     case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
    287                         subtype = MetricsEvent.CAMERA_EXTERNAL_USED;
    288                         break;
    289                     default:
    290                         continue;
    291                 }
    292                 LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT)
    293                         .setType(MetricsEvent.TYPE_ACTION)
    294                         .setSubtype(subtype)
    295                         .setLatency(e.getDuration())
    296                         .setPackageName(e.mClientName);
    297                 mLogger.write(l);
    298             }
    299             mCameraUsageHistory.clear();
    300         }
    301         CameraStatsJobService.schedule(mContext);
    302     }
    303 
    304     private void switchUserLocked(int userHandle) {
    305         Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
    306         mLastUser = userHandle;
    307         if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
    308             // Some user handles have been added or removed, update mediaserver.
    309             mEnabledCameraUsers = currentUserHandles;
    310             notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, currentUserHandles);
    311         }
    312     }
    313 
    314     private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
    315         int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle);
    316         Set<Integer> handles = new ArraySet<>(userProfiles.length);
    317 
    318         for (int id : userProfiles) {
    319             handles.add(id);
    320         }
    321 
    322         return handles;
    323     }
    324 
    325     private void notifySwitchWithRetries(int retries) {
    326         synchronized(mLock) {
    327             if (mEnabledCameraUsers == null) {
    328                 return;
    329             }
    330             if (notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) {
    331                 retries = 0;
    332             }
    333         }
    334         if (retries <= 0) {
    335             return;
    336         }
    337         Slog.i(TAG, "Could not notify camera service of user switch, retrying...");
    338         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWITCH_USER, retries - 1, 0, null),
    339                 RETRY_DELAY_TIME);
    340     }
    341 
    342     private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) {
    343         // Forward the user switch event to the native camera service running in the mediaserver
    344         // process.
    345         if (mCameraServiceRaw == null) {
    346             IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
    347             if (cameraServiceBinder == null) {
    348                 Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
    349                 return false; // Camera service not active, cannot evict user clients.
    350             }
    351             try {
    352                 cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
    353             } catch (RemoteException e) {
    354                 Slog.w(TAG, "Could not link to death of native camera service");
    355                 return false;
    356             }
    357 
    358             mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
    359         }
    360 
    361         try {
    362             mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
    363         } catch (RemoteException e) {
    364             Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e);
    365             // Not much we can do if camera service is dead.
    366             return false;
    367         }
    368         return true;
    369     }
    370 
    371     private void updateActivityCount(String cameraId, int newCameraState, int facing, String clientName) {
    372         synchronized(mLock) {
    373             // Update active camera list and notify NFC if necessary
    374             boolean wasEmpty = mActiveCameraUsage.isEmpty();
    375             switch (newCameraState) {
    376                 case ICameraServiceProxy.CAMERA_STATE_OPEN:
    377                     break;
    378                 case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
    379                     CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName);
    380                     CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
    381                     if (oldEvent != null) {
    382                         Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
    383                         oldEvent.markCompleted();
    384                         mCameraUsageHistory.add(oldEvent);
    385                     }
    386                     break;
    387                 case ICameraServiceProxy.CAMERA_STATE_IDLE:
    388                 case ICameraServiceProxy.CAMERA_STATE_CLOSED:
    389                     CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId);
    390                     if (doneEvent != null) {
    391                         doneEvent.markCompleted();
    392                         mCameraUsageHistory.add(doneEvent);
    393                         if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
    394                             dumpUsageEvents();
    395                         }
    396                     }
    397                     break;
    398             }
    399             boolean isEmpty = mActiveCameraUsage.isEmpty();
    400             if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
    401                 notifyNfcService(isEmpty);
    402             }
    403         }
    404     }
    405 
    406     private void notifyNfcService(boolean enablePolling) {
    407 
    408         IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
    409         if (nfcServiceBinder == null) {
    410             Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
    411             return;
    412         }
    413         INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
    414         int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
    415         if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
    416         try {
    417             nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
    418         } catch (RemoteException e) {
    419             Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
    420         }
    421     }
    422 
    423     private static int[] toArray(Collection<Integer> c) {
    424         int len = c.size();
    425         int[] ret = new int[len];
    426         int idx = 0;
    427         for (Integer i : c) {
    428             ret[idx++] = i;
    429         }
    430         return ret;
    431     }
    432 
    433     private static String cameraStateToString(int newCameraState) {
    434         switch (newCameraState) {
    435             case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
    436             case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
    437             case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
    438             case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
    439             default: break;
    440         }
    441         return "CAMERA_STATE_UNKNOWN";
    442     }
    443 
    444     private static String cameraFacingToString(int cameraFacing) {
    445         switch (cameraFacing) {
    446             case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
    447             case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
    448             case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
    449             default: break;
    450         }
    451         return "CAMERA_FACING_UNKNOWN";
    452     }
    453 
    454 }
    455