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      */
CaptureSessionStatsCollector()55     public CaptureSessionStatsCollector() {
56         mUsageStatistics = UsageStatistics.instance();
57     }
58 
59     /**
60      * Constructor for testing/dependency injection
61      */
62     @VisibleForTesting
CaptureSessionStatsCollector(UsageStatistics usageStatistics)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      */
decorateAtTimeOfCaptureRequestAvailable(CaptureResultProxy captureResult)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      */
decorateAtTimeCaptureRequest( final int mode, final String filename, final boolean frontFacing, final boolean isHDR, final float zoom, final String flashSetting, final boolean gridLinesOn, final float timerSeconds, final TouchCoordinate touchCoordinate, final Boolean volumeButtonShutter, final Rect activeSensorSize )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      */
decorateAtTimeWriteToDisk( final ExifInterface exifInterface )139     public void decorateAtTimeWriteToDisk(
140             final ExifInterface exifInterface
141     ) {
142         mExifInterface = exifInterface;
143     }
144 
145     /**
146      * Called when image processing time begins.
147      */
markProcessingTimeStart()148     public void markProcessingTimeStart() {
149         mCaptureTimeMillis = getElapsedRealTime();
150     }
151 
152     /**
153      * Send capture event to the UsageStatistics singleton.
154      */
photoCaptureDoneEvent()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      */
isCompleteForPhotoCaptureEvent()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      */
isValidForPhotoCaptureEvent()188     public boolean isValidForPhotoCaptureEvent() {
189         return (mMode != null);
190     }
191 
192     /**
193      * Call to SystemClock.elapsedRealtime() that we can override for testing.
194      */
getElapsedRealTime()195     public long getElapsedRealTime() {
196         return SystemClock.elapsedRealtime();
197     }
198 
199 }
200