1 /*
2  * Copyright (C) 2024 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 com.android.federatedcompute.services.common;
18 
19 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_COMPUTATION_COMPLETED;
20 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_COMPUTATION_ERROR_EXAMPLE_ITERATOR;
21 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_COMPUTATION_ERROR_INVALID_ARGUMENT;
22 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_COMPUTATION_ERROR_TENSORFLOW;
23 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_ERROR_INVALID_PAYLOAD;
24 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_PLAN_RECEIVED;
25 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_PLAN_URI_RECEIVED;
26 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_STARTED;
27 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_TURNED_AWAY;
28 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_FAILURE_UPLOADED;
29 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_FAILURE_UPLOAD_STARTED;
30 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_INITIATE_REPORT_RESULT_AUTH_SUCCEEDED;
31 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_KEY_ATTESTATION_SUCCEEDED;
32 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_NOT_STARTED;
33 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_REPORT_RESULT_UNAUTHORIZED;
34 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_RESULT_UPLOADED;
35 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_RESULT_UPLOAD_SERVER_ABORTED;
36 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_RESULT_UPLOAD_STARTED;
37 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_TASK_ASSIGNMENT_AUTH_SUCCEEDED;
38 import static com.android.federatedcompute.services.stats.FederatedComputeStatsLog.FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_TASK_ASSIGNMENT_UNAUTHORIZED;
39 
40 import com.android.federatedcompute.internal.util.LogUtil;
41 import com.android.federatedcompute.services.statsd.FederatedComputeStatsdLogger;
42 import com.android.federatedcompute.services.statsd.TrainingEventReported;
43 
44 /** The helper function to log {@link TrainingEventReported} in statsd. */
45 public class TrainingEventLogger {
46     private static final String TAG = TrainingEventLogger.class.getSimpleName();
47     private long mTaskId = 0;
48     private long mVersion = 0;
49     private long mPopulationId = 0;
50     private String mSdkPackageName = "";
51 
setTaskId(long taskId)52     public void setTaskId(long taskId) {
53         this.mTaskId = taskId;
54     }
55 
setClientVersion(long version)56     public void setClientVersion(long version) {
57         this.mVersion = version;
58     }
59 
setPopulationName(String populationName)60     public void setPopulationName(String populationName) {
61         this.mPopulationId = populationName.hashCode();
62     }
63 
setSdkPackageName(String sdkPackageName)64     public void setSdkPackageName(String sdkPackageName) {
65         this.mSdkPackageName = sdkPackageName;
66     }
67 
68     /** Logs when device doesn't start federated task like not meet training constraints. */
logTaskNotStarted()69     public void logTaskNotStarted() {
70         TrainingEventReported.Builder event =
71                 new TrainingEventReported.Builder()
72                         .setEventKind(
73                                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_NOT_STARTED);
74         logEvent(event);
75     }
76 
77     /** Logs when device checks in starts. */
logCheckinStarted()78     public void logCheckinStarted() {
79         TrainingEventReported.Builder event =
80                 new TrainingEventReported.Builder()
81                         .setEventKind(
82                                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_STARTED);
83         logEvent(event);
84     }
85 
86     /** Logs when device is turned away from federated training. */
logCheckinRejected(NetworkStats networkStats)87     public void logCheckinRejected(NetworkStats networkStats) {
88         logNetworkEvent(
89                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_TURNED_AWAY,
90                 networkStats);
91     }
92 
93     /**
94      * Logs when device checks in, gets task assignment, download plan model and plan is invalid.
95      */
logCheckinInvalidPayload(NetworkStats networkStats)96     public void logCheckinInvalidPayload(NetworkStats networkStats) {
97         logNetworkEvent(
98                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_ERROR_INVALID_PAYLOAD,
99                 networkStats);
100     }
101 
102     /**
103      * Logs when device checks in, gets task assignment and receive plan uri but not download yet.
104      */
logCheckinPlanUriReceived(NetworkStats networkStats)105     public void logCheckinPlanUriReceived(NetworkStats networkStats) {
106         logNetworkEvent(
107                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_PLAN_URI_RECEIVED,
108                 networkStats);
109     }
110 
111     /** Logs when device checks in, gets task assignment, download plan model and plan is valid. */
logCheckinFinished(NetworkStats networkStats)112     public void logCheckinFinished(NetworkStats networkStats) {
113         logNetworkEvent(
114                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_DOWNLOAD_PLAN_RECEIVED,
115                 networkStats);
116     }
117 
118     /** Logs when federated computation job fails with invalid argument reason. */
logComputationInvalidArgument(ExampleStats exampleStats)119     public void logComputationInvalidArgument(ExampleStats exampleStats) {
120         logEventWithExampleStats(
121                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_COMPUTATION_ERROR_INVALID_ARGUMENT,
122                 exampleStats);
123     }
124 
125     /** Logs when federated computation job fails due to example iterator. */
logComputationExampleIteratorError(ExampleStats exampleStats)126     public void logComputationExampleIteratorError(ExampleStats exampleStats) {
127         logEventWithExampleStats(
128                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_COMPUTATION_ERROR_EXAMPLE_ITERATOR,
129                 exampleStats);
130     }
131 
132     /**
133      * Logs when federated computation job fails due to tensorflow issue like unsupported
134      * operations, kernels.
135      */
logComputationTensorflowError(ExampleStats exampleStats)136     public void logComputationTensorflowError(ExampleStats exampleStats) {
137         logEventWithExampleStats(
138                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_COMPUTATION_ERROR_TENSORFLOW,
139                 exampleStats);
140     }
141 
142     /** Logs when federated computation job complete. */
logComputationCompleted(ExampleStats exampleStats, long durationInMs)143     public void logComputationCompleted(ExampleStats exampleStats, long durationInMs) {
144         TrainingEventReported.Builder event =
145                 new TrainingEventReported.Builder()
146                         .setEventKind(
147                                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_COMPUTATION_COMPLETED)
148                         .setExampleCount(exampleStats.mExampleCount.get())
149                         .setExampleSize(exampleStats.mExampleSizeBytes.get())
150                         .setExampleStoreBindLatencyNanos(
151                                 exampleStats.mBindToExampleStoreLatencyNanos.get())
152                         .setExampleStoreStartQueryLatencyNanos(
153                                 exampleStats.mStartQueryLatencyNanos.get())
154                         .setDurationInMillis(durationInMs);
155         logEvent(event);
156     }
157 
158     /** Log training event kind with duration. */
logEventWithDuration(int eventKind, long durationInMs)159     public void logEventWithDuration(int eventKind, long durationInMs) {
160         TrainingEventReported.Builder event =
161                 new TrainingEventReported.Builder()
162                         .setEventKind(eventKind)
163                         .setDurationInMillis(durationInMs);
164         logEvent(event);
165     }
166 
167     /** Logs training event kind with {@link ExampleStats}. */
logEventWithExampleStats(int eventKind, ExampleStats exampleStats)168     public void logEventWithExampleStats(int eventKind, ExampleStats exampleStats) {
169         TrainingEventReported.Builder event =
170                 new TrainingEventReported.Builder()
171                         .setEventKind(eventKind)
172                         .setExampleCount(exampleStats.mExampleCount.get())
173                         .setExampleSize(exampleStats.mExampleSizeBytes.get())
174                         .setExampleStoreBindLatencyNanos(
175                                 exampleStats.mBindToExampleStoreLatencyNanos.get())
176                         .setExampleStoreStartQueryLatencyNanos(
177                                 exampleStats.mStartQueryLatencyNanos.get());
178 
179         logEvent(event);
180     }
181 
182     /** Logs training event kind. */
logEventKind(int eventKind)183     public void logEventKind(int eventKind) {
184         TrainingEventReported.Builder event =
185                 new TrainingEventReported.Builder().setEventKind(eventKind);
186         logEvent(event);
187     }
188 
189     /** Logs when device starts to upload computation result. */
logResultUploadStarted()190     public void logResultUploadStarted() {
191         TrainingEventReported.Builder event =
192                 new TrainingEventReported.Builder()
193                         .setEventKind(
194                                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_RESULT_UPLOAD_STARTED);
195         logEvent(event);
196     }
197 
198     /** Logs when device uploads computation result but rejected by federated server. */
logResultUploadRejected(NetworkStats networkStats)199     public void logResultUploadRejected(NetworkStats networkStats) {
200         logNetworkEvent(
201                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_RESULT_UPLOAD_SERVER_ABORTED,
202                 networkStats);
203     }
204 
205     /** Logs when device uploads computation result completed. */
logResultUploadCompleted(NetworkStats networkStats)206     public void logResultUploadCompleted(NetworkStats networkStats) {
207         logNetworkEvent(
208                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_RESULT_UPLOADED,
209                 networkStats);
210     }
211 
212     /** Logs when device starts to upload failure computation result. */
logFailureResultUploadStarted()213     public void logFailureResultUploadStarted() {
214         TrainingEventReported.Builder event =
215                 new TrainingEventReported.Builder()
216                         .setEventKind(
217                                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_FAILURE_UPLOAD_STARTED);
218         logEvent(event);
219     }
220 
221     /** Logs when device finishes uploading failure computation result. */
logFailureResultUploadCompleted(NetworkStats networkStats)222     public void logFailureResultUploadCompleted(NetworkStats networkStats) {
223         logNetworkEvent(
224                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_FAILURE_UPLOADED,
225                 networkStats);
226     }
227 
logNetworkEvent(int eventKind, NetworkStats networkStats)228     private void logNetworkEvent(int eventKind, NetworkStats networkStats) {
229         TrainingEventReported.Builder event =
230                 new TrainingEventReported.Builder()
231                         .setEventKind(eventKind)
232                         .setDataTransferDurationMillis(
233                                 networkStats.getDataTransferDurationInMillis())
234                         .setBytesUploaded(networkStats.getTotalBytesUploaded())
235                         .setBytesDownloaded(networkStats.getTotalBytesDownloaded());
236         logEvent(event);
237     }
238 
239     /** Logs when devices are unauthorized to create task assignment. */
logTaskAssignmentUnauthorized()240     public void logTaskAssignmentUnauthorized() {
241         logKeyAttestationEvent(
242                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_TASK_ASSIGNMENT_UNAUTHORIZED);
243     }
244 
245     /** Logs when devices are successfully authenticated to create task assignment. */
logTaskAssignmentAuthSucceeded()246     public void logTaskAssignmentAuthSucceeded() {
247         logKeyAttestationEvent(
248                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_TASK_ASSIGNMENT_AUTH_SUCCEEDED);
249     }
250 
251     /** Logs when devices are not authorized to report result. */
logReportResultUnauthorized()252     public void logReportResultUnauthorized() {
253         logKeyAttestationEvent(
254                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_REPORT_RESULT_UNAUTHORIZED);
255     }
256 
257     /** Logs when devices are successfully authenticated to report result. */
logReportResultAuthSucceeded()258     public void logReportResultAuthSucceeded() {
259         logKeyAttestationEvent(
260                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_INITIATE_REPORT_RESULT_AUTH_SUCCEEDED);
261     }
262 
263     /** Logs the latency of calling key attestation on device */
logKeyAttestationLatencyEvent(long latencyMillis)264     public void logKeyAttestationLatencyEvent(long latencyMillis) {
265         TrainingEventReported.Builder event =
266                 new TrainingEventReported.Builder()
267                         .setKeyAttestationLatencyMillis(latencyMillis)
268                         .setEventKind(
269                                 FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED__KIND__TRAIN_KEY_ATTESTATION_SUCCEEDED);
270         logEvent(event);
271     }
272 
logKeyAttestationEvent(int eventKind)273     private void logKeyAttestationEvent(int eventKind) {
274         TrainingEventReported.Builder event =
275                 new TrainingEventReported.Builder().setEventKind(eventKind);
276         logEvent(event);
277     }
278 
logEvent(TrainingEventReported.Builder event)279     private void logEvent(TrainingEventReported.Builder event) {
280         if (mTaskId != 0) {
281             event.setTaskId(mTaskId);
282         }
283         if (mVersion != 0) {
284             event.setClientVersion(mVersion);
285         }
286         if (mPopulationId != 0) {
287             event.setPopulationId(mPopulationId);
288         }
289         if (mSdkPackageName != null && !mSdkPackageName.isBlank()) {
290             event.setSdkPackageName(mSdkPackageName);
291         }
292         TrainingEventReported trainingEvent = event.build();
293         LogUtil.d(
294                 TAG,
295                 "Log population id %d event kind %d, calling sdk package name: %s,"
296                         + " network upload %d download %d data transfer time %d"
297                         + " example stats %d key attestation stats %d"
298                         + " example store bind latency: %d"
299                         + " start query latency: %d",
300                 trainingEvent.getPopulationId(),
301                 trainingEvent.getEventKind(),
302                 trainingEvent.getSdkPackageName(),
303                 trainingEvent.getBytesUploaded(),
304                 trainingEvent.getBytesDownloaded(),
305                 trainingEvent.getDataTransferDurationMillis(),
306                 trainingEvent.getExampleCount(),
307                 trainingEvent.getKeyAttestationLatencyMillis(),
308                 trainingEvent.getExampleStoreBindLatencyNanos(),
309                 trainingEvent.getExampleStoreStartQueryLatencyNanos());
310         FederatedComputeStatsdLogger.getInstance().logTrainingEventReported(trainingEvent);
311     }
312 
313     /** Generate task id for logging purpose because we can't log string field in WW. */
getTaskIdForLogging(String populationName, String taskId)314     public static long getTaskIdForLogging(String populationName, String taskId) {
315         String taskName = populationName + "/" + taskId;
316         return taskName.hashCode();
317     }
318 }
319