1 package com.android.camera.stats; 2 3 import android.graphics.Rect; 4 import android.hardware.camera2.CaptureResult; 5 import android.hardware.camera2.params.Face; 6 import android.os.SystemClock; 7 8 import com.google.common.annotations.VisibleForTesting; 9 10 import com.android.camera.exif.ExifInterface; 11 import com.android.camera.one.v2.camera2proxy.CaptureResultProxy; 12 import com.android.camera.ui.TouchCoordinate; 13 14 import java.util.ArrayList; 15 import java.util.List; 16 17 /** 18 * Accumulates statistics during the lifecycle of a Capture Session. Since a 19 * CaptureSession instance is available for the lifetime of the request and the 20 * image processing of the said request, CaptureSessionStatsCollector is 21 * attached to the CaptureSession so that we can collect information from both 22 * the CaptureModule and the ImageBackend. 23 */ 24 public class CaptureSessionStatsCollector { 25 26 27 /** Time when capture is completed in SystemClock.elapsedRealtime(). */ 28 protected long mCaptureTimeMillis; 29 protected final UsageStatistics mUsageStatistics; 30 31 // Define all fields as Objects so that we know whether they were set or not. 32 // A required field 33 protected Integer mMode; 34 35 // Fields with defaults, which are passed as primitives 36 protected Boolean mIsFrontFacing = Boolean.FALSE; 37 protected Boolean mIsHdr = Boolean.FALSE; 38 protected Float mZoom = new Float(0.0f); 39 40 // Optional fields (passed as Java Objects) 41 protected String mFilename; 42 protected ExifInterface mExifInterface; 43 protected String mFlashSetting; 44 protected Boolean mGridLinesOn; 45 protected Float mTimerSeconds; 46 protected TouchCoordinate mTouchCoordinate; 47 protected Boolean mVolumeButtonShutter; 48 protected List<Camera2FaceProxy> mFaceProxies; 49 protected Float mLensFocusDistance; 50 protected Rect mActiveSensorSize; 51 52 /** 53 * Constructor 54 */ 55 public CaptureSessionStatsCollector() { 56 mUsageStatistics = UsageStatistics.instance(); 57 } 58 59 /** 60 * Constructor for testing/dependency injection 61 */ 62 @VisibleForTesting 63 public CaptureSessionStatsCollector(UsageStatistics usageStatistics) { 64 mUsageStatistics = usageStatistics; 65 } 66 67 /** 68 * Decorate the collector when the CaptureResult becomes available, which happens sometime 69 * after picture is taken. In the current implementation, we query this structure for 70 * two fields: 1) CaptureResult.STATISTICS_FACES and 2) CaptureResult.LENS_FOCUS_DISTANCE 71 * 72 * @param captureResult CaptureResults to be queried for capture event information 73 */ 74 public void decorateAtTimeOfCaptureRequestAvailable(CaptureResultProxy captureResult) { 75 Face [] facesCaptured = captureResult.get(CaptureResult.STATISTICS_FACES); 76 if(facesCaptured == null) { 77 mFaceProxies = null; 78 } else { 79 mFaceProxies = new ArrayList<>(facesCaptured.length); 80 for (Face face : facesCaptured) { 81 mFaceProxies.add(Camera2FaceProxy.from(face)); 82 } 83 } 84 85 mLensFocusDistance = captureResult.get(CaptureResult.LENS_FOCUS_DISTANCE); 86 } 87 88 /** 89 * Accumulate the information that should be available at the time of the Capture Request. 90 * If you are unable to deliver one of these parameters, you may want to think again. 91 * 92 * @param mode a mode specified by eventprotos.NavigationChange.Mode 93 * @param filename filename of image to be created 94 * @param frontFacing whether the camera request is going to the front camera or not 95 * @param isHDR whether the camera is HDR mode 96 * @param zoom value of the zoom on the camera request 97 * @param flashSetting string representing the state of the flash (KEY_FLASH_MODE) 98 * @param gridLinesOn whether the gridlines are on the preview display 99 * @param timerSeconds value of the countdown timer 100 * @param touchCoordinate the last shutter touch coordinate 101 * @param volumeButtonShutter whether the volume button was used to initialize the request. 102 * @param activeSensorSize size of the active sensor array, to be used for the coordinate 103 * space of the face array 104 */ 105 public void decorateAtTimeCaptureRequest( 106 final int mode, 107 final String filename, 108 final boolean frontFacing, 109 final boolean isHDR, 110 final float zoom, 111 final String flashSetting, 112 final boolean gridLinesOn, 113 final float timerSeconds, 114 final TouchCoordinate touchCoordinate, 115 final Boolean volumeButtonShutter, 116 final Rect activeSensorSize 117 ) { 118 mMode = mode; 119 mFilename = filename; 120 mIsFrontFacing = frontFacing; 121 mIsHdr = isHDR; 122 mZoom = zoom; 123 mFlashSetting = flashSetting; 124 mGridLinesOn = gridLinesOn; 125 mTimerSeconds = timerSeconds; 126 mTouchCoordinate = touchCoordinate; 127 mVolumeButtonShutter = volumeButtonShutter; 128 mActiveSensorSize = activeSensorSize; 129 } 130 131 /** 132 * Accumalate the information that should be available at the time of 133 * Write-To-Disk. If you are unable to deliver one of these parameters, you 134 * may want to think again. 135 * 136 * @param exifInterface exif values to be associated with the JPEG image 137 * file that is being created. 138 */ 139 public void decorateAtTimeWriteToDisk( 140 final ExifInterface exifInterface 141 ) { 142 mExifInterface = exifInterface; 143 } 144 145 /** 146 * Called when image processing time begins. 147 */ 148 public void markProcessingTimeStart() { 149 mCaptureTimeMillis = getElapsedRealTime(); 150 } 151 152 /** 153 * Send capture event to the UsageStatistics singleton. 154 */ 155 public void photoCaptureDoneEvent() { 156 Float processingTime = (getElapsedRealTime() - mCaptureTimeMillis) / 1000f; 157 if (isValidForPhotoCaptureEvent()) { 158 mUsageStatistics.photoCaptureDoneEvent( 159 mMode, mFilename, mExifInterface, mIsFrontFacing, 160 mIsHdr, mZoom, mFlashSetting, mGridLinesOn, mTimerSeconds, 161 processingTime, mTouchCoordinate, mVolumeButtonShutter, 162 mFaceProxies, mLensFocusDistance, mActiveSensorSize); 163 } 164 } 165 166 /** 167 * Returns whether all the fields in the CaptureSessionStatsCollector are set or not. 168 */ 169 public boolean isCompleteForPhotoCaptureEvent() { 170 return (mMode != null) && 171 (mFilename != null) && 172 (mExifInterface != null) && 173 (mIsFrontFacing != null) && 174 (mIsHdr != null) && 175 (mZoom != null) && 176 (mFlashSetting != null) && 177 (mGridLinesOn != null) && 178 (mTimerSeconds != null) && 179 (mTouchCoordinate != null) && 180 (mVolumeButtonShutter != null); 181 } 182 183 /** 184 * Return whether state of collector is sufficient for PhotoCaptureEvent. 185 * 186 * @return whether state of collector is sufficient for PhotoCaptureEvent. 187 */ 188 public boolean isValidForPhotoCaptureEvent() { 189 return (mMode != null); 190 } 191 192 /** 193 * Call to SystemClock.elapsedRealtime() that we can override for testing. 194 */ 195 public long getElapsedRealTime() { 196 return SystemClock.elapsedRealtime(); 197 } 198 199 } 200