xref: /aosp_15_r20/external/android_onboarding/java/com/android/onboarding/nodes/OnboardingEvent.kt (revision c625018464ae97c56936c82b1b617e11aa899faa)
1 package com.android.onboarding.nodes
2 
3 import com.android.onboarding.OnboardingProtos
4 import com.android.onboarding.contracts.annotations.OnboardingNode
5 import com.google.common.io.BaseEncoding
6 import java.time.Instant
7 
8 /** Possible Events. */
9 sealed interface OnboardingEvent : OnboardingGraphLog.OnboardingEventDelegate {
10   override val nodeName: String
11   override val nodeComponent: String
12   override val nodeId: Long
13   override val timestamp: Instant
14 
15   // LINT.IfChange
16   override val source: OnboardingEvent
17     get() = this
18 
19   /** Writes this event to a string which can be parsed by [OnboardingEvent.deserialize]. */
serializeToStringnull20   fun serializeToString(): String {
21     return BaseEncoding.base64().encode(serialize().toByteArray())
22   }
23 
serializenull24   fun serialize(): OnboardingProtos.LogProto
25 
26   /** Marker interface for events containing an intent data. */
27   interface WithIntent {
28     /** An intent related to this event. */
29     val intent: IntentData?
30   }
31 
32   /** Marker interface for events containing a result. */
33   interface WithResult {
34     /** A result produced by the target node. */
35     val result: Any?
36   }
37 
38   /** Marker interface for events containing an argument. */
39   interface WithArgument {
40     /** The argument supplied for launching target node with. */
41     val argument: Any?
42   }
43 
44   /** Marker interface for events containing a source node id. */
45   interface WithSource {
46     /** The id of the source node being interacted with. */
47     val sourceNodeId: Long
48   }
49 
50   /** An [OnboardingEvent] spawned only from contracts implemented by activities. */
51   sealed interface OnboardingActivityEvent : OnboardingEvent
52 
53   /**
54    * At [timestamp], a target [android.app.Activity] with id [nodeId] has been launched from
55    * [sourceNodeId] by invoking the [nodeName] contract from inside the [sourceNodeId] node with
56    * [argument] without expecting a result. This implies that [sourceNodeId] is finishing and does
57    * not expect to be returned to.
58    *
59    * Spawned at [sourceNodeId].
60    *
61    * @property nodeId target node id.
62    * @property nodeName target node name.
63    * @property nodeComponent target node component.
64    * @property sourceNodeId source node id.
65    */
66   data class ActivityNodeExecutedDirectly(
67     override val nodeName: String,
68     override val nodeComponent: String,
69     override val sourceNodeId: Long,
70     override val nodeId: Long,
71     override val argument: Any?,
72     override val timestamp: Instant = Instant.now(),
73   ) : OnboardingActivityEvent, WithArgument, WithSource {
74     constructor(
75       sourceNodeId: Long,
76       nodeId: Long,
77       nodeClass: Class<*>,
78       argument: Any? = null,
79       timestamp: Instant = Instant.now(),
80     ) : this(
81       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
82       sourceNodeId = sourceNodeId,
83       nodeId = nodeId,
84       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
85       argument = argument,
86       timestamp = timestamp,
87     )
88 
serializenull89     override fun serialize(): OnboardingProtos.LogProto {
90       return OnboardingProtos.LogProto.newBuilder()
91         .setActivityNodeExecutedDirectly(
92           OnboardingProtos.ActivityNodeExecutedDirectlyProto.newBuilder()
93             .setSourceNodeId(sourceNodeId)
94             .setNodeId(nodeId)
95             .setNodeName(nodeName)
96             .setNodeComponent(nodeComponent)
97             // .setArgument(argument)
98             .setTimestamp(timestamp.toEpochMilli())
99             .build()
100         )
101         .build()
102     }
103 
104     companion object {
fromProtonull105       fun fromProto(
106         proto: OnboardingProtos.ActivityNodeExecutedDirectlyProto,
107         timestamp: Instant?,
108       ) =
109         ActivityNodeExecutedDirectly(
110           sourceNodeId = proto.sourceNodeId,
111           nodeId = proto.nodeId,
112           nodeName = proto.nodeName,
113           nodeComponent = proto.nodeComponent,
114           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
115           argument = null,
116         )
117     }
118   }
119 
120   /**
121    * At [timestamp], an [android.app.Activity] with id [nodeId] is using the [nodeName] contract to
122    * validate intent [intent].
123    *
124    * Spawned at [nodeId].
125    */
126   data class ActivityNodeValidating(
127     override val nodeName: String,
128     override val nodeComponent: String,
129     override val nodeId: Long,
130     override val intent: IntentData? = null,
131     override val timestamp: Instant = Instant.now(),
132   ) : OnboardingActivityEvent, WithIntent {
133     constructor(
134       nodeId: Long,
135       nodeClass: Class<*>,
136       intent: IntentData? = null,
137       timestamp: Instant = Instant.now(),
138     ) : this(
139       nodeId = nodeId,
140       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
141       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
142       intent = intent,
143       timestamp = timestamp,
144     )
145 
146     override fun serialize(): OnboardingProtos.LogProto {
147       return OnboardingProtos.LogProto.newBuilder()
148         .setActivityNodeValidating(
149           OnboardingProtos.ActivityNodeValidatingProto.newBuilder()
150             .setNodeId(nodeId)
151             .setNodeName(nodeName)
152             .setNodeComponent(nodeComponent)
153             // .setIntent(intent)
154             .setTimestamp(timestamp.toEpochMilli())
155             .build()
156         )
157         .build()
158     }
159 
160     companion object {
161       fun fromProto(proto: OnboardingProtos.ActivityNodeValidatingProto, timestamp: Instant?) =
162         ActivityNodeValidating(
163           nodeId = proto.nodeId,
164           nodeName = proto.nodeName,
165           nodeComponent = proto.nodeComponent,
166           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
167         )
168     }
169   }
170 
171   /**
172    * At [timestamp], an [android.app.Activity] with id [nodeId] has failed validation of [intent]
173    * using the [nodeName] contract. The exception was [exception].
174    *
175    * Spawned at [nodeId].
176    */
177   data class ActivityNodeFailedValidation(
178     override val nodeName: String,
179     override val nodeComponent: String,
180     override val nodeId: Long,
181     val exception: Throwable = IllegalArgumentException("Failed validation"),
182     override val intent: IntentData? = null,
183     override val timestamp: Instant = Instant.now(),
184   ) : OnboardingActivityEvent, WithIntent {
185     constructor(
186       nodeId: Long,
187       nodeClass: Class<*>,
188       exception: Throwable = IllegalArgumentException("Failed validation"),
189       intent: IntentData? = null,
190       timestamp: Instant = Instant.now(),
191     ) : this(
192       nodeId = nodeId,
193       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
194       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
195       exception = exception,
196       intent = intent,
197       timestamp = timestamp,
198     )
199 
serializenull200     override fun serialize(): OnboardingProtos.LogProto {
201       return OnboardingProtos.LogProto.newBuilder()
202         .setActivityNodeFailedValidation(
203           OnboardingProtos.ActivityNodeFailedValidationProto.newBuilder()
204             .setNodeId(nodeId)
205             .setNodeName(nodeName)
206             .setNodeComponent(nodeComponent)
207             // .setException(exception)
208             // .setIntent(IntentData(intent?.action ?: "", mapOf()))
209             .setTimestamp(timestamp.toEpochMilli())
210             .build()
211         )
212         .build()
213     }
214 
215     companion object {
fromProtonull216       fun fromProto(
217         proto: OnboardingProtos.ActivityNodeFailedValidationProto,
218         timestamp: Instant?,
219       ) =
220         ActivityNodeFailedValidation(
221           nodeId = proto.nodeId,
222           nodeName = proto.nodeName,
223           nodeComponent = proto.nodeComponent,
224           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
225         )
226     }
227   }
228 
229   /**
230    * At [timestamp], an [android.app.Activity] with id [nodeId] is extracting the argument from
231    * [intent] using the [nodeName] contract.
232    *
233    * Spawned at [nodeId].
234    */
235   data class ActivityNodeExtractArgument(
236     override val nodeName: String,
237     override val nodeComponent: String,
238     override val nodeId: Long,
239     override val intent: IntentData,
240     override val timestamp: Instant = Instant.now(),
241   ) : OnboardingActivityEvent, WithIntent {
242     constructor(
243       nodeId: Long,
244       nodeClass: Class<*>,
245       intent: IntentData,
246       timestamp: Instant = Instant.now(),
247     ) : this(
248       nodeId = nodeId,
249       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
250       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
251       intent = intent,
252       timestamp = timestamp,
253     )
254 
255     override fun serialize(): OnboardingProtos.LogProto {
256       return OnboardingProtos.LogProto.newBuilder()
257         .setActivityNodeExtractArgument(
258           OnboardingProtos.ActivityNodeExtractArgumentProto.newBuilder()
259             .setNodeId(nodeId)
260             .setNodeName(nodeName)
261             .setNodeComponent(nodeComponent)
262             .setIntent(
263               OnboardingProtos.IntentDataProto.newBuilder().setAction(intent.action ?: "").build()
264             )
265             .setTimestamp(timestamp.toEpochMilli())
266             .build()
267         )
268         .build()
269     }
270 
271     companion object {
272       fun fromProto(proto: OnboardingProtos.ActivityNodeExtractArgumentProto, timestamp: Instant?) =
273         ActivityNodeExtractArgument(
274           nodeId = proto.nodeId,
275           nodeName = proto.nodeName,
276           nodeComponent = proto.nodeComponent,
277           intent =
278             IntentData(
279               if (proto.hasIntent()) proto.intent.action else "",
280               mapOf(),
281             ),
282           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
283         )
284     }
285   }
286 
287   /**
288    * At [timestamp], an [android.app.Activity] with id [nodeId] has extracted [argument] using the
289    * [nodeName] contract.
290    *
291    * Spawned at [nodeId].
292    */
293   data class ActivityNodeArgumentExtracted(
294     override val nodeName: String,
295     override val nodeComponent: String,
296     override val nodeId: Long,
297     override val argument: Any?,
298     override val timestamp: Instant = Instant.now(),
299   ) : OnboardingActivityEvent, WithArgument {
300     constructor(
301       nodeId: Long,
302       nodeClass: Class<*>,
303       argument: Any? = null,
304       timestamp: Instant = Instant.now(),
305     ) : this(
306       nodeId = nodeId,
307       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
308       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
309       argument = argument,
310       timestamp = timestamp,
311     )
312 
serializenull313     override fun serialize(): OnboardingProtos.LogProto {
314       return OnboardingProtos.LogProto.newBuilder()
315         .setActivityNodeArgumentExtracted(
316           OnboardingProtos.ActivityNodeArgumentExtractedProto.newBuilder()
317             .setNodeId(nodeId)
318             .setNodeName(nodeName)
319             .setNodeComponent(nodeComponent)
320             // setArgument(argument)
321             .setTimestamp(timestamp.toEpochMilli())
322             .build()
323         )
324         .build()
325     }
326 
327     companion object {
fromProtonull328       fun fromProto(
329         proto: OnboardingProtos.ActivityNodeArgumentExtractedProto,
330         timestamp: Instant?,
331       ) =
332         ActivityNodeArgumentExtracted(
333           nodeId = proto.nodeId,
334           nodeName = proto.nodeName,
335           nodeComponent = proto.nodeComponent,
336           argument = null,
337           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
338         )
339     }
340   }
341 
342   /**
343    * At [timestamp], an [android.app.Activity] with id [nodeId] has set the result to [result] using
344    * the [nodeName] contract.
345    *
346    * Spawned at [nodeId].
347    */
348   data class ActivityNodeSetResult(
349     override val nodeName: String,
350     override val nodeComponent: String,
351     override val nodeId: Long,
352     override val result: Any?,
353     override val timestamp: Instant = Instant.now(),
354   ) : OnboardingActivityEvent, WithResult {
355     constructor(
356       nodeId: Long,
357       nodeClass: Class<*>,
358       result: Any?,
359       timestamp: Instant = Instant.now(),
360     ) : this(
361       nodeId = nodeId,
362       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
363       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
364       result = result,
365       timestamp = timestamp,
366     )
367 
368     override fun serialize(): OnboardingProtos.LogProto {
369       return OnboardingProtos.LogProto.newBuilder()
370         .setActivityNodeSetResult(
371           OnboardingProtos.ActivityNodeSetResultProto.newBuilder()
372             .setNodeId(nodeId)
373             .setNodeName(nodeName)
374             .setNodeComponent(nodeComponent)
375             // setResult(result)
376             .setTimestamp(timestamp.toEpochMilli())
377             .build()
378         )
379         .build()
380     }
381 
382     companion object {
383       fun fromProto(proto: OnboardingProtos.ActivityNodeSetResultProto, timestamp: Instant?) =
384         ActivityNodeSetResult(
385           nodeId = proto.nodeId,
386           nodeName = proto.nodeName,
387           nodeComponent = proto.nodeComponent,
388           result = null,
389           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
390         )
391     }
392   }
393 
394   /**
395    * At [timestamp], an [android.app.Activity] with id [nodeId] has failed the [nodeName] contract
396    * because of [reason].
397    *
398    * Spawned at [nodeId].
399    */
400   data class ActivityNodeFail(
401     override val nodeId: Long,
402     val reason: String?,
403     override val timestamp: Instant = Instant.now(),
404   ) : OnboardingActivityEvent {
405     override val nodeName: String = IOnboardingGraphNode.unknown(nodeId)
406     override val nodeComponent: String = IOnboardingGraphNode.unknownComponent(nodeId)
407 
serializenull408     override fun serialize(): OnboardingProtos.LogProto {
409       return OnboardingProtos.LogProto.newBuilder()
410         .setActivityNodeFail(
411           OnboardingProtos.ActivityNodeFailProto.newBuilder()
412             .setNodeId(nodeId)
413             .setReason(reason ?: "")
414             .setTimestamp(timestamp.toEpochMilli())
415             .build()
416         )
417         .build()
418     }
419 
420     companion object {
fromProtonull421       fun fromProto(proto: OnboardingProtos.ActivityNodeFailProto, timestamp: Instant?) =
422         ActivityNodeFail(
423           nodeId = proto.nodeId,
424           reason = proto.reason,
425           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
426         )
427     }
428   }
429 
430   /**
431    * At [timestamp], a target [android.app.Activity] with id [nodeId] has been launched by invoking
432    * the [nodeName] contract from inside the [sourceNodeId] node with [argument] while expecting a
433    * result. Concluded by matching [ActivityNodeResultReceived] event.
434    *
435    * Spawned at [sourceNodeId].
436    *
437    * @property nodeId target node id.
438    * @property nodeName target node name.
439    * @property nodeComponent target node component.
440    * @property sourceNodeId source node id.
441    * @property argument the argument supplied for launching target node with.
442    */
443   data class ActivityNodeExecutedForResult(
444     override val nodeName: String,
445     override val nodeComponent: String,
446     override val sourceNodeId: Long,
447     override val nodeId: Long,
448     override val argument: Any? = null,
449     override val timestamp: Instant = Instant.now(),
450   ) : OnboardingActivityEvent, WithSource, WithArgument {
451     constructor(
452       sourceNodeId: Long,
453       nodeId: Long,
454       nodeClass: Class<*>,
455       argument: Any? = null,
456       timestamp: Instant = Instant.now(),
457     ) : this(
458       sourceNodeId = sourceNodeId,
459       nodeId = nodeId,
460       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
461       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
462       argument = argument,
463       timestamp = timestamp,
464     )
465 
466     override fun serialize(): OnboardingProtos.LogProto {
467       return OnboardingProtos.LogProto.newBuilder()
468         .setActivityNodeExecutedForResult(
469           OnboardingProtos.ActivityNodeExecutedForResultProto.newBuilder()
470             .setSourceNodeId(sourceNodeId)
471             .setNodeId(nodeId)
472             .setNodeName(nodeName)
473             .setNodeComponent(nodeComponent)
474             // .setArgument(argument)
475             .setTimestamp(timestamp.toEpochMilli())
476             .build()
477         )
478         .build()
479     }
480 
481     companion object {
482       fun fromProto(
483         proto: OnboardingProtos.ActivityNodeExecutedForResultProto,
484         timestamp: Instant?,
485       ) =
486         ActivityNodeExecutedForResult(
487           sourceNodeId = proto.sourceNodeId,
488           nodeId = proto.nodeId,
489           nodeName = proto.nodeName,
490           nodeComponent = proto.nodeComponent,
491           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
492         )
493     }
494   }
495 
496   /**
497    * At [timestamp], the [result] of the [android.app.Activity] node with id [nodeId] was received.
498    *
499    * Note that this does not specify which node received the result. The event must be matched up
500    * with a previous [ActivityNodeExecutedForResult] event.
501    *
502    * Spawned at source node.
503    *
504    * @property nodeName name of the target node.
505    * @property nodeComponent name of the target node.
506    * @property nodeId id of the target node.
507    * @property result a result produced by the target node.
508    */
509   data class ActivityNodeResultReceived(
510     override val nodeName: String,
511     override val nodeComponent: String,
512     override val nodeId: Long,
513     override val result: Any?,
514     override val timestamp: Instant = Instant.now(),
515   ) : OnboardingActivityEvent, WithResult {
516     constructor(
517       nodeId: Long,
518       nodeClass: Class<*>,
519       result: Any?,
520       timestamp: Instant = Instant.now(),
521     ) : this(
522       nodeId = nodeId,
523       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
524       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
525       result = result,
526       timestamp = timestamp,
527     )
528 
serializenull529     override fun serialize(): OnboardingProtos.LogProto {
530       return OnboardingProtos.LogProto.newBuilder()
531         .setActivityNodeResultReceived(
532           OnboardingProtos.ActivityNodeResultReceivedProto.newBuilder()
533             .setNodeId(nodeId)
534             .setNodeName(nodeName)
535             .setNodeComponent(nodeComponent)
536             // setResult(result)
537             .setTimestamp(timestamp.toEpochMilli())
538             .build()
539         )
540         .build()
541     }
542 
543     companion object {
fromProtonull544       fun fromProto(proto: OnboardingProtos.ActivityNodeResultReceivedProto, timestamp: Instant?) =
545         ActivityNodeResultReceived(
546           nodeId = proto.nodeId,
547           nodeName = proto.nodeName,
548           nodeComponent = proto.nodeComponent,
549           result = null,
550           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
551         )
552     }
553   }
554 
555   /**
556    * At [timestamp], the activity node with id [nodeId] called finish.
557    *
558    * Spawned at [nodeId].
559    */
560   data class ActivityNodeFinished(
561     override val nodeName: String,
562     override val nodeComponent: String,
563     override val nodeId: Long,
564     override val timestamp: Instant = Instant.now(),
565   ) : OnboardingActivityEvent {
566     constructor(
567       nodeId: Long,
568       nodeClass: Class<*>,
569       timestamp: Instant = Instant.now(),
570     ) : this(
571       nodeId = nodeId,
572       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
573       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
574       timestamp = timestamp,
575     )
576 
577     override fun serialize(): OnboardingProtos.LogProto {
578       return OnboardingProtos.LogProto.newBuilder()
579         .setActivityNodeFinished(
580           OnboardingProtos.ActivityNodeFinishedProto.newBuilder()
581             .setNodeId(nodeId)
582             .setNodeName(nodeName)
583             .setNodeComponent(nodeComponent)
584             .setTimestamp(timestamp.toEpochMilli())
585             .build()
586         )
587         .build()
588     }
589 
590     companion object {
591       fun fromProto(proto: OnboardingProtos.ActivityNodeFinishedProto, timestamp: Instant?) =
592         ActivityNodeFinished(
593           nodeId = proto.nodeId,
594           nodeName = proto.nodeName,
595           nodeComponent = proto.nodeComponent,
596           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
597         )
598     }
599   }
600 
601   /**
602    * At [timestamp], the activity node with id [nodeId] resumes after it previously launched node
603    * with id [sourceNodeId].
604    *
605    * Spawned at [nodeId].
606    *
607    * @property sourceNodeId the id of the node that this node previously launched and now resumed
608    *   from.
609    * @property nodeId the id of the node spawning this event.
610    * @property nodeName the name of the node spawning this event.
611    * @property nodeComponent the component of the node spawning this event.
612    */
613   data class ActivityNodeResumedAfterLaunch(
614     override val nodeName: String,
615     override val nodeComponent: String,
616     override val sourceNodeId: Long,
617     override val nodeId: Long,
618     override val timestamp: Instant = Instant.now(),
619   ) : OnboardingActivityEvent, WithSource {
620     constructor(
621       sourceNodeId: Long,
622       nodeId: Long,
623       nodeClass: Class<*>,
624       timestamp: Instant = Instant.now(),
625     ) : this(
626       sourceNodeId = sourceNodeId,
627       nodeId = nodeId,
628       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
629       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
630       timestamp = timestamp,
631     )
632 
serializenull633     override fun serialize(): OnboardingProtos.LogProto {
634       return OnboardingProtos.LogProto.newBuilder()
635         .setActivityNodeResumedAfterLaunch(
636           OnboardingProtos.ActivityNodeResumedAfterLaunchProto.newBuilder()
637             .setSourceNodeId(sourceNodeId)
638             .setNodeId(nodeId)
639             .setNodeName(nodeName)
640             .setNodeComponent(nodeComponent)
641             .setTimestamp(timestamp.toEpochMilli())
642             .build()
643         )
644         .build()
645     }
646 
647     companion object {
fromProtonull648       fun fromProto(
649         proto: OnboardingProtos.ActivityNodeResumedAfterLaunchProto,
650         timestamp: Instant?,
651       ) =
652         ActivityNodeResumedAfterLaunch(
653           sourceNodeId = proto.sourceNodeId,
654           nodeId = proto.nodeId,
655           nodeName = proto.nodeName,
656           nodeComponent = proto.nodeComponent,
657           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
658         )
659     }
660   }
661 
662   /**
663    * At [timestamp], the activity node with id [nodeId] called finish.
664    *
665    * Spawned at [nodeId].
666    */
667   data class ActivityNodeResumed(
668     override val nodeName: String,
669     override val nodeComponent: String,
670     override val nodeId: Long,
671     override val timestamp: Instant = Instant.now(),
672   ) : OnboardingActivityEvent {
673     constructor(
674       nodeId: Long,
675       nodeClass: Class<*>,
676       timestamp: Instant = Instant.now(),
677     ) : this(
678       nodeId = nodeId,
679       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
680       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
681       timestamp = timestamp,
682     )
683 
684     override fun serialize(): OnboardingProtos.LogProto {
685       return OnboardingProtos.LogProto.newBuilder()
686         .setActivityNodeResumed(
687           OnboardingProtos.ActivityNodeResumedProto.newBuilder()
688             .setNodeId(nodeId)
689             .setNodeName(nodeName)
690             .setNodeComponent(nodeComponent)
691             .setTimestamp(timestamp.toEpochMilli())
692             .build()
693         )
694         .build()
695     }
696 
697     companion object {
698       fun fromProto(proto: OnboardingProtos.ActivityNodeResumedProto, timestamp: Instant?) =
699         ActivityNodeResumed(
700           nodeId = proto.nodeId,
701           nodeName = proto.nodeName,
702           nodeComponent = proto.nodeComponent,
703           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
704         )
705     }
706   }
707 
708   /** An [OnboardingActivityEvent] spawned only from synchronous executions of contracts. */
709   sealed interface OnboardingSynchronousActivityEvent : OnboardingActivityEvent
710 
711   /**
712    * At [timestamp], the node with id [sourceNodeId] began synchronous execution of the node with id
713    * [nodeId]. Concluded by matching [ActivityNodeExecutedSynchronously] event. Note that
714    * [OnboardingEvent.ActivityNodeExecutedForResult] will always be spawned immediately after this
715    * event in the same flow.
716    *
717    * Spawned at [sourceNodeId].
718    *
719    * @property nodeId target node id.
720    * @property nodeName target node name.
721    * @property nodeComponent target node component.
722    * @property sourceNodeId source node id.
723    * @property argument the argument supplied for launching target node with.
724    */
725   data class ActivityNodeStartExecuteSynchronously(
726     override val nodeName: String,
727     override val nodeComponent: String,
728     override val sourceNodeId: Long,
729     override val nodeId: Long,
730     override val argument: Any?,
731     override val timestamp: Instant = Instant.now(),
732   ) : OnboardingSynchronousActivityEvent, WithSource, WithArgument {
733     constructor(
734       sourceNodeId: Long,
735       nodeId: Long,
736       nodeClass: Class<*>,
737       argument: Any? = null,
738       timestamp: Instant = Instant.now(),
739     ) : this(
740       sourceNodeId = sourceNodeId,
741       nodeId = nodeId,
742       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
743       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
744       argument = argument,
745       timestamp = timestamp,
746     )
747 
serializenull748     override fun serialize(): OnboardingProtos.LogProto {
749       return OnboardingProtos.LogProto.newBuilder()
750         .setActivityNodeStartExecuteSynchronously(
751           OnboardingProtos.ActivityNodeStartExecuteSynchronouslyProto.newBuilder()
752             .setSourceNodeId(sourceNodeId)
753             .setNodeId(nodeId)
754             .setNodeName(nodeName)
755             .setNodeComponent(nodeComponent)
756             // setArgument(argument)
757             .setTimestamp(timestamp.toEpochMilli())
758             .build()
759         )
760         .build()
761     }
762 
763     companion object {
fromProtonull764       fun fromProto(
765         proto: OnboardingProtos.ActivityNodeStartExecuteSynchronouslyProto,
766         timestamp: Instant?,
767       ) =
768         ActivityNodeStartExecuteSynchronously(
769           sourceNodeId = proto.sourceNodeId,
770           nodeId = proto.nodeId,
771           nodeName = proto.nodeName,
772           nodeComponent = proto.nodeComponent,
773           argument = null,
774           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
775         )
776     }
777   }
778 
779   /**
780    * At [timestamp], the synchronously executing node with id [nodeId] produced a [result].
781    *
782    * Note that this does not specify which node received the result. The event must be matched up
783    * with a previous [ActivityNodeStartExecuteSynchronously] event.
784    *
785    * Spawned at source node.
786    *
787    * @property nodeName name of the target node.
788    * @property nodeComponent name of the target node.
789    * @property nodeId id of the target node.
790    */
791   data class ActivityNodeExecutedSynchronously(
792     override val nodeName: String,
793     override val nodeComponent: String,
794     override val nodeId: Long,
795     override val result: Any?,
796     override val timestamp: Instant = Instant.now(),
797   ) : OnboardingSynchronousActivityEvent, WithResult {
798     constructor(
799       nodeId: Long,
800       nodeClass: Class<*>,
801       result: Any?,
802       timestamp: Instant = Instant.now(),
803     ) : this(
804       nodeId = nodeId,
805       nodeName = OnboardingNode.extractNodeNameFromClass(nodeClass),
806       nodeComponent = OnboardingNode.extractComponentNameFromClass(nodeClass),
807       result = result,
808       timestamp = timestamp,
809     )
810 
811     override fun serialize(): OnboardingProtos.LogProto {
812       return OnboardingProtos.LogProto.newBuilder()
813         .setActivityNodeExecutedSynchronously(
814           OnboardingProtos.ActivityNodeExecutedSynchronouslyProto.newBuilder()
815             .setNodeId(nodeId)
816             .setNodeName(nodeName)
817             .setNodeComponent(nodeComponent)
818             // setResult(result)
819             .setTimestamp(timestamp.toEpochMilli())
820             .build()
821         )
822         .build()
823     }
824 
825     companion object {
826       fun fromProto(
827         proto: OnboardingProtos.ActivityNodeExecutedSynchronouslyProto,
828         timestamp: Instant?,
829       ) =
830         ActivityNodeExecutedSynchronously(
831           nodeId = proto.nodeId,
832           nodeName = proto.nodeName,
833           nodeComponent = proto.nodeComponent,
834           result = null,
835           timestamp = timestamp ?: Instant.ofEpochMilli(proto.timestamp),
836         )
837     }
838   }
839 
840   // LINT.ThenChange(//depot/google3/logs/proto/android_onboarding/node_interaction_metadata.proto)
841 
842   /**
843    * Wrapper around the same information as [android.content.Intent] but can be used without
844    * dependency on Android.
845    */
846   data class IntentData(val action: String?, val extras: Map<String, Any?>)
847 
848   companion object {
deserializenull849     fun deserialize(str: String, timestamp: Instant? = null): OnboardingEvent {
850       val proto = OnboardingProtos.LogProto.parseFrom(BaseEncoding.base64().decode(str))
851 
852       return when {
853         proto.hasActivityNodeExecutedDirectly() ->
854           ActivityNodeExecutedDirectly.fromProto(proto.activityNodeExecutedDirectly, timestamp)
855         proto.hasActivityNodeExecutedForResult() ->
856           ActivityNodeExecutedForResult.fromProto(proto.activityNodeExecutedForResult, timestamp)
857         proto.hasActivityNodeValidating() ->
858           ActivityNodeValidating.fromProto(proto.activityNodeValidating, timestamp)
859         proto.hasActivityNodeFailedValidation() ->
860           ActivityNodeFailedValidation.fromProto(proto.activityNodeFailedValidation, timestamp)
861         proto.hasActivityNodeExtractArgument() ->
862           ActivityNodeExtractArgument.fromProto(proto.activityNodeExtractArgument, timestamp)
863         proto.hasActivityNodeArgumentExtracted() ->
864           ActivityNodeArgumentExtracted.fromProto(proto.activityNodeArgumentExtracted, timestamp)
865         proto.hasActivityNodeSetResult() ->
866           ActivityNodeSetResult.fromProto(proto.activityNodeSetResult, timestamp)
867         proto.hasActivityNodeFail() -> ActivityNodeFail.fromProto(proto.activityNodeFail, timestamp)
868         proto.hasActivityNodeResultReceived() ->
869           ActivityNodeResultReceived.fromProto(proto.activityNodeResultReceived, timestamp)
870         proto.hasActivityNodeStartExecuteSynchronously() ->
871           ActivityNodeStartExecuteSynchronously.fromProto(
872             proto.activityNodeStartExecuteSynchronously,
873             timestamp,
874           )
875         proto.hasActivityNodeExecutedSynchronously() ->
876           ActivityNodeExecutedSynchronously.fromProto(
877             proto.activityNodeExecutedSynchronously,
878             timestamp,
879           )
880         proto.hasActivityNodeFinished() ->
881           ActivityNodeFinished.fromProto(proto.activityNodeFinished, timestamp)
882         proto.hasActivityNodeResumedAfterLaunch() ->
883           ActivityNodeResumedAfterLaunch.fromProto(proto.activityNodeResumedAfterLaunch, timestamp)
884         proto.hasActivityNodeResumed() ->
885           ActivityNodeResumed.fromProto(proto.activityNodeResumed, timestamp)
886         else -> throw IllegalStateException("Could not parse $proto")
887       }
888     }
889   }
890 }
891