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