1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.android.startop.iorap;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Intent;
22 import android.util.Log;
23 
24 import com.android.server.wm.ActivityMetricsLaunchObserver;
25 
26 import java.io.StringWriter;
27 import java.io.PrintWriter;
28 
29 /**
30  * A validator to check the correctness of event sequence during app startup.
31  *
32  * <p> A valid state transition of event sequence is shown as the following:
33  *
34  * <pre>
35  *
36  *                                +--------------------+
37  *                                |                    |
38  *                                |        INIT        |
39  *                                |                    |
40  *                                +--------------------+
41  *                                          |
42  *                                          |
43  *                                          ↓
44  *                                +--------------------+
45  *                                |                    |
46  *            +-------------------|   INTENT_STARTED   | ←--------------------------------+
47  *            |                   |                    |                                  |
48  *            |                   +--------------------+                                  |
49  *            |                             |                                             |
50  *            |                             |                                             |
51  *            ↓                             ↓                                             |
52  * +--------------------+         +--------------------+                                  |
53  * |                    |         |                    |                                  |
54  * |   INTENT_FAILED    |         | ACTIVITY_LAUNCHED  |------------------+               |
55  * |                    |         |                    |                  |               |
56  * +--------------------+         +--------------------+                  |               |
57  *            |                              |                            |               |
58  *            |                              ↓                            ↓               |
59  *            |                   +--------------------+       +--------------------+     |
60  *            |                   |                    |       |                    |     |
61  *            +------------------ |  ACTIVITY_FINISHED |       | ACTIVITY_CANCELLED |     |
62  *            |                   |                    |       |                    |     |
63  *            |                   +--------------------+       +--------------------+     |
64  *            |                              |                            |               |
65  *            |                              |                            |               |
66  *            |                              ↓                            |               |
67  *            |                   +--------------------+                  |               |
68  *            |                   |                    |                  |               |
69  *            |                   | REPORT_FULLY_DRAWN |                  |               |
70  *            |                   |                    |                  |               |
71  *            |                   +--------------------+                  |               |
72  *            |                              |                            |               |
73  *            |                              |                            |               |
74  *            |                              ↓                            |               |
75  *            |                   +--------------------+                  |               |
76  *            |                   |                    |                  |               |
77  *            +-----------------→ |        END         |←-----------------+               |
78  *                                |                    |                                  |
79  *                                +--------------------+                                  |
80  *                                           |                                            |
81  *                                           |                                            |
82  *                                           |                                            |
83  *                                           +---------------------------------------------
84  *
85  * <p> END is not a real state in implementation. All states that points to END directly
86  * could transition to INTENT_STARTED.
87  *
88  * <p> If any bad transition happened, the state becomse UNKNOWN. The UNKNOWN state
89  * could be accumulated, because during the UNKNOWN state more IntentStarted may
90  * be triggered. To recover from UNKNOWN to INIT, all the accumualted IntentStarted
91  * should termniate.
92  *
93  * <p> During UNKNOWN state, each IntentStarted increases the accumulation, and any of
94  * IntentFailed, ActivityLaunchCancelled and ActivityFinished decreases the accumulation.
95  * ReportFullyDrawn doesn't impact the accumulation.
96  */
97 public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
98   static final String TAG = "EventSequenceValidator";
99   /** $> adb shell 'setprop log.tag.EventSequenceValidator VERBOSE' */
100   public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
101   private State state = State.INIT;
102   private long accIntentStartedEvents = 0;
103 
104   @Override
onIntentStarted(@onNull Intent intent, long timestampNs)105   public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
106     if (state == State.UNKNOWN) {
107       logWarningWithStackTrace("IntentStarted during UNKNOWN. " + intent);
108       incAccIntentStartedEvents();
109       return;
110     }
111 
112     if (state != State.INIT &&
113         state != State.INTENT_FAILED &&
114         state != State.ACTIVITY_CANCELLED &&
115         state != State.ACTIVITY_FINISHED &&
116         state != State.REPORT_FULLY_DRAWN) {
117       logWarningWithStackTrace(
118           String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
119       incAccIntentStartedEvents();
120       incAccIntentStartedEvents();
121       return;
122     }
123 
124     Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_STARTED));
125     state = State.INTENT_STARTED;
126   }
127 
128   @Override
onIntentFailed()129   public void onIntentFailed() {
130     if (state == State.UNKNOWN) {
131       logWarningWithStackTrace("onIntentFailed during UNKNOWN.");
132       decAccIntentStartedEvents();
133       return;
134     }
135     if (state != State.INTENT_STARTED) {
136       logWarningWithStackTrace(
137           String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
138       incAccIntentStartedEvents();
139       return;
140     }
141 
142     Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_FAILED));
143     state = State.INTENT_FAILED;
144   }
145 
146   @Override
onActivityLaunched(@onNull @ctivityRecordProto byte[] activity, @Temperature int temperature)147   public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
148       @Temperature int temperature) {
149     if (state == State.UNKNOWN) {
150       logWarningWithStackTrace("onActivityLaunched during UNKNOWN.");
151       return;
152     }
153     if (state != State.INTENT_STARTED) {
154       logWarningWithStackTrace(
155           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
156       incAccIntentStartedEvents();
157       return;
158     }
159 
160     Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
161     state = State.ACTIVITY_LAUNCHED;
162   }
163 
164   @Override
onActivityLaunchCancelled(@ullable @ctivityRecordProto byte[] activity)165   public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
166     if (state == State.UNKNOWN) {
167       logWarningWithStackTrace("onActivityLaunchCancelled during UNKNOWN.");
168       decAccIntentStartedEvents();
169       return;
170     }
171     if (state != State.ACTIVITY_LAUNCHED) {
172       logWarningWithStackTrace(
173           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
174       incAccIntentStartedEvents();
175       return;
176     }
177 
178     Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_CANCELLED));
179     state = State.ACTIVITY_CANCELLED;
180   }
181 
182   @Override
onActivityLaunchFinished(@onNull @ctivityRecordProto byte[] activity, long timestampNs)183   public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
184       long timestampNs) {
185     if (state == State.UNKNOWN) {
186       logWarningWithStackTrace("onActivityLaunchFinished during UNKNOWN.");
187       decAccIntentStartedEvents();
188       return;
189     }
190 
191     if (state != State.ACTIVITY_LAUNCHED) {
192       logWarningWithStackTrace(
193           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
194       incAccIntentStartedEvents();
195       return;
196     }
197 
198     Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_FINISHED));
199     state = State.ACTIVITY_FINISHED;
200   }
201 
202   @Override
onReportFullyDrawn(@onNull @ctivityRecordProto byte[] activity, long timestampNs)203   public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
204       long timestampNs) {
205     if (state == State.UNKNOWN) {
206       logWarningWithStackTrace("onReportFullyDrawn during UNKNOWN.");
207       return;
208     }
209     if (state == State.INIT) {
210       return;
211     }
212 
213     if (state != State.ACTIVITY_FINISHED) {
214       logWarningWithStackTrace(
215           String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
216       return;
217     }
218 
219     Log.i(TAG, String.format("Transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
220     state = State.REPORT_FULLY_DRAWN;
221   }
222 
223   enum State {
224     INIT,
225     INTENT_STARTED,
226     INTENT_FAILED,
227     ACTIVITY_LAUNCHED,
228     ACTIVITY_CANCELLED,
229     ACTIVITY_FINISHED,
230     REPORT_FULLY_DRAWN,
231     UNKNOWN,
232   }
233 
incAccIntentStartedEvents()234   private void incAccIntentStartedEvents() {
235     if (accIntentStartedEvents < 0) {
236       throw new AssertionError("The number of unknowns cannot be negative");
237     }
238     if (accIntentStartedEvents == 0) {
239       state = State.UNKNOWN;
240     }
241     ++accIntentStartedEvents;
242     Log.i(TAG,
243         String.format("inc AccIntentStartedEvents to %d", accIntentStartedEvents));
244   }
245 
decAccIntentStartedEvents()246   private void decAccIntentStartedEvents() {
247     if (accIntentStartedEvents <= 0) {
248       throw new AssertionError("The number of unknowns cannot be negative");
249     }
250     if(accIntentStartedEvents == 1) {
251       state = State.INIT;
252     }
253     --accIntentStartedEvents;
254     Log.i(TAG,
255         String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents));
256   }
257 
logWarningWithStackTrace(String log)258   private void logWarningWithStackTrace(String log) {
259     if (DEBUG) {
260       StringWriter sw = new StringWriter();
261       PrintWriter pw = new PrintWriter(sw);
262       new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
263       Log.wtf(TAG, String.format("%s\n%s", log, sw));
264     }
265   }
266 }
267 
268