1 /*
2  * Copyright (C) 2023 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 
17 package android.hardware.camera2.utils;
18 
19 import android.annotation.NonNull;
20 import android.hardware.CameraExtensionSessionStats;
21 import android.hardware.camera2.CameraManager;
22 import android.util.Log;
23 
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors;
26 
27 /**
28  * Utility class to aggregate metrics specific to Camera Extensions and pass them to
29  * {@link CameraManager}. {@link android.hardware.camera2.CameraExtensionSession} should call
30  * {@link #commit} before closing the session.
31  *
32  * @hide
33  */
34 public class ExtensionSessionStatsAggregator {
35     private static final boolean DEBUG = false;
36     private static final String TAG = ExtensionSessionStatsAggregator.class.getSimpleName();
37 
38     private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
39 
40     private final Object mLock = new Object(); // synchronizes access to all fields of the class
41     private boolean mIsDone = false; // marks the aggregator as "done".
42                                      // Mutations and commits become no-op if this is true.
43     private final CameraExtensionSessionStats mStats;
44 
ExtensionSessionStatsAggregator(@onNull String cameraId, boolean isAdvanced)45     public ExtensionSessionStatsAggregator(@NonNull String cameraId, boolean isAdvanced) {
46         if (DEBUG) {
47             Log.v(TAG, "Creating new Extension Session Stats Aggregator");
48         }
49         mStats = new CameraExtensionSessionStats();
50         mStats.key = "";
51         mStats.cameraId = cameraId;
52         mStats.isAdvanced = isAdvanced;
53     }
54 
55     /**
56      * Set client package name
57      *
58      * @param clientName package name of the client that these stats are associated with.
59      */
setClientName(@onNull String clientName)60     public void setClientName(@NonNull String clientName) {
61         synchronized (mLock) {
62             if (mIsDone) {
63                 return;
64             }
65             if (DEBUG) {
66                 Log.v(TAG, "Setting clientName: " + clientName);
67             }
68             mStats.clientName = clientName;
69         }
70     }
71 
72     /**
73      * Set extension type.
74      *
75      * @param extensionType Type of extension. Must match one of
76      *                      {@code CameraExtensionCharacteristics#EXTENSION_*}
77      */
setExtensionType(int extensionType)78     public void setExtensionType(int extensionType) {
79         synchronized (mLock) {
80             if (mIsDone) {
81                 return;
82             }
83             if (DEBUG) {
84                 Log.v(TAG, "Setting type: " + extensionType);
85             }
86             mStats.type = extensionType;
87         }
88     }
89 
90     /**
91      * Asynchronously commits the stats to CameraManager on a background thread.
92      *
93      * @param isFinal marks the stats as final and prevents any further commits or changes. This
94      *                should be set to true when the stats are considered final for logging,
95      *                for example right before the capture session is about to close
96      */
commit(boolean isFinal)97     public void commit(boolean isFinal) {
98         // Call binder on a background thread to reduce latencies from metrics logging.
99         mExecutor.execute(() -> {
100             synchronized (mLock) {
101                 if (mIsDone) {
102                     return;
103                 }
104                 mIsDone = isFinal;
105                 if (DEBUG) {
106                     Log.v(TAG, "Committing: " + prettyPrintStats(mStats));
107                 }
108                 mStats.key = CameraManager.reportExtensionSessionStats(mStats);
109             }
110         });
111     }
112 
prettyPrintStats(@onNull CameraExtensionSessionStats stats)113     private static String prettyPrintStats(@NonNull CameraExtensionSessionStats stats) {
114         return CameraExtensionSessionStats.class.getSimpleName() + ":\n"
115                 + "  key: '" + stats.key + "'\n"
116                 + "  cameraId: '" + stats.cameraId + "'\n"
117                 + "  clientName: '" + stats.clientName + "'\n"
118                 + "  type: '" + stats.type + "'\n"
119                 + "  isAdvanced: '" + stats.isAdvanced + "'\n";
120     }
121 }
122