1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5library fuchsia.cobalt;
6
7using fuchsia.mem;
8
9// Cobalt is the Fuchsia service used to log, collect and analyze metrics.
10// The two main pillars of Cobalt are protecting user privacy and providing
11// high-quality, aggregate metrics to serve system and component software
12// developers' needs.
13//
14// This file contains interfaces that allow clients to log Events to
15// Cobalt.
16//
17// To use Cobalt, you must have a Project and one or more Metrics registered
18// with the Cobalt registration system. You must also register one or more
19// Reports in order to see the results of your logging aggregated over
20// all Fuchsia devices. Registration of Projects, Metrics and Reports consists
21// of entries in the YAML files in this repo:
22// https://cobalt-analytics.googlesource.com/config/.
23// In a Garnet checkout that is mapped to //third_party/cobalt_config.
24// Each registered object has an integer ID and those IDs are used as
25// parameters in the methods in this file.
26//
27// While Cobalt's code and registration files are open source, the running
28// system being managed by the Cobalt team is currently intended to be used by
29// software engineers at Google in order to collect metrics in a way that
30// preserves our users' privacy. If you are a Google software engineer
31// please see our internal [user guide](go/fuchsia-cobalt-userguide) or
32// ask for assistance from the Cobalt [team](go/fuchsia-cobalt#comms).
33//
34// Usage: First use LoggerFactory to get a Logger for your
35// project. Then you log Events using the Log*() methods.
36// Events are accumulated by the cobalt FIDL service and periodically
37// Observations, derived from the logged Events, are sent to the Cobalt server,
38// where they are used to generate Reports.
39
40// The maximum size of a single Event is 100 KB.
41const int64 MAX_BYTES_PER_EVENT = 102400;
42
43// Response codes for Logger operations.
44enum Status : int32 {
45    OK = 0;
46
47    // For example the supplied metric id is invalid.
48    INVALID_ARGUMENTS = 1;
49
50    // An attempt was made to log an Event whose serialized size exceeds
51    // MAX_BYTES_PER_EVENT.
52    EVENT_TOO_BIG = 2;
53
54    // Cobalt's local buffer is temporarily full and cannot handle any more
55    // Events at this time. Try again later. This condition should be rare
56    BUFFER_FULL = 3;
57
58    // Catch-all for unexpected errors.
59    INTERNAL_ERROR = -1;
60};
61
62// The release stage of a client's project is used to decide which
63// metrics are allowed to be collected.
64enum ReleaseStage : int32 {
65    // Only metrics targetting GA releases will be sent to the server.
66    GA = 0;
67
68    // Metrics targetting DOGFOOD and GA will be sent to the server.
69    DOGFOOD = 10;
70
71    // Metrics targetting FISHFOOD, DOGFOOD and GA will be sent to the server.
72    FISHFOOD = 20;
73
74    // All metrics will be sent to the server.
75    DEBUG = 99;
76};
77
78// A ProjectProfile is used to describe the client's Cobalt project
79struct ProjectProfile {
80    // The |config| Buffer contains the bytes of a serialized Cobalt config
81    // proto message. This specifies registered Metric and Report definitions
82    // for a single project.
83    fuchsia.mem.Buffer config;
84
85    // The release stage of the client's project.
86    ReleaseStage release_stage;
87};
88
89[Discoverable, Layout = "Simple"]
90// LoggerFactory creates Loggers.
91interface LoggerFactory {
92    // Creates a Logger for the given ProjectProfile.
93    //
94    // |profile| A ProjectProfile describing the Cobalt project that the
95    // returned Logger is for.
96    //
97    // |status| Returns OK on success or INVALID_ARGUMENTS if the project
98    // profile does not contain a valid Cobalt config with only a single
99    // project_id.
100    1: CreateLogger(ProjectProfile profile, request<Logger> logger)
101           -> (Status status);
102
103    // Creates a LoggerSimple for the given ProjectProfile.
104    //
105    // |profile| A ProjectProfile describing the Cobalt project that the
106    // returned Logger is for.
107    //
108    // |status| Returns OK on success or INVALID_ARGUMENTS if the project
109    // profile does not contain a valid Cobalt config with only a single
110    // project_id.
111    2: CreateLoggerSimple(ProjectProfile profile, request<LoggerSimple> logger)
112           -> (Status status);
113
114    // Creates a Logger for the project with the given name, using the state of
115    // the metrics registry that is bundled with Cobalt.
116    //
117    // WARNING: This version of CreateLogger does not allow the client to
118    // provide Cobalt with an updated version of the metrics registry. Therefore
119    // only metrics that were already registered at the time Cobalt was built
120    // may be used. This method may be appropriate in the case where it is known
121    // that new versions of the calling package will only be installed along
122    // with new versions of Cobalt. For other cases consider the method
123    // CreateLogger() above.
124    //
125    // |project_name| The name of the client's Cobalt project. (Only Cobalt v1.0
126    // projects are supported)
127    //
128    // |release_stage| The release stage for the client's project.
129    //
130    // |status| Returns OK on success or INVALID_ARGUMENTS if there is no
131    // project with the given name in the version of the metrics registry that
132    // is bundled with Cobalt.
133    3: CreateLoggerFromProjectName(string:64 project_name,
134                                   ReleaseStage release_stage,
135                                   request<Logger> logger)
136           -> (Status status);
137
138    // Creates a LoggerSimple for the project with the given name, using the
139    // state of the metrics registry that is bundled with Cobalt.
140    //
141    // WARNING: This version of CreateLoggerSimple does not allow the client to
142    // provide Cobalt with an updated version of the metrics registry. Therefore
143    // only metrics that were already registered at the time Cobalt was built
144    // may be used. This method may be appropriate in the case where it is known
145    // that new versions of the calling package will only be installed along
146    // with new versions of Cobalt. For other cases consider the method
147    // CreateLoggerSimple() above.
148    //
149    // |project_name| The name of the client's Cobalt project. (Only Cobalt v1.0
150    // projects are supported)
151    //
152    // |release_stage| The release stage for the client's project.
153    //
154    // |status| Returns OK on success or INVALID_ARGUMENTS if there is no
155    // project with the given name in the version of the metrics registry that
156    // is bundled with Cobalt.
157    4: CreateLoggerSimpleFromProjectName(string:64 project_name,
158                                         ReleaseStage release_stage,
159                                         request<LoggerSimple> logger)
160           -> (Status status);
161
162    // Creates a Logger for the project with the given name, using the state of
163    // the metrics registry that is bundled with Cobalt.
164    //
165    // WARNING: This version of CreateLogger does not allow the client to
166    // provide Cobalt with an updated version of the metrics registry. Therefore
167    // only metrics that were already registered at the time Cobalt was built
168    // may be used. This method may be appropriate in the case where it is known
169    // that new versions of the calling package will only be installed along
170    // with new versions of Cobalt. For other cases consider the method
171    // CreateLogger() above.
172    //
173    // |project_id| The id of the client's Cobalt project. (Only Cobalt v0.1
174    // projects are supported)
175    //
176    // |release_stage| The release stage for the client's project.
177    //
178    // |status| Returns OK on success or INVALID_ARGUMENTS if there is no
179    // project with the given name in the version of the metrics registry that
180    // is bundled with Cobalt.
181    5: CreateLoggerFromProjectId(uint32 project_id, ReleaseStage release_stage,
182                                 request<Logger> logger)
183           -> (Status status);
184
185    // Creates a LoggerSimple for the project with the given name, using the
186    // state of the metrics registry that is bundled with Cobalt.
187    //
188    // WARNING: This version of CreateLoggerSimple does not allow the client to
189    // provide Cobalt with an updated version of the metrics registry. Therefore
190    // only metrics that were already registered at the time Cobalt was built
191    // may be used. This method may be appropriate in the case where it is known
192    // that new versions of the calling package will only be installed along
193    // with new versions of Cobalt. For other cases consider the method
194    // CreateLoggerSimple() above.
195    //
196    // |project_id| The id of the client's Cobalt project. (Only Cobalt v0.1
197    // projects are supported)
198    //
199    // |release_stage| The release stage for the client's project.
200    //
201    // |status| Returns OK on success or INVALID_ARGUMENTS if there is no
202    // project with the given name in the version of the metrics registry that
203    // is bundled with Cobalt.
204    6: CreateLoggerSimpleFromProjectId(uint32 project_id,
205                                       ReleaseStage release_stage,
206                                       request<LoggerSimple> logger)
207           -> (Status status);
208};
209
210/////////////////////////////////////////////////////////////////////
211/// LoggerBase Interface
212/////////////////////////////////////////////////////////////////////
213
214// LoggerBase and its extensions are used to log Events to the Cobalt system.
215// The Cobalt FIDL service stores the Events locally for some period of time,
216// processes the Events to form Observations, and periodically uploads batches
217// of Observations to the Cobalt server. The Cobalt server processes the
218// Observations and generates Reports. See [TODO(rudominer)] for more
219// description of the Cobalt server and Reports.
220//
221// LoggerBase or one of its extensions is associated with a single Cobalt
222// project.
223//
224// This interface conforms to the Simple layout so that Simple bindings
225// may be generated for it. For the full interfaces, see Logger and LoggerSimple
226// below.
227[Layout = "Simple", FragileBase]
228interface LoggerBase {
229    // Logs the fact that an event has occurred.
230    //
231    // |metric_id| ID of the metric to use. It must be one of the Metrics
232    // from the ProjectProfile used to obtain this Logger, and it must be of
233    // type EVENT_OCCURRED.
234    //
235    // |event_code| The index of the event that occurred. The indexed set of all
236    // event codes and their labels is specified in the metric definition.
237    1: LogEvent(uint32 metric_id, uint32 event_code)
238           -> (Status status);
239
240    // Logs that an event has occurred a given number of times.
241    //
242    // |metric_id| ID of the metric to use. It must be one of the Metrics
243    // from the ProjectProfile used to obtain this Logger, and it must be of
244    // type EVENT_COUNT.
245    //
246    // |event_code| The index of the event that occurred. The indexed set of all
247    // event codes and their labels is specified in the metric definition.
248    //
249    // |component| Optionally, a component associated with the event may also be
250    // logged. Any notion of component that makes sense may be used or use the
251    // empty string if there is no natural notion of component.
252    //
253    // |period_duration_micros| Optionally, the period of time over which the
254    // |count| events occurred may be logged. If this is not relevant the value
255    // may be set to 0. Otherwise specify the period duration as a number of
256    // microseconds.
257    //
258    // |count| The number of times the event occurred. One may choose to always
259    // set this value to 1 and always set
260    //
261    // |period_duration_micros| to 0 in order to achieve a semantics similar to
262    // the LogEventOccurred() method, but with a |component|.
263    2: LogEventCount(uint32 metric_id, uint32 event_code, string:64 component,
264                     int64 period_duration_micros, int64 count)
265           -> (Status status);
266
267    // Logs that an event lasted a given amount of time.
268    //
269    // |metric_id| ID of the metric to use. It must be one of the Metrics
270    // from the ProjectProfile used to obtain this Logger, and it must be of
271    // type ELAPSED_TIME.
272    //
273    // |event_code| The index of the event that occurred. The indexed set of all
274    // event codes and their labels is specified in the metric definition.
275    //
276    // |component| Optionally, a component associated with the event may also be
277    // logged. Any notion of component that makes sense may be used or use the
278    // empty string if there is no natural notion of component.
279    //
280    // |elapsed_micros| The elapsed time of the event, specified as a number of
281    // microseconds.
282    3: LogElapsedTime(uint32 metric_id, uint32 event_code, string:64 component,
283                      int64 elapsed_micros)
284           -> (Status status);
285
286    // Logs a measured average frame rate.
287    //
288    // |metric_id| ID of the metric to use. It must be one of the Metrics
289    // from the ProjectProfile used to obtain this Logger, and it must be of
290    // type FRAME_RATE.
291    //
292    // |event_code| The index of the event that associated with the frame-rate
293    // measurement. The indexed set of all event codes and their labels is
294    // specified in the metric definition.
295    //
296    // |component| Optionally, a component associated with the frame-rate
297    // measurement may also be logged. Any notion of component that makes sense
298    // may be used or use the empty string if there is no natural notion of
299    // component.
300    //
301    // |fps| The average-frame rate in frames-per-second.
302    4: LogFrameRate(uint32 metric_id, uint32 event_code, string:64 component,
303                    float32 fps)
304           -> (Status status);
305
306    // Logs a measured memory usage.
307    //
308    // |metric_id| ID of the metric to use. It must be one of the Metrics
309    // from the ProjectProfile used to obtain this Logger, and it must be of
310    // type MEMORY_USAGE.
311    //
312    // |event_code| The index of the event type associated with the memory
313    // usage. The indexed set of all event codes and their labels is specified
314    // in the metric definition.
315    //
316    // |component| Optionally, a component associated with the memory usage may
317    // also be logged. Any notion of component that makes sense may be used or
318    // use the empty string if there is no natural notion of component.
319    //
320    // |bytes| The memory used, in bytes.
321    5: LogMemoryUsage(uint32 metric_id, uint32 event_code, string:64 component,
322                      int64 bytes)
323           -> (Status status);
324
325    // Logs the fact that a given string was used, in a specific context.
326    // The semantics of the context and the string is specified in the
327    // Metric definition.
328    //
329    //  This method is intended to be used in the following situation:
330    //  * The string s being logged does not contain PII or passwords.
331    //  * The set S of all possible strings that may be logged is large.
332    //    If the set S is small consider using LogEvent() instead.
333    //  * The ultimate data of interest is the statistical distribution of the
334    //    most commonly used strings from S over the population of all Fuchsia
335    //    devices.
336    //
337    // |metric_id| ID of the metric to use. It must be one of the Metrics
338    // from the ProjectProfile used to obtain this Logger, and it must be of
339    // type STRING_USED.
340    //
341    // |s| The string to log. This should be a human-readable string of size no
342    // more than 256 bytes.
343    6: LogString(uint32 metric_id, string:256 s) -> (Status status);
344
345    // This method is part of Cobalt's helper service for measuring the time
346    // delta between two events that occur in different processes. This starts
347    // the timer. A corresponding invocation of EndTimer() with the same
348    // |timer_id| ends the timer. After both StartTimer() and EnvdTimer() have
349    // been invoked, LogElapsedTime() will be invoked with the difference
350    // between the end timestamp and the start timestamp as the value of
351    // |duration_microseconds|. It is OK if Cobalt receives the EndTimer()
352    // call before the StartTimer() call.
353    //
354    // |metric_id| ID of the metric to use. It must be one of the Metrics
355    // from the ProjectProfile used to obtain this Logger, and it must be of
356    // type ELAPSED_TIME.
357    //
358    // |event_code| The index of the event type to associate with the elapsed
359    // time. This is passed to LogElapsedTime()
360    //
361    // |component| Optionally, a component associated with the event may also be
362    // logged. See the description at LogElapsedTime().
363    //
364    // |timer_id| The ID of the timer being started. This is an arbitrary
365    // non-empty string provided by the caller and it is the caller's
366    // responsibility to ensure that Cobalt receives a pair of StartTimer(),
367    // EndTimer() calls with this id before the timeout and without any
368    // intervening additional calls to StartTimer() or EndTimer() using the same
369    // id. Once such a pair is received Cobalt will delete the timer with this
370    // ID and after that the ID may be re-used.
371    //
372    // |timestamp| The timestamp to set as the start of the timer. The units
373    // must be microseconds. The absolute value does not matter, only the
374    // difference between the end and start timestamps will be used.
375    //
376    // |timeout_s| The number of seconds Cobalt should wait to receive the
377    // corresponding EndTimer() call with the same |timer_id|. If Cobalt has
378    // already received the corresponding EndTimer() call before receiving this
379    // StartTimer() call then this value is ignored as the timeout has already
380    // been set by the EndTimer() call. If Cobalt does not receive the
381    // corresponding EndTimer() call before the timeout then the timer will be
382    // deleted and this invocation of StartTimer() will be forgotten. Must be a
383    // positive value less than 300.
384    //
385    // |status| Returns OK on success. There are two success cases:
386    //     (i) Cobalt does not currently have any timers with the given
387    //         timer_id. In that case this call creates a new timer with
388    //         the given ID and start timestamp.
389    //     (ii) Cobalt currently has a timer with the given timer_id for
390    //         which it has received exactly one EndTimer() call and no
391    //         StartTimer() calls. In this case Cobalt will delete the
392    //         timer and invoke LogElapsedTime() using the difference
393    //         between the end timestamp and the start timestamp as the
394    //         value of |duration_micros|. It is ok if this value is
395    //         negative.
396    //     Returns INVALID_ARGUMENTS if |timer_id| is empty, the timeout
397    //        is not positive and less than 5 minutes or Cobalt currently
398    //        has a timer with the given timer_ID and it already has a start
399    //        timestamp. In the last case Cobalt will delete the timer with
400    //        the given |timer_id| and this invocation of StartTimer()
401    //        will be forgotten.
402    //     Any error returned by LogElapsedTime() may also be returned by this
403    //     method.
404    7: StartTimer(uint32 metric_id, uint32 event_code, string:64 component,
405                  string:64 timer_id, uint64 timestamp, uint32 timeout_s)
406           -> (Status status);
407
408    // This method is part of Cobalt's helper service for measuring the time
409    // delta between two events that occur in different processes. This ends
410    // the timer. A corresponding invocation of StartTimer() with the same
411    // |timer_id| starts the timer. After both StartTimer() and EndTimer() have
412    // been invoked, LogElapsedTime() will be invoked with the difference
413    // between the end timestamp and the start timestamp as the value of
414    // |duration_microseconds|. It is OK if Cobalt receives the EndTimer()
415    // call before the StartTimer() call.
416    //
417    // |timer_id| The ID of the timer being ended. This is an arbitrary
418    // non-empty string provided by the caller and it is the caller's
419    // responsibility to ensure that Cobalt receives a pair of StartTimer(),
420    // EndTimer() calls with this id before the timeout and without any
421    // intervening additional calls to StartTimer() or EndTimer() using the same
422    // id. Once such a pair is received Cobalt will delete the timer with this
423    // ID and after that the ID may be re-used.
424    //
425    // |timestamp| The timestamp to set as the end of the timer. The units must
426    // be microseconds. The absolute value does not matter, only the difference
427    // between the end and start timestamps will be used.
428    //
429    // |timeout_s| The number of seconds Cobalt should wait to receive the
430    // corresponding EndTimer() call with the same |timer_id|. If Cobalt has
431    // already received the corresponding EndTimer() call before receiving this
432    // StartTimer() call then this value is ignored as the timeout has already
433    // been set by the EndTimer() call. If Cobalt does not receive the
434    // corresponding EndTimer() call before the timeout then the timer will be
435    // deleted and this invocation of StartTimer() will be forgotten. Must be a
436    // positive value less than 300.
437    //
438    // |status| Returns OK on success. There are two success cases:
439    //     (i) Cobalt does not currently have any timers with the given
440    //         timer_id. In that case this call creates a new timer with
441    //         the given ID and end timestamp.
442    //     (ii) Cobalt currently has a timer with the given timer_id for
443    //         which it has received exactly one StartTimer() call and no
444    //         EndTimer() calls. In this case Cobalt will delete the
445    //         timer and invoke LogElapsedTime() using the difference
446    //         between the end timestamp and the start timestamp as the
447    //         value of |duration_micros|. It is ok if this value is
448    //         negative.
449    //     Returns INVALID_ARGUMENTS if |timer_id| is empty, the timeout
450    //        is not positive and less than 5 minutes or Cobalt currently
451    //        has a timer with the given timer_ID and it already has an end
452    //        timestamp. In the last case Cobalt will delete the timer with
453    //        the given |timer_id| and this invocation of EndTimer()
454    //        will be forgotten.
455    //     Any error returned by LogElapsedTime() may also be returned by this
456    //     method.
457    8: EndTimer(string:64 timer_id, uint64 timestamp, uint32 timeout_s)
458           -> (Status status);
459
460    // Method ordinals >= 100 are reserved for sub-interfaces.
461};
462
463/////////////////////////////////////////////////////////////////////
464/// Logger Interface
465/////////////////////////////////////////////////////////////////////
466
467// A value for a custom Event. This is used by the method LogCustomEvent().
468struct CustomEventValue {
469    // The name of the Metric dimension this value is for.
470    string dimension_name;
471
472    // The value for that dimension.
473    Value value;
474};
475
476// A value that may be a string, int, double, or index.
477union Value {
478    string string_value;
479    int64 int_value;
480    float64 double_value;
481    uint32 index_value;
482};
483
484// One bucket of histogram. This is used by the method LogIntHistogram().
485struct HistogramBucket {
486    // The index of the bucket. The meaning of the bucket is specified in the
487    // Metric definition.
488    uint32 index;
489
490    // The number of values in that bucket.
491    uint64 count;
492};
493
494// Logger is an extension of the LoggerBase interface that adds some additional
495// methods that do not naturally conform to the Simple layout. We opt for
496// a natural easy-to-understand interface at the cost of not being "Simple".
497// See the interface LoggerSimple below for versions of some of these methods
498// that do conform to the Simple layout.
499interface Logger : LoggerBase {
500    // Logs a histogram over a set of integer buckets. The meaning of the
501    // Metric and the buckets is specified in the Metric definition.
502    //
503    // This method is intended to be used in situations where the client
504    // wishes to aggregate a large number of integer-valued measurements
505    // *in-process*, prior to submitting the data to Cobalt.
506    // One reason a client may wish to do this is that the measurements occur
507    // with very high frequency and it is not practical to make a FIDL call
508    // for each individual measurement.
509    //
510    // |metric_id| ID of the metric to use. It must be one of the Metrics
511    // from the ProjectProfile used to obtain this Logger, and it must be of
512    // type INT_HISTOGRAM.
513    //
514    // |event_code| The index of the event type associated with the
515    // integer-valued measurement. The indexed set of all event codes and their
516    // labels is specified in the metric definition.
517    //
518    // |component| Optionally, a component associated with integer-valued
519    // measurements may also be logged. Any notion of component that makes sense
520    // may be used or use the empty string if there is no natural notion of
521    // component.
522    //
523    // |histogram| The histogram to log. Each HistogramBucket gives the count
524    // for one bucket of the histogram. The definitions of the buckets is given
525    // in the Metric definition.
526    100: LogIntHistogram(uint32 metric_id, uint32 event_code,
527                         string:64 component, vector<HistogramBucket> histogram)
528             -> (Status status);
529
530    // Logs a custom Event. The semantics of the Metric are specified in the
531    // Metric definition.
532    //
533    // |metric_id| ID of the metric to use. It must be one of the Metrics
534    // from the ProjectProfile used to obtain this Logger, and it must be of
535    // type CUSTOM.
536    //
537    // |event_values| The values for the custom Event. There is one value for
538    // each dimension of the Metric. The number and types of the values must
539    // be consistent with the dimensions declared in the Metric definition.
540    101: LogCustomEvent(uint32 metric_id, vector<CustomEventValue> event_values)
541             -> (Status status);
542};
543
544/////////////////////////////////////////////////////////////////////
545/// LoggerSimple Interface
546/////////////////////////////////////////////////////////////////////
547
548// LoggerSimple is an extension of the LoggerBase interface that adds some
549// additional methods intended to be used by lower-levels of the Fuchsia system.
550//
551// This interface conforms to the Simple layout so that Simple bindings
552// may be generated for it.
553[Layout = "Simple"]
554interface LoggerSimple : LoggerBase {
555    // Logs a histogram over a set of integer buckets. The meaning of the
556    // Metric and the buckets is specified in the Metric definition.
557    //
558    // See the method LogIntHistogram() in the Logger interface for more
559    // information. This method is similar except that it adheres to the
560    // requirements of Simple layout. Instead of a vector of HistogramBucekts
561    // this version takes two parallel vectors of bucket indices and the
562    // corresponding bucket counts.
563    100: LogIntHistogram(uint32 metric_id, uint32 event_code,
564                         string:64 component,
565                         vector<uint32>:100 bucket_indices,
566                         vector<uint64>:100 bucket_counts)
567             -> (Status status);
568};
569
570/////////////////////////////////////////////////////////////////////
571/// SystemProfileUpdater Interface
572/////////////////////////////////////////////////////////////////////
573
574// The state of a single experiment on a device or binary.
575struct Experiment {
576    // The id of the experiment as defined by the A/B Experiment framework.
577    uint64 experiment_id;
578    // The id of the experiment arm as defined by the A/B Experiment framework.
579    uint32 arm_id;
580};
581
582[Discoverable]
583// The SystemDataUpdater interface allows callers to update the state of
584// the System Data in Cobalt. This includes the SystemProfile and experiment
585// state. The changes are global and affect all loggers running on the device.
586interface SystemDataUpdater {
587    // Resets Cobalt's view of the system-wide experiment state and replaces it
588    // with the given values.
589    //
590    // |experiments|  All experiments the device has a notion of and the arms
591    // the device belongs to for each of them. These are the only experiments
592    // the device can collect data for.
593    1: SetExperimentState(vector<Experiment> experiments)
594           -> (Status status);
595};
596