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 static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; 19 import static android.os.Build.VERSION_CODES.M; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.TestApi; 25 import android.app.ActivityManager; 26 import android.app.ActivityTaskManager; 27 import android.app.compat.CompatChanges; 28 import android.compat.annotation.ChangeId; 29 import android.compat.annotation.Disabled; 30 import android.compat.annotation.Overridable; 31 import android.content.BroadcastReceiver; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.pm.ActivityInfo; 36 import android.content.pm.PackageManager; 37 import android.content.pm.ParceledListSlice; 38 import android.content.res.Configuration; 39 import android.hardware.CameraSessionStats; 40 import android.hardware.CameraStreamStats; 41 import android.hardware.ICameraService; 42 import android.hardware.ICameraServiceProxy; 43 import android.hardware.camera2.CameraCharacteristics; 44 import android.hardware.camera2.CameraMetadata; 45 import android.hardware.camera2.CaptureRequest; 46 import android.hardware.devicestate.DeviceStateManager; 47 import android.hardware.devicestate.DeviceStateManager.FoldStateListener; 48 import android.hardware.display.DisplayManager; 49 import android.media.AudioManager; 50 import android.nfc.INfcAdapter; 51 import android.os.Binder; 52 import android.os.Handler; 53 import android.os.HandlerExecutor; 54 import android.os.IBinder; 55 import android.os.Message; 56 import android.os.Process; 57 import android.os.RemoteException; 58 import android.os.SystemClock; 59 import android.os.SystemProperties; 60 import android.os.UserHandle; 61 import android.os.UserManager; 62 import android.stats.camera.nano.CameraProtos.CameraStreamProto; 63 import android.util.ArrayMap; 64 import android.util.ArraySet; 65 import android.util.Log; 66 import android.util.Slog; 67 import android.view.Display; 68 import android.view.IDisplayWindowListener; 69 import android.view.Surface; 70 import android.view.WindowManagerGlobal; 71 72 import com.android.framework.protobuf.nano.MessageNano; 73 import com.android.internal.annotations.GuardedBy; 74 import com.android.internal.util.FrameworkStatsLog; 75 import com.android.server.LocalServices; 76 import com.android.server.ServiceThread; 77 import com.android.server.SystemService; 78 import com.android.server.wm.WindowManagerInternal; 79 80 import java.lang.annotation.Retention; 81 import java.lang.annotation.RetentionPolicy; 82 import java.util.ArrayList; 83 import java.util.Arrays; 84 import java.util.Collection; 85 import java.util.Collections; 86 import java.util.List; 87 import java.util.Set; 88 import java.util.concurrent.ScheduledThreadPoolExecutor; 89 import java.util.concurrent.TimeUnit; 90 91 /** 92 * CameraServiceProxy is the system_server analog to the camera service running in cameraserver. 93 * 94 * @hide 95 */ 96 public class CameraServiceProxy extends SystemService 97 implements Handler.Callback, IBinder.DeathRecipient { 98 private static final String TAG = "CameraService_proxy"; 99 private static final boolean DEBUG = false; 100 101 /** 102 * This must match the ICameraService.aidl definition 103 */ 104 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; 105 106 public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy"; 107 108 /** 109 * When enabled this change id forces the packages it is applied to override the default 110 * camera rotate & crop behavior and always return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE . 111 * The default behavior along with all possible override combinations is discussed in the table 112 * below. 113 */ 114 @ChangeId 115 @Overridable 116 @Disabled 117 @TestApi 118 public static final long OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS = 189229956L; // buganizer id 119 120 /** 121 * When enabled this change id forces the packages it is applied to ignore the current value of 122 * 'android:resizeableActivity' as well as target SDK equal to or below M and consider the 123 * activity as non-resizeable. In this case, the value of camera rotate & crop will only depend 124 * on the needed compensation considering the current display rotation. 125 */ 126 @ChangeId 127 @Overridable 128 @Disabled 129 @TestApi 130 public static final long OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK = 191513214L; // buganizer id 131 132 /** 133 * Possible override combinations 134 * 135 * |OVERRIDE |OVERRIDE_ 136 * |CAMERA_ |CAMERA_ 137 * |ROTATE_ |RESIZEABLE_ 138 * |AND_CROP_ |AND_SDK_ 139 * |DEFAULTS |CHECK 140 * _________________________________________________ 141 * Default Behavior | D |D 142 * _________________________________________________ 143 * Ignore SDK&Resize | D |E 144 * _________________________________________________ 145 * SCALER_ROTATE_AND_CROP_NONE | E |D, E 146 * _________________________________________________ 147 * Where: 148 * E -> Override enabled 149 * D -> Override disabled 150 * Default behavior -> Rotate&crop will be calculated depending on the required 151 * compensation necessary for the current display rotation. 152 * Additionally the app must either target M (or below) 153 * or is declared as non-resizeable. 154 * Ignore SDK&Resize -> The Rotate&crop value will depend on the required 155 * compensation for the current display rotation. 156 * SCALER_ROTATE_AND_CROP_NONE -> Always return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE 157 */ 158 159 // Flags arguments to NFC adapter to enable/disable NFC 160 public static final int DISABLE_POLLING_FLAGS = 0x1000; 161 public static final int ENABLE_POLLING_FLAGS = 0x0000; 162 163 // Handler message codes 164 private static final int MSG_SWITCH_USER = 1; 165 private static final int MSG_NOTIFY_DEVICE_STATE = 2; 166 167 private static final int RETRY_DELAY_TIME = 20; //ms 168 private static final int RETRY_TIMES = 60; 169 170 @IntDef(flag = true, prefix = { "DEVICE_STATE_" }, value = { 171 ICameraService.DEVICE_STATE_BACK_COVERED, 172 ICameraService.DEVICE_STATE_FRONT_COVERED, 173 ICameraService.DEVICE_STATE_FOLDED 174 }) 175 @Retention(RetentionPolicy.SOURCE) 176 @interface DeviceStateFlags {} 177 178 // Maximum entries to keep in usage history before dumping out 179 private static final int MAX_USAGE_HISTORY = 20; 180 // Number of stream statistics being dumped for each camera session 181 // Must be equal to number of CameraStreamProto in CameraActionEvent 182 private static final int MAX_STREAM_STATISTICS = 5; 183 184 private final Context mContext; 185 private final ServiceThread mHandlerThread; 186 private final Handler mHandler; 187 private UserManager mUserManager; 188 189 private final Object mLock = new Object(); 190 private Set<Integer> mEnabledCameraUsers; 191 private int mLastUser; 192 // The current set of device state flags. May be different from mLastReportedDeviceState if the 193 // native camera service has not been notified of the change. 194 @GuardedBy("mLock") 195 @DeviceStateFlags 196 private int mDeviceState; 197 // The most recent device state flags reported to the native camera server. 198 @GuardedBy("mLock") 199 @DeviceStateFlags 200 private int mLastReportedDeviceState; 201 202 private ICameraService mCameraServiceRaw; 203 204 // Map of currently active camera IDs 205 private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>(); 206 private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>(); 207 208 private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc"; 209 private static final String NFC_SERVICE_BINDER_NAME = "nfc"; 210 private static final IBinder nfcInterfaceToken = new Binder(); 211 212 private final boolean mNotifyNfc; 213 214 private ScheduledThreadPoolExecutor mLogWriterService = new ScheduledThreadPoolExecutor( 215 /*corePoolSize*/ 1); 216 217 /** 218 * Structure to track camera usage 219 */ 220 private static class CameraUsageEvent { 221 public final String mCameraId; 222 public final int mCameraFacing; 223 public final String mClientName; 224 public final int mAPILevel; 225 public final boolean mIsNdk; 226 public final int mAction; 227 public final int mLatencyMs; 228 public final int mOperatingMode; 229 230 private boolean mCompleted; 231 public int mInternalReconfigure; 232 public long mRequestCount; 233 public long mResultErrorCount; 234 public boolean mDeviceError; 235 public List<CameraStreamStats> mStreamStats; 236 private long mDurationOrStartTimeMs; // Either start time, or duration once completed 237 CameraUsageEvent(String cameraId, int facing, String clientName, int apiLevel, boolean isNdk, int action, int latencyMs, int operatingMode)238 CameraUsageEvent(String cameraId, int facing, String clientName, int apiLevel, 239 boolean isNdk, int action, int latencyMs, int operatingMode) { 240 mCameraId = cameraId; 241 mCameraFacing = facing; 242 mClientName = clientName; 243 mAPILevel = apiLevel; 244 mDurationOrStartTimeMs = SystemClock.elapsedRealtime(); 245 mCompleted = false; 246 mIsNdk = isNdk; 247 mAction = action; 248 mLatencyMs = latencyMs; 249 mOperatingMode = operatingMode; 250 } 251 markCompleted(int internalReconfigure, long requestCount, long resultErrorCount, boolean deviceError, List<CameraStreamStats> streamStats)252 public void markCompleted(int internalReconfigure, long requestCount, 253 long resultErrorCount, boolean deviceError, 254 List<CameraStreamStats> streamStats) { 255 if (mCompleted) { 256 return; 257 } 258 mCompleted = true; 259 mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs; 260 mInternalReconfigure = internalReconfigure; 261 mRequestCount = requestCount; 262 mResultErrorCount = resultErrorCount; 263 mDeviceError = deviceError; 264 mStreamStats = streamStats; 265 if (CameraServiceProxy.DEBUG) { 266 Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) + 267 " was in use by " + mClientName + " for " + 268 mDurationOrStartTimeMs + " ms"); 269 } 270 } 271 272 /** 273 * Return duration of camera usage event, or 0 if the event is not done 274 */ getDuration()275 public long getDuration() { 276 return mCompleted ? mDurationOrStartTimeMs : 0; 277 } 278 } 279 280 private final class DisplayWindowListener extends IDisplayWindowListener.Stub { 281 282 @Override onDisplayConfigurationChanged(int displayId, Configuration newConfig)283 public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { 284 ICameraService cs = getCameraServiceRawLocked(); 285 if (cs == null) return; 286 287 try { 288 cs.notifyDisplayConfigurationChange(); 289 } catch (RemoteException e) { 290 Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e); 291 // Not much we can do if camera service is dead. 292 } 293 } 294 295 @Override onDisplayAdded(int displayId)296 public void onDisplayAdded(int displayId) { } 297 298 @Override onDisplayRemoved(int displayId)299 public void onDisplayRemoved(int displayId) { } 300 301 @Override onFixedRotationStarted(int displayId, int newRotation)302 public void onFixedRotationStarted(int displayId, int newRotation) { } 303 304 @Override onFixedRotationFinished(int displayId)305 public void onFixedRotationFinished(int displayId) { } 306 } 307 308 309 private final DisplayWindowListener mDisplayWindowListener = new DisplayWindowListener(); 310 311 public static final class TaskInfo { 312 public int frontTaskId; 313 public boolean isResizeable; 314 public boolean isFixedOrientationLandscape; 315 public boolean isFixedOrientationPortrait; 316 public int displayId; 317 public int userId; 318 } 319 320 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 321 @Override 322 public void onReceive(Context context, Intent intent) { 323 final String action = intent.getAction(); 324 if (action == null) return; 325 326 switch (action) { 327 case Intent.ACTION_USER_ADDED: 328 case Intent.ACTION_USER_REMOVED: 329 case Intent.ACTION_USER_INFO_CHANGED: 330 case Intent.ACTION_MANAGED_PROFILE_ADDED: 331 case Intent.ACTION_MANAGED_PROFILE_REMOVED: 332 synchronized(mLock) { 333 // Return immediately if we haven't seen any users start yet 334 if (mEnabledCameraUsers == null) return; 335 switchUserLocked(mLastUser); 336 } 337 break; 338 default: 339 break; // do nothing 340 } 341 342 } 343 }; 344 isMOrBelow(Context ctx, String packageName)345 private static boolean isMOrBelow(Context ctx, String packageName) { 346 try { 347 return ctx.getPackageManager().getPackageInfo( 348 packageName, 0).applicationInfo.targetSdkVersion <= M; 349 } catch (PackageManager.NameNotFoundException e) { 350 Slog.e(TAG,"Package name not found!"); 351 } 352 return false; 353 } 354 355 /** 356 * Estimate the app crop-rotate-scale compensation value. 357 */ getCropRotateScale(@onNull Context ctx, @NonNull String packageName, @Nullable TaskInfo taskInfo, int displayRotation, int lensFacing, boolean ignoreResizableAndSdkCheck)358 public static int getCropRotateScale(@NonNull Context ctx, @NonNull String packageName, 359 @Nullable TaskInfo taskInfo, int displayRotation, int lensFacing, 360 boolean ignoreResizableAndSdkCheck) { 361 if (taskInfo == null) { 362 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 363 } 364 365 // External cameras do not need crop-rotate-scale. 366 if (lensFacing != CameraMetadata.LENS_FACING_FRONT 367 && lensFacing != CameraMetadata.LENS_FACING_BACK) { 368 Log.v(TAG, "lensFacing=" + lensFacing + ". Crop-rotate-scale is disabled."); 369 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 370 } 371 372 // In case the activity behavior is not explicitly overridden, enable the 373 // crop-rotate-scale workaround if the app targets M (or below) or is not 374 // resizeable. 375 if (!ignoreResizableAndSdkCheck && !isMOrBelow(ctx, packageName) && 376 taskInfo.isResizeable) { 377 Slog.v(TAG, 378 "The activity is N or above and claims to support resizeable-activity. " 379 + "Crop-rotate-scale is disabled."); 380 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 381 } 382 383 if (!taskInfo.isFixedOrientationPortrait && !taskInfo.isFixedOrientationLandscape) { 384 Log.v(TAG, "Non-fixed orientation activity. Crop-rotate-scale is disabled."); 385 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 386 } 387 388 int rotationDegree; 389 switch (displayRotation) { 390 case Surface.ROTATION_0: 391 rotationDegree = 0; 392 break; 393 case Surface.ROTATION_90: 394 rotationDegree = 90; 395 break; 396 case Surface.ROTATION_180: 397 rotationDegree = 180; 398 break; 399 case Surface.ROTATION_270: 400 rotationDegree = 270; 401 break; 402 default: 403 Log.e(TAG, "Unsupported display rotation: " + displayRotation); 404 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 405 } 406 407 Slog.v(TAG, 408 "Display.getRotation()=" + rotationDegree 409 + " isFixedOrientationPortrait=" + taskInfo.isFixedOrientationPortrait 410 + " isFixedOrientationLandscape=" + 411 taskInfo.isFixedOrientationLandscape); 412 // We are trying to estimate the necessary rotation compensation for clients that 413 // don't handle various display orientations. 414 // The logic that is missing on client side is similar to the reference code 415 // in {@link android.hardware.Camera#setDisplayOrientation} where "info.orientation" 416 // is already applied in "CameraUtils::getRotationTransform". 417 // Care should be taken to reverse the rotation direction depending on the camera 418 // lens facing. 419 if (rotationDegree == 0) { 420 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 421 } 422 if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) { 423 // Switch direction for front facing cameras 424 rotationDegree = 360 - rotationDegree; 425 } 426 427 switch (rotationDegree) { 428 case 90: 429 return CaptureRequest.SCALER_ROTATE_AND_CROP_90; 430 case 270: 431 return CaptureRequest.SCALER_ROTATE_AND_CROP_270; 432 case 180: 433 return CaptureRequest.SCALER_ROTATE_AND_CROP_180; 434 case 0: 435 default: 436 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 437 } 438 } 439 440 private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() { 441 @Override 442 public int getRotateAndCropOverride(String packageName, int lensFacing, int userId) { 443 if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { 444 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + 445 " camera service UID!"); 446 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 447 } 448 449 TaskInfo taskInfo = null; 450 ParceledListSlice<ActivityManager.RecentTaskInfo> recentTasks = null; 451 452 try { 453 recentTasks = ActivityTaskManager.getService().getRecentTasks(/*maxNum*/1, 454 /*flags*/ 0, userId); 455 } catch (RemoteException e) { 456 Log.e(TAG, "Failed to query recent tasks!"); 457 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 458 } 459 460 if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) { 461 ActivityManager.RecentTaskInfo task = recentTasks.getList().get(0); 462 if (packageName.equals(task.topActivityInfo.packageName)) { 463 taskInfo = new TaskInfo(); 464 taskInfo.frontTaskId = task.taskId; 465 taskInfo.isResizeable = 466 (task.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE); 467 taskInfo.displayId = task.displayId; 468 taskInfo.userId = task.userId; 469 taskInfo.isFixedOrientationLandscape = 470 ActivityInfo.isFixedOrientationLandscape( 471 task.topActivityInfo.screenOrientation); 472 taskInfo.isFixedOrientationPortrait = 473 ActivityInfo.isFixedOrientationPortrait( 474 task.topActivityInfo.screenOrientation); 475 } else { 476 Log.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName 477 + " doesn't match with camera client package name: " + packageName); 478 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 479 } 480 } else { 481 Log.e(TAG, "Recent task list is empty!"); 482 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 483 } 484 485 // TODO: Modify the sensor orientation in camera characteristics along with any 3A 486 // regions in capture requests/results to account for thea physical rotation. The 487 // former is somewhat tricky as it assumes that camera clients always check for the 488 // current value by retrieving the camera characteristics from the camera device. 489 if ((taskInfo != null) && (CompatChanges.isChangeEnabled( 490 OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName, 491 UserHandle.getUserHandleForUid(taskInfo.userId)))) { 492 Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS enabled!"); 493 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 494 } 495 boolean ignoreResizableAndSdkCheck = false; 496 if ((taskInfo != null) && (CompatChanges.isChangeEnabled( 497 OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK, packageName, 498 UserHandle.getUserHandleForUid(taskInfo.userId)))) { 499 Slog.v(TAG, "OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK enabled!"); 500 ignoreResizableAndSdkCheck = true; 501 } 502 503 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 504 int displayRotation; 505 if (displayManager != null) { 506 Display display = displayManager.getDisplay(taskInfo.displayId); 507 if (display == null) { 508 Slog.e(TAG, "Invalid display id: " + taskInfo.displayId); 509 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 510 } 511 512 displayRotation = display.getRotation(); 513 } else { 514 Slog.e(TAG, "Failed to query display manager!"); 515 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE; 516 } 517 518 return getCropRotateScale(mContext, packageName, taskInfo, displayRotation, 519 lensFacing, ignoreResizableAndSdkCheck); 520 } 521 522 @Override 523 public void pingForUserUpdate() { 524 if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { 525 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + 526 " camera service UID!"); 527 return; 528 } 529 notifySwitchWithRetries(RETRY_TIMES); 530 notifyDeviceStateWithRetries(RETRY_TIMES); 531 } 532 533 @Override 534 public void notifyCameraState(CameraSessionStats cameraState) { 535 if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { 536 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + 537 " camera service UID!"); 538 return; 539 } 540 String state = cameraStateToString(cameraState.getNewCameraState()); 541 String facingStr = cameraFacingToString(cameraState.getFacing()); 542 if (DEBUG) { 543 Slog.v(TAG, "Camera " + cameraState.getCameraId() 544 + " facing " + facingStr + " state now " + state 545 + " for client " + cameraState.getClientName() 546 + " API Level " + cameraState.getApiLevel()); 547 } 548 549 updateActivityCount(cameraState); 550 } 551 }; 552 553 private final FoldStateListener mFoldStateListener; 554 CameraServiceProxy(Context context)555 public CameraServiceProxy(Context context) { 556 super(context); 557 mContext = context; 558 mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false); 559 mHandlerThread.start(); 560 mHandler = new Handler(mHandlerThread.getLooper(), this); 561 562 mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0; 563 if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled")); 564 // Don't keep any extra logging threads if not needed 565 mLogWriterService.setKeepAliveTime(1, TimeUnit.SECONDS); 566 mLogWriterService.allowCoreThreadTimeOut(true); 567 568 mFoldStateListener = new FoldStateListener(mContext, folded -> { 569 if (folded) { 570 setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED); 571 } else { 572 clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED); 573 } 574 }); 575 } 576 577 /** 578 * Sets the device state bits set within {@code deviceStateFlags} leaving all other bits the 579 * same. 580 * <p> 581 * Calling requires permission {@link android.Manifest.permission#CAMERA_SEND_SYSTEM_EVENTS}. 582 * 583 * @param deviceStateFlags a bitmask of the device state bits that should be set. 584 * 585 * @see #clearDeviceStateFlags(int) 586 */ setDeviceStateFlags(@eviceStateFlags int deviceStateFlags)587 private void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) { 588 synchronized (mLock) { 589 mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE); 590 mDeviceState |= deviceStateFlags; 591 if (mDeviceState != mLastReportedDeviceState) { 592 notifyDeviceStateWithRetriesLocked(RETRY_TIMES); 593 } 594 } 595 } 596 597 /** 598 * Clears the device state bits set within {@code deviceStateFlags} leaving all other bits the 599 * same. 600 * <p> 601 * Calling requires permission {@link android.Manifest.permission#CAMERA_SEND_SYSTEM_EVENTS}. 602 * 603 * @param deviceStateFlags a bitmask of the device state bits that should be cleared. 604 * 605 * @see #setDeviceStateFlags(int) 606 */ clearDeviceStateFlags(@eviceStateFlags int deviceStateFlags)607 private void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) { 608 synchronized (mLock) { 609 mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE); 610 mDeviceState &= ~deviceStateFlags; 611 if (mDeviceState != mLastReportedDeviceState) { 612 notifyDeviceStateWithRetriesLocked(RETRY_TIMES); 613 } 614 } 615 } 616 617 @Override handleMessage(Message msg)618 public boolean handleMessage(Message msg) { 619 switch(msg.what) { 620 case MSG_SWITCH_USER: { 621 notifySwitchWithRetries(msg.arg1); 622 } break; 623 case MSG_NOTIFY_DEVICE_STATE: { 624 notifyDeviceStateWithRetries(msg.arg1); 625 } break; 626 default: { 627 Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what); 628 } break; 629 } 630 return true; 631 } 632 633 @Override onStart()634 public void onStart() { 635 mUserManager = UserManager.get(mContext); 636 if (mUserManager == null) { 637 // Should never see this unless someone messes up the SystemServer service boot order. 638 throw new IllegalStateException("UserManagerService must start before" + 639 " CameraServiceProxy!"); 640 } 641 642 IntentFilter filter = new IntentFilter(); 643 filter.addAction(Intent.ACTION_USER_ADDED); 644 filter.addAction(Intent.ACTION_USER_REMOVED); 645 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 646 filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); 647 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); 648 mContext.registerReceiver(mIntentReceiver, filter); 649 650 publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy); 651 publishLocalService(CameraServiceProxy.class, this); 652 } 653 654 @Override onBootPhase(int phase)655 public void onBootPhase(int phase) { 656 if (phase == PHASE_BOOT_COMPLETED) { 657 CameraStatsJobService.schedule(mContext); 658 659 try { 660 int[] displayIds = WindowManagerGlobal.getWindowManagerService() 661 .registerDisplayWindowListener(mDisplayWindowListener); 662 for (int i = 0; i < displayIds.length; i++) { 663 mDisplayWindowListener.onDisplayAdded(displayIds[i]); 664 } 665 } catch (RemoteException e) { 666 Log.e(TAG, "Failed to register display window listener!"); 667 } 668 669 mContext.getSystemService(DeviceStateManager.class) 670 .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener); 671 } 672 } 673 674 @Override onUserStarting(@onNull TargetUser user)675 public void onUserStarting(@NonNull TargetUser user) { 676 synchronized(mLock) { 677 if (mEnabledCameraUsers == null) { 678 // Initialize cameraserver, or update cameraserver if we are recovering 679 // from a crash. 680 switchUserLocked(user.getUserIdentifier()); 681 } 682 } 683 } 684 685 @Override onUserSwitching(@ullable TargetUser from, @NonNull TargetUser to)686 public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { 687 synchronized(mLock) { 688 switchUserLocked(to.getUserIdentifier()); 689 } 690 } 691 692 /** 693 * Handle the death of the native camera service 694 */ 695 @Override binderDied()696 public void binderDied() { 697 if (DEBUG) Slog.w(TAG, "Native camera service has died"); 698 synchronized(mLock) { 699 mCameraServiceRaw = null; 700 701 // All cameras reset to idle on camera service death 702 boolean wasEmpty = mActiveCameraUsage.isEmpty(); 703 mActiveCameraUsage.clear(); 704 705 if ( mNotifyNfc && !wasEmpty ) { 706 notifyNfcService(/*enablePolling*/ true); 707 } 708 } 709 } 710 711 private class EventWriterTask implements Runnable { 712 private ArrayList<CameraUsageEvent> mEventList; 713 private static final long WRITER_SLEEP_MS = 100; 714 EventWriterTask(ArrayList<CameraUsageEvent> eventList)715 public EventWriterTask(ArrayList<CameraUsageEvent> eventList) { 716 mEventList = eventList; 717 } 718 719 @Override run()720 public void run() { 721 if (mEventList != null) { 722 for (CameraUsageEvent event : mEventList) { 723 logCameraUsageEvent(event); 724 try { 725 Thread.sleep(WRITER_SLEEP_MS); 726 } catch (InterruptedException e) {} 727 } 728 mEventList.clear(); 729 } 730 } 731 732 /** 733 * Write camera usage events to stats log. 734 * Package-private 735 */ logCameraUsageEvent(CameraUsageEvent e)736 private void logCameraUsageEvent(CameraUsageEvent e) { 737 int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN; 738 switch(e.mCameraFacing) { 739 case CameraSessionStats.CAMERA_FACING_BACK: 740 facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK; 741 break; 742 case CameraSessionStats.CAMERA_FACING_FRONT: 743 facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT; 744 break; 745 case CameraSessionStats.CAMERA_FACING_EXTERNAL: 746 facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL; 747 break; 748 default: 749 Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing); 750 } 751 752 int streamCount = 0; 753 if (e.mStreamStats != null) { 754 streamCount = e.mStreamStats.size(); 755 } 756 if (CameraServiceProxy.DEBUG) { 757 Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction 758 + " clientName " + e.mClientName 759 + ", duration " + e.getDuration() 760 + ", APILevel " + e.mAPILevel 761 + ", cameraId " + e.mCameraId 762 + ", facing " + facing 763 + ", isNdk " + e.mIsNdk 764 + ", latencyMs " + e.mLatencyMs 765 + ", operatingMode " + e.mOperatingMode 766 + ", internalReconfigure " + e.mInternalReconfigure 767 + ", requestCount " + e.mRequestCount 768 + ", resultErrorCount " + e.mResultErrorCount 769 + ", deviceError " + e.mDeviceError 770 + ", streamCount is " + streamCount); 771 } 772 // Convert from CameraStreamStats to CameraStreamProto 773 CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; 774 for (int i = 0; i < MAX_STREAM_STATISTICS; i++) { 775 streamProtos[i] = new CameraStreamProto(); 776 if (i < streamCount) { 777 CameraStreamStats streamStats = e.mStreamStats.get(i); 778 streamProtos[i].width = streamStats.getWidth(); 779 streamProtos[i].height = streamStats.getHeight(); 780 streamProtos[i].format = streamStats.getFormat(); 781 streamProtos[i].dataSpace = streamStats.getDataSpace(); 782 streamProtos[i].usage = streamStats.getUsage(); 783 streamProtos[i].requestCount = streamStats.getRequestCount(); 784 streamProtos[i].errorCount = streamStats.getErrorCount(); 785 streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); 786 streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); 787 streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); 788 streamProtos[i].histogramType = streamStats.getHistogramType(); 789 streamProtos[i].histogramBins = streamStats.getHistogramBins(); 790 streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); 791 792 if (CameraServiceProxy.DEBUG) { 793 String histogramTypeName = 794 cameraHistogramTypeToString(streamProtos[i].histogramType); 795 Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width 796 + ", height " + streamProtos[i].height 797 + ", format " + streamProtos[i].format 798 + ", dataSpace " + streamProtos[i].dataSpace 799 + ", usage " + streamProtos[i].usage 800 + ", requestCount " + streamProtos[i].requestCount 801 + ", errorCount " + streamProtos[i].errorCount 802 + ", firstCaptureLatencyMillis " 803 + streamProtos[i].firstCaptureLatencyMillis 804 + ", maxHalBuffers " + streamProtos[i].maxHalBuffers 805 + ", maxAppBuffers " + streamProtos[i].maxAppBuffers 806 + ", histogramType " + histogramTypeName 807 + ", histogramBins " 808 + Arrays.toString(streamProtos[i].histogramBins) 809 + ", histogramCounts " 810 + Arrays.toString(streamProtos[i].histogramCounts)); 811 } 812 } 813 } 814 FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(), 815 e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk, 816 e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure, 817 e.mRequestCount, e.mResultErrorCount, e.mDeviceError, 818 streamCount, MessageNano.toByteArray(streamProtos[0]), 819 MessageNano.toByteArray(streamProtos[1]), 820 MessageNano.toByteArray(streamProtos[2]), 821 MessageNano.toByteArray(streamProtos[3]), 822 MessageNano.toByteArray(streamProtos[4])); 823 } 824 } 825 826 /** 827 * Dump camera usage events to log. 828 * Package-private 829 */ dumpUsageEvents()830 void dumpUsageEvents() { 831 synchronized(mLock) { 832 // Randomize order of events so that it's not meaningful 833 Collections.shuffle(mCameraUsageHistory); 834 mLogWriterService.execute(new EventWriterTask( 835 new ArrayList<CameraUsageEvent>(mCameraUsageHistory))); 836 837 mCameraUsageHistory.clear(); 838 } 839 final long ident = Binder.clearCallingIdentity(); 840 try { 841 CameraStatsJobService.schedule(mContext); 842 } finally { 843 Binder.restoreCallingIdentity(ident); 844 } 845 } 846 847 @Nullable getCameraServiceRawLocked()848 private ICameraService getCameraServiceRawLocked() { 849 if (mCameraServiceRaw == null) { 850 IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME); 851 if (cameraServiceBinder == null) { 852 return null; 853 } 854 try { 855 cameraServiceBinder.linkToDeath(this, /*flags*/ 0); 856 } catch (RemoteException e) { 857 Slog.w(TAG, "Could not link to death of native camera service"); 858 return null; 859 } 860 861 mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder); 862 } 863 return mCameraServiceRaw; 864 } 865 switchUserLocked(int userHandle)866 private void switchUserLocked(int userHandle) { 867 Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle); 868 mLastUser = userHandle; 869 if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) { 870 // Some user handles have been added or removed, update cameraserver. 871 mEnabledCameraUsers = currentUserHandles; 872 notifySwitchWithRetriesLocked(RETRY_TIMES); 873 } 874 } 875 getEnabledUserHandles(int currentUserHandle)876 private Set<Integer> getEnabledUserHandles(int currentUserHandle) { 877 int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle); 878 Set<Integer> handles = new ArraySet<>(userProfiles.length); 879 880 for (int id : userProfiles) { 881 handles.add(id); 882 } 883 884 return handles; 885 } 886 notifySwitchWithRetries(int retries)887 private void notifySwitchWithRetries(int retries) { 888 synchronized(mLock) { 889 notifySwitchWithRetriesLocked(retries); 890 } 891 } 892 notifySwitchWithRetriesLocked(int retries)893 private void notifySwitchWithRetriesLocked(int retries) { 894 if (mEnabledCameraUsers == null) { 895 return; 896 } 897 if (notifyCameraserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) { 898 retries = 0; 899 } 900 if (retries <= 0) { 901 return; 902 } 903 Slog.i(TAG, "Could not notify camera service of user switch, retrying..."); 904 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWITCH_USER, retries - 1, 0, null), 905 RETRY_DELAY_TIME); 906 } 907 notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles)908 private boolean notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles) { 909 // Forward the user switch event to the native camera service running in the cameraserver 910 // process. 911 ICameraService cameraService = getCameraServiceRawLocked(); 912 if (cameraService == null) { 913 Slog.w(TAG, "Could not notify cameraserver, camera service not available."); 914 return false; 915 } 916 917 try { 918 mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles)); 919 } catch (RemoteException e) { 920 Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e); 921 // Not much we can do if camera service is dead. 922 return false; 923 } 924 return true; 925 } 926 notifyDeviceStateWithRetries(int retries)927 private void notifyDeviceStateWithRetries(int retries) { 928 synchronized (mLock) { 929 notifyDeviceStateWithRetriesLocked(retries); 930 } 931 } 932 notifyDeviceStateWithRetriesLocked(int retries)933 private void notifyDeviceStateWithRetriesLocked(int retries) { 934 if (notifyDeviceStateChangeLocked(mDeviceState)) { 935 return; 936 } 937 if (retries <= 0) { 938 return; 939 } 940 Slog.i(TAG, "Could not notify camera service of device state change, retrying..."); 941 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_NOTIFY_DEVICE_STATE, retries - 1, 942 0, null), RETRY_DELAY_TIME); 943 } 944 notifyDeviceStateChangeLocked(@eviceStateFlags int deviceState)945 private boolean notifyDeviceStateChangeLocked(@DeviceStateFlags int deviceState) { 946 // Forward the state to the native camera service running in the cameraserver process. 947 ICameraService cameraService = getCameraServiceRawLocked(); 948 if (cameraService == null) { 949 Slog.w(TAG, "Could not notify cameraserver, camera service not available."); 950 return false; 951 } 952 953 try { 954 mCameraServiceRaw.notifyDeviceStateChange(deviceState); 955 } catch (RemoteException e) { 956 Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e); 957 // Not much we can do if camera service is dead. 958 return false; 959 } 960 mLastReportedDeviceState = deviceState; 961 return true; 962 } 963 updateActivityCount(CameraSessionStats cameraState)964 private void updateActivityCount(CameraSessionStats cameraState) { 965 String cameraId = cameraState.getCameraId(); 966 int newCameraState = cameraState.getNewCameraState(); 967 int facing = cameraState.getFacing(); 968 String clientName = cameraState.getClientName(); 969 int apiLevel = cameraState.getApiLevel(); 970 boolean isNdk = cameraState.isNdk(); 971 int sessionType = cameraState.getSessionType(); 972 int internalReconfigureCount = cameraState.getInternalReconfigureCount(); 973 int latencyMs = cameraState.getLatencyMs(); 974 long requestCount = cameraState.getRequestCount(); 975 long resultErrorCount = cameraState.getResultErrorCount(); 976 boolean deviceError = cameraState.getDeviceErrorFlag(); 977 List<CameraStreamStats> streamStats = cameraState.getStreamStats(); 978 synchronized(mLock) { 979 // Update active camera list and notify NFC if necessary 980 boolean wasEmpty = mActiveCameraUsage.isEmpty(); 981 switch (newCameraState) { 982 case CameraSessionStats.CAMERA_STATE_OPEN: 983 // Notify the audio subsystem about the facing of the most-recently opened 984 // camera This can be used to select the best audio tuning in case video 985 // recording with that camera will happen. Since only open events are used, if 986 // multiple cameras are opened at once, the one opened last will be used to 987 // select audio tuning. 988 AudioManager audioManager = getContext().getSystemService(AudioManager.class); 989 if (audioManager != null) { 990 // Map external to front for audio tuning purposes 991 String facingStr = (facing == CameraSessionStats.CAMERA_FACING_BACK) ? 992 "back" : "front"; 993 String facingParameter = "cameraFacing=" + facingStr; 994 audioManager.setParameters(facingParameter); 995 } 996 CameraUsageEvent openEvent = new CameraUsageEvent( 997 cameraId, facing, clientName, apiLevel, isNdk, 998 FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN, 999 latencyMs, sessionType); 1000 mCameraUsageHistory.add(openEvent); 1001 break; 1002 case CameraSessionStats.CAMERA_STATE_ACTIVE: 1003 // Check current active camera IDs to see if this package is already talking to 1004 // some camera 1005 boolean alreadyActivePackage = false; 1006 for (int i = 0; i < mActiveCameraUsage.size(); i++) { 1007 if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) { 1008 alreadyActivePackage = true; 1009 break; 1010 } 1011 } 1012 // If not already active, notify window manager about this new package using a 1013 // camera 1014 if (!alreadyActivePackage) { 1015 WindowManagerInternal wmi = 1016 LocalServices.getService(WindowManagerInternal.class); 1017 wmi.addNonHighRefreshRatePackage(clientName); 1018 } 1019 1020 // Update activity events 1021 CameraUsageEvent newEvent = new CameraUsageEvent( 1022 cameraId, facing, clientName, apiLevel, isNdk, 1023 FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__SESSION, 1024 latencyMs, sessionType); 1025 CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent); 1026 if (oldEvent != null) { 1027 Slog.w(TAG, "Camera " + cameraId + " was already marked as active"); 1028 oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0, 1029 /*resultErrorCount*/0, /*deviceError*/false, streamStats); 1030 mCameraUsageHistory.add(oldEvent); 1031 } 1032 break; 1033 case CameraSessionStats.CAMERA_STATE_IDLE: 1034 case CameraSessionStats.CAMERA_STATE_CLOSED: 1035 CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId); 1036 if (doneEvent != null) { 1037 1038 doneEvent.markCompleted(internalReconfigureCount, requestCount, 1039 resultErrorCount, deviceError, streamStats); 1040 mCameraUsageHistory.add(doneEvent); 1041 1042 // Check current active camera IDs to see if this package is still 1043 // talking to some camera 1044 boolean stillActivePackage = false; 1045 for (int i = 0; i < mActiveCameraUsage.size(); i++) { 1046 if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) { 1047 stillActivePackage = true; 1048 break; 1049 } 1050 } 1051 // If not longer active, notify window manager about this package being done 1052 // with camera 1053 if (!stillActivePackage) { 1054 WindowManagerInternal wmi = 1055 LocalServices.getService(WindowManagerInternal.class); 1056 wmi.removeNonHighRefreshRatePackage(clientName); 1057 } 1058 } 1059 1060 if (newCameraState == CameraSessionStats.CAMERA_STATE_CLOSED) { 1061 CameraUsageEvent closeEvent = new CameraUsageEvent( 1062 cameraId, facing, clientName, apiLevel, isNdk, 1063 FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE, 1064 latencyMs, sessionType); 1065 mCameraUsageHistory.add(closeEvent); 1066 } 1067 1068 if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) { 1069 dumpUsageEvents(); 1070 } 1071 1072 break; 1073 } 1074 boolean isEmpty = mActiveCameraUsage.isEmpty(); 1075 if ( mNotifyNfc && (wasEmpty != isEmpty) ) { 1076 notifyNfcService(isEmpty); 1077 } 1078 } 1079 } 1080 notifyNfcService(boolean enablePolling)1081 private void notifyNfcService(boolean enablePolling) { 1082 1083 IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME); 1084 if (nfcServiceBinder == null) { 1085 Slog.w(TAG, "Could not connect to NFC service to notify it of camera state"); 1086 return; 1087 } 1088 INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder); 1089 int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS; 1090 if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags); 1091 try { 1092 nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null); 1093 } catch (RemoteException e) { 1094 Slog.w(TAG, "Could not notify NFC service, remote exception: " + e); 1095 } 1096 } 1097 toArray(Collection<Integer> c)1098 private static int[] toArray(Collection<Integer> c) { 1099 int len = c.size(); 1100 int[] ret = new int[len]; 1101 int idx = 0; 1102 for (Integer i : c) { 1103 ret[idx++] = i; 1104 } 1105 return ret; 1106 } 1107 cameraStateToString(int newCameraState)1108 private static String cameraStateToString(int newCameraState) { 1109 switch (newCameraState) { 1110 case CameraSessionStats.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN"; 1111 case CameraSessionStats.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE"; 1112 case CameraSessionStats.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE"; 1113 case CameraSessionStats.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED"; 1114 default: break; 1115 } 1116 return "CAMERA_STATE_UNKNOWN"; 1117 } 1118 cameraFacingToString(int cameraFacing)1119 private static String cameraFacingToString(int cameraFacing) { 1120 switch (cameraFacing) { 1121 case CameraSessionStats.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK"; 1122 case CameraSessionStats.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT"; 1123 case CameraSessionStats.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL"; 1124 default: break; 1125 } 1126 return "CAMERA_FACING_UNKNOWN"; 1127 } 1128 cameraHistogramTypeToString(int cameraHistogramType)1129 private static String cameraHistogramTypeToString(int cameraHistogramType) { 1130 switch (cameraHistogramType) { 1131 case CameraStreamStats.HISTOGRAM_TYPE_CAPTURE_LATENCY: 1132 return "HISTOGRAM_TYPE_CAPTURE_LATENCY"; 1133 default: 1134 break; 1135 } 1136 return "HISTOGRAM_TYPE_UNKNOWN"; 1137 } 1138 1139 } 1140