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