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 
CameraUsageEvent(int facing, String clientName, int apiLevel)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 
markCompleted()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          */
getDuration()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 
CameraServiceProxy(Context context)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
handleMessage(Message msg)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
onStart()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
onStartUser(int userHandle)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
onSwitchUser(int userHandle)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
binderDied()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      */
dumpUsageEvents()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 
switchUserLocked(int userHandle)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 
getEnabledUserHandles(int currentUserHandle)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 
notifySwitchWithRetries(int retries)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 
notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles)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 
updateActivityCount(String cameraId, int newCameraState, int facing, String clientName, int apiLevel)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 
notifyNfcService(boolean enablePolling)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 
toArray(Collection<Integer> c)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 
cameraStateToString(int newCameraState)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 
cameraFacingToString(int cameraFacing)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