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