• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.media.AudioManager;
25 import android.metrics.LogMaker;
26 import android.nfc.INfcAdapter;
27 import android.os.Binder;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Message;
31 import android.os.Process;
32 import android.os.RemoteException;
33 import android.os.SystemClock;
34 import android.os.SystemProperties;
35 import android.os.UserManager;
36 import android.util.ArrayMap;
37 import android.util.ArraySet;
38 import android.util.Slog;
39 
40 import com.android.internal.logging.MetricsLogger;
41 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
42 import com.android.internal.util.FrameworkStatsLog;
43 import com.android.server.LocalServices;
44 import com.android.server.ServiceThread;
45 import com.android.server.SystemService;
46 import com.android.server.wm.WindowManagerInternal;
47 
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.Collections;
51 import java.util.List;
52 import java.util.Set;
53 import java.util.concurrent.ScheduledThreadPoolExecutor;
54 import java.util.concurrent.TimeUnit;
55 
56 /**
57  * CameraServiceProxy is the system_server analog to the camera service running in cameraserver.
58  *
59  * @hide
60  */
61 public class CameraServiceProxy extends SystemService
62         implements Handler.Callback, IBinder.DeathRecipient {
63     private static final String TAG = "CameraService_proxy";
64     private static final boolean DEBUG = false;
65 
66     /**
67      * This must match the ICameraService.aidl definition
68      */
69     private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
70 
71     public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy";
72 
73     // Flags arguments to NFC adapter to enable/disable NFC
74     public static final int DISABLE_POLLING_FLAGS = 0x1000;
75     public static final int ENABLE_POLLING_FLAGS = 0x0000;
76 
77     // Handler message codes
78     private static final int MSG_SWITCH_USER = 1;
79 
80     private static final int RETRY_DELAY_TIME = 20; //ms
81     private static final int RETRY_TIMES = 60;
82 
83     // Maximum entries to keep in usage history before dumping out
84     private static final int MAX_USAGE_HISTORY = 100;
85 
86     private final Context mContext;
87     private final ServiceThread mHandlerThread;
88     private final Handler mHandler;
89     private UserManager mUserManager;
90 
91     private final Object mLock = new Object();
92     private Set<Integer> mEnabledCameraUsers;
93     private int mLastUser;
94 
95     private ICameraService mCameraServiceRaw;
96 
97     // Map of currently active camera IDs
98     private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
99     private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
100 
101     private final MetricsLogger mLogger = new MetricsLogger();
102     private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
103     private static final String NFC_SERVICE_BINDER_NAME = "nfc";
104     private static final IBinder nfcInterfaceToken = new Binder();
105 
106     private final boolean mNotifyNfc;
107 
108     private ScheduledThreadPoolExecutor mLogWriterService = new ScheduledThreadPoolExecutor(
109             /*corePoolSize*/ 1);
110 
111     /**
112      * Structure to track camera usage
113      */
114     private static class CameraUsageEvent {
115         public final int mCameraFacing;
116         public final String mClientName;
117         public final int mAPILevel;
118 
119         private boolean mCompleted;
120         private long mDurationOrStartTimeMs;  // Either start time, or duration once completed
121 
CameraUsageEvent(int facing, String clientName, int apiLevel)122         public CameraUsageEvent(int facing, String clientName, int apiLevel) {
123             mCameraFacing = facing;
124             mClientName = clientName;
125             mAPILevel = apiLevel;
126             mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
127             mCompleted = false;
128         }
129 
markCompleted()130         public void markCompleted() {
131             if (mCompleted) {
132                 return;
133             }
134             mCompleted = true;
135             mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs;
136             if (CameraServiceProxy.DEBUG) {
137                 Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
138                         " was in use by " + mClientName + " for " +
139                         mDurationOrStartTimeMs + " ms");
140             }
141         }
142 
143         /**
144          * Return duration of camera usage event, or 0 if the event is not done
145          */
getDuration()146         public long getDuration() {
147             return mCompleted ? mDurationOrStartTimeMs : 0;
148         }
149     }
150 
151     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
152         @Override
153         public void onReceive(Context context, Intent intent) {
154             final String action = intent.getAction();
155             if (action == null) return;
156 
157             switch (action) {
158                 case Intent.ACTION_USER_ADDED:
159                 case Intent.ACTION_USER_REMOVED:
160                 case Intent.ACTION_USER_INFO_CHANGED:
161                 case Intent.ACTION_MANAGED_PROFILE_ADDED:
162                 case Intent.ACTION_MANAGED_PROFILE_REMOVED:
163                     synchronized(mLock) {
164                         // Return immediately if we haven't seen any users start yet
165                         if (mEnabledCameraUsers == null) return;
166                         switchUserLocked(mLastUser);
167                     }
168                     break;
169                 default:
170                     break; // do nothing
171             }
172 
173         }
174     };
175 
176     private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
177         @Override
178         public void pingForUserUpdate() {
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             notifySwitchWithRetries(RETRY_TIMES);
185         }
186 
187         @Override
188         public void notifyCameraState(String cameraId, int newCameraState, int facing,
189                 String clientName, int apiLevel) {
190             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
191                 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
192                         " camera service UID!");
193                 return;
194             }
195             String state = cameraStateToString(newCameraState);
196             String facingStr = cameraFacingToString(facing);
197             if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
198                     state + " for client " + clientName + " API Level " + apiLevel);
199 
200             updateActivityCount(cameraId, newCameraState, facing, clientName, apiLevel);
201         }
202     };
203 
CameraServiceProxy(Context context)204     public CameraServiceProxy(Context context) {
205         super(context);
206         mContext = context;
207         mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false);
208         mHandlerThread.start();
209         mHandler = new Handler(mHandlerThread.getLooper(), this);
210 
211         mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0;
212         if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled"));
213         // Don't keep any extra logging threads if not needed
214         mLogWriterService.setKeepAliveTime(1, TimeUnit.SECONDS);
215         mLogWriterService.allowCoreThreadTimeOut(true);
216     }
217 
218     @Override
handleMessage(Message msg)219     public boolean handleMessage(Message msg) {
220         switch(msg.what) {
221             case MSG_SWITCH_USER: {
222                 notifySwitchWithRetries(msg.arg1);
223             } break;
224             default: {
225                 Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what);
226             } break;
227         }
228         return true;
229     }
230 
231     @Override
onStart()232     public void onStart() {
233         mUserManager = UserManager.get(mContext);
234         if (mUserManager == null) {
235             // Should never see this unless someone messes up the SystemServer service boot order.
236             throw new IllegalStateException("UserManagerService must start before" +
237                     " CameraServiceProxy!");
238         }
239 
240         IntentFilter filter = new IntentFilter();
241         filter.addAction(Intent.ACTION_USER_ADDED);
242         filter.addAction(Intent.ACTION_USER_REMOVED);
243         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
244         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
245         filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
246         mContext.registerReceiver(mIntentReceiver, filter);
247 
248         publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
249         publishLocalService(CameraServiceProxy.class, this);
250 
251         CameraStatsJobService.schedule(mContext);
252     }
253 
254     @Override
onStartUser(int userHandle)255     public void onStartUser(int userHandle) {
256         synchronized(mLock) {
257             if (mEnabledCameraUsers == null) {
258                 // Initialize cameraserver, or update cameraserver if we are recovering
259                 // from a crash.
260                 switchUserLocked(userHandle);
261             }
262         }
263     }
264 
265     @Override
onSwitchUser(int userHandle)266     public void onSwitchUser(int userHandle) {
267         synchronized(mLock) {
268             switchUserLocked(userHandle);
269         }
270     }
271 
272     /**
273      * Handle the death of the native camera service
274      */
275     @Override
binderDied()276     public void binderDied() {
277         if (DEBUG) Slog.w(TAG, "Native camera service has died");
278         synchronized(mLock) {
279             mCameraServiceRaw = null;
280 
281             // All cameras reset to idle on camera service death
282             boolean wasEmpty = mActiveCameraUsage.isEmpty();
283             mActiveCameraUsage.clear();
284 
285             if ( mNotifyNfc && !wasEmpty ) {
286                 notifyNfcService(/*enablePolling*/ true);
287             }
288         }
289     }
290 
291     private class EventWriterTask implements Runnable {
292         private ArrayList<CameraUsageEvent> mEventList;
293         private static final long WRITER_SLEEP_MS = 100;
294 
EventWriterTask(ArrayList<CameraUsageEvent> eventList)295         public EventWriterTask(ArrayList<CameraUsageEvent> eventList) {
296             mEventList = eventList;
297         }
298 
299         @Override
run()300         public void run() {
301             if (mEventList != null) {
302                 for (CameraUsageEvent event : mEventList) {
303                     logCameraUsageEvent(event);
304                     try {
305                         Thread.sleep(WRITER_SLEEP_MS);
306                     } catch (InterruptedException e) {}
307                 }
308                 mEventList.clear();
309             }
310         }
311 
312         /**
313          * Write camera usage events to stats log.
314          * Package-private
315          */
logCameraUsageEvent(CameraUsageEvent e)316         private void logCameraUsageEvent(CameraUsageEvent e) {
317             int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
318             switch(e.mCameraFacing) {
319                 case ICameraServiceProxy.CAMERA_FACING_BACK:
320                     facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
321                     break;
322                 case ICameraServiceProxy.CAMERA_FACING_FRONT:
323                     facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
324                     break;
325                 case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
326                     facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
327                     break;
328                 default:
329                     Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing);
330             }
331             FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(),
332                     e.mAPILevel, e.mClientName, facing);
333         }
334     }
335 
336     /**
337      * Dump camera usage events to log.
338      * Package-private
339      */
dumpUsageEvents()340     void dumpUsageEvents() {
341         synchronized(mLock) {
342             // Randomize order of events so that it's not meaningful
343             Collections.shuffle(mCameraUsageHistory);
344             for (CameraUsageEvent e : mCameraUsageHistory) {
345                 if (DEBUG) {
346                     Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " +
347                             cameraFacingToString(e.mCameraFacing) + " for " +
348                             e.getDuration() + " ms");
349                 }
350                 int subtype = 0;
351                 switch(e.mCameraFacing) {
352                     case ICameraServiceProxy.CAMERA_FACING_BACK:
353                         subtype = MetricsEvent.CAMERA_BACK_USED;
354                         break;
355                     case ICameraServiceProxy.CAMERA_FACING_FRONT:
356                         subtype = MetricsEvent.CAMERA_FRONT_USED;
357                         break;
358                     case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
359                         subtype = MetricsEvent.CAMERA_EXTERNAL_USED;
360                         break;
361                     default:
362                         continue;
363                 }
364                 LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT)
365                         .setType(MetricsEvent.TYPE_ACTION)
366                         .setSubtype(subtype)
367                         .setLatency(e.getDuration())
368                         .addTaggedData(MetricsEvent.FIELD_CAMERA_API_LEVEL, e.mAPILevel)
369                         .setPackageName(e.mClientName);
370                 mLogger.write(l);
371             }
372 
373             mLogWriterService.execute(new EventWriterTask(
374                         new ArrayList<CameraUsageEvent>(mCameraUsageHistory)));
375 
376             mCameraUsageHistory.clear();
377         }
378         final long ident = Binder.clearCallingIdentity();
379         try {
380             CameraStatsJobService.schedule(mContext);
381         } finally {
382             Binder.restoreCallingIdentity(ident);
383         }
384     }
385 
switchUserLocked(int userHandle)386     private void switchUserLocked(int userHandle) {
387         Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
388         mLastUser = userHandle;
389         if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
390             // Some user handles have been added or removed, update cameraserver.
391             mEnabledCameraUsers = currentUserHandles;
392             notifySwitchWithRetriesLocked(RETRY_TIMES);
393         }
394     }
395 
getEnabledUserHandles(int currentUserHandle)396     private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
397         int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle);
398         Set<Integer> handles = new ArraySet<>(userProfiles.length);
399 
400         for (int id : userProfiles) {
401             handles.add(id);
402         }
403 
404         return handles;
405     }
406 
notifySwitchWithRetries(int retries)407     private void notifySwitchWithRetries(int retries) {
408         synchronized(mLock) {
409             notifySwitchWithRetriesLocked(retries);
410         }
411     }
412 
notifySwitchWithRetriesLocked(int retries)413     private void notifySwitchWithRetriesLocked(int retries) {
414         if (mEnabledCameraUsers == null) {
415             return;
416         }
417         if (notifyCameraserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) {
418             retries = 0;
419         }
420         if (retries <= 0) {
421             return;
422         }
423         Slog.i(TAG, "Could not notify camera service of user switch, retrying...");
424         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWITCH_USER, retries - 1, 0, null),
425                 RETRY_DELAY_TIME);
426     }
427 
notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles)428     private boolean notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles) {
429         // Forward the user switch event to the native camera service running in the cameraserver
430         // process.
431         if (mCameraServiceRaw == null) {
432             IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
433             if (cameraServiceBinder == null) {
434                 Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
435                 return false; // Camera service not active, cannot evict user clients.
436             }
437             try {
438                 cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
439             } catch (RemoteException e) {
440                 Slog.w(TAG, "Could not link to death of native camera service");
441                 return false;
442             }
443 
444             mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
445         }
446 
447         try {
448             mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
449         } catch (RemoteException e) {
450             Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e);
451             // Not much we can do if camera service is dead.
452             return false;
453         }
454         return true;
455     }
456 
updateActivityCount(String cameraId, int newCameraState, int facing, String clientName, int apiLevel)457     private void updateActivityCount(String cameraId, int newCameraState, int facing,
458             String clientName, int apiLevel) {
459         synchronized(mLock) {
460             // Update active camera list and notify NFC if necessary
461             boolean wasEmpty = mActiveCameraUsage.isEmpty();
462             switch (newCameraState) {
463                 case ICameraServiceProxy.CAMERA_STATE_OPEN:
464                     // Notify the audio subsystem about the facing of the most-recently opened
465                     // camera This can be used to select the best audio tuning in case video
466                     // recording with that camera will happen.  Since only open events are used, if
467                     // multiple cameras are opened at once, the one opened last will be used to
468                     // select audio tuning.
469                     AudioManager audioManager = getContext().getSystemService(AudioManager.class);
470                     if (audioManager != null) {
471                         // Map external to front for audio tuning purposes
472                         String facingStr = (facing == ICameraServiceProxy.CAMERA_FACING_BACK) ?
473                                 "back" : "front";
474                         String facingParameter = "cameraFacing=" + facingStr;
475                         audioManager.setParameters(facingParameter);
476                     }
477                     break;
478                 case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
479                     // Check current active camera IDs to see if this package is already talking to
480                     // some camera
481                     boolean alreadyActivePackage = false;
482                     for (int i = 0; i < mActiveCameraUsage.size(); i++) {
483                         if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) {
484                             alreadyActivePackage = true;
485                             break;
486                         }
487                     }
488                     // If not already active, notify window manager about this new package using a
489                     // camera
490                     if (!alreadyActivePackage) {
491                         WindowManagerInternal wmi =
492                                 LocalServices.getService(WindowManagerInternal.class);
493                         wmi.addNonHighRefreshRatePackage(clientName);
494                     }
495 
496                     // Update activity events
497                     CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel);
498                     CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
499                     if (oldEvent != null) {
500                         Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
501                         oldEvent.markCompleted();
502                         mCameraUsageHistory.add(oldEvent);
503                     }
504                     break;
505                 case ICameraServiceProxy.CAMERA_STATE_IDLE:
506                 case ICameraServiceProxy.CAMERA_STATE_CLOSED:
507                     CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId);
508                     if (doneEvent == null) break;
509 
510                     doneEvent.markCompleted();
511                     mCameraUsageHistory.add(doneEvent);
512                     if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
513                         dumpUsageEvents();
514                     }
515 
516                     // Check current active camera IDs to see if this package is still talking to
517                     // some camera
518                     boolean stillActivePackage = false;
519                     for (int i = 0; i < mActiveCameraUsage.size(); i++) {
520                         if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) {
521                             stillActivePackage = true;
522                             break;
523                         }
524                     }
525                     // If not longer active, notify window manager about this package being done
526                     // with camera
527                     if (!stillActivePackage) {
528                         WindowManagerInternal wmi =
529                                 LocalServices.getService(WindowManagerInternal.class);
530                         wmi.removeNonHighRefreshRatePackage(clientName);
531                     }
532 
533                     break;
534             }
535             boolean isEmpty = mActiveCameraUsage.isEmpty();
536             if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
537                 notifyNfcService(isEmpty);
538             }
539         }
540     }
541 
notifyNfcService(boolean enablePolling)542     private void notifyNfcService(boolean enablePolling) {
543 
544         IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
545         if (nfcServiceBinder == null) {
546             Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
547             return;
548         }
549         INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
550         int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
551         if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
552         try {
553             nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
554         } catch (RemoteException e) {
555             Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
556         }
557     }
558 
toArray(Collection<Integer> c)559     private static int[] toArray(Collection<Integer> c) {
560         int len = c.size();
561         int[] ret = new int[len];
562         int idx = 0;
563         for (Integer i : c) {
564             ret[idx++] = i;
565         }
566         return ret;
567     }
568 
cameraStateToString(int newCameraState)569     private static String cameraStateToString(int newCameraState) {
570         switch (newCameraState) {
571             case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
572             case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
573             case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
574             case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
575             default: break;
576         }
577         return "CAMERA_STATE_UNKNOWN";
578     }
579 
cameraFacingToString(int cameraFacing)580     private static String cameraFacingToString(int cameraFacing) {
581         switch (cameraFacing) {
582             case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
583             case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
584             case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
585             default: break;
586         }
587         return "CAMERA_FACING_UNKNOWN";
588     }
589 
590 }
591