1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.os;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.content.ComponentName;
23 import android.content.Intent;
24 import android.os.SystemClock;
25 
26 import com.android.internal.os.anr.AnrLatencyTracker;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 
31 /**
32  * A timeout that has triggered on the system.
33  *
34  * @hide
35  */
36 public class TimeoutRecord {
37     /** Kind of timeout, e.g. BROADCAST_RECEIVER, etc. */
38     @IntDef(value = {
39             TimeoutKind.INPUT_DISPATCH_NO_FOCUSED_WINDOW,
40             TimeoutKind.INPUT_DISPATCH_WINDOW_UNRESPONSIVE,
41             TimeoutKind.BROADCAST_RECEIVER,
42             TimeoutKind.SERVICE_START,
43             TimeoutKind.SERVICE_EXEC,
44             TimeoutKind.CONTENT_PROVIDER,
45             TimeoutKind.APP_REGISTERED,
46             TimeoutKind.SHORT_FGS_TIMEOUT,
47             TimeoutKind.JOB_SERVICE,
48     })
49 
50     @Retention(RetentionPolicy.SOURCE)
51     public @interface TimeoutKind {
52         int INPUT_DISPATCH_NO_FOCUSED_WINDOW = 1;
53         int INPUT_DISPATCH_WINDOW_UNRESPONSIVE = 2;
54         int BROADCAST_RECEIVER = 3;
55         int SERVICE_START = 4;
56         int SERVICE_EXEC = 5;
57         int CONTENT_PROVIDER = 6;
58         int APP_REGISTERED = 7;
59         int SHORT_FGS_TIMEOUT = 8;
60         int JOB_SERVICE = 9;
61         int APP_START = 10;
62     }
63 
64     /** Kind of timeout, e.g. BROADCAST_RECEIVER, etc. */
65     @TimeoutKind
66     public final int mKind;
67 
68     /** Reason for the timeout. */
69     public final String mReason;
70 
71     /** System uptime in millis when the timeout was triggered. */
72     public final long mEndUptimeMillis;
73 
74     /**
75      * Was the end timestamp taken right after the timeout triggered, before any potentially
76      * expensive operations such as taking locks?
77      */
78     public final boolean mEndTakenBeforeLocks;
79 
80     /** Latency tracker associated with this instance. */
81     public final AnrLatencyTracker mLatencyTracker;
82 
TimeoutRecord(@imeoutKind int kind, @NonNull String reason, long endUptimeMillis, boolean endTakenBeforeLocks)83     private TimeoutRecord(@TimeoutKind int kind, @NonNull String reason, long endUptimeMillis,
84             boolean endTakenBeforeLocks) {
85         this.mKind = kind;
86         this.mReason = reason;
87         this.mEndUptimeMillis = endUptimeMillis;
88         this.mEndTakenBeforeLocks = endTakenBeforeLocks;
89         this.mLatencyTracker = new AnrLatencyTracker(kind, endUptimeMillis);
90     }
91 
endingNow(@imeoutKind int kind, String reason)92     private static TimeoutRecord endingNow(@TimeoutKind int kind, String reason) {
93         long endUptimeMillis = SystemClock.uptimeMillis();
94         return new TimeoutRecord(kind, reason, endUptimeMillis, /* endTakenBeforeLocks */ true);
95     }
96 
endingApproximatelyNow(@imeoutKind int kind, String reason)97     private static TimeoutRecord endingApproximatelyNow(@TimeoutKind int kind, String reason) {
98         long endUptimeMillis = SystemClock.uptimeMillis();
99         return new TimeoutRecord(kind, reason, endUptimeMillis, /* endTakenBeforeLocks */ false);
100     }
101 
102     /** Record for a broadcast receiver timeout. */
103     @NonNull
forBroadcastReceiver(@onNull Intent intent, @Nullable String packageName, @Nullable String className)104     public static TimeoutRecord forBroadcastReceiver(@NonNull Intent intent,
105             @Nullable String packageName, @Nullable String className) {
106         final Intent logIntent;
107         if (packageName != null) {
108             if (className != null) {
109                 logIntent = new Intent(intent);
110                 logIntent.setComponent(new ComponentName(packageName, className));
111             } else {
112                 logIntent = new Intent(intent);
113                 logIntent.setPackage(packageName);
114             }
115         } else {
116             logIntent = intent;
117         }
118         return forBroadcastReceiver(logIntent);
119     }
120 
121     /** Record for a broadcast receiver timeout. */
122     @NonNull
forBroadcastReceiver(@onNull Intent intent)123     public static TimeoutRecord forBroadcastReceiver(@NonNull Intent intent) {
124         final StringBuilder reason = new StringBuilder("Broadcast of ");
125         intent.toString(reason);
126         return TimeoutRecord.endingNow(TimeoutKind.BROADCAST_RECEIVER, reason.toString());
127     }
128 
129     /** Record for a broadcast receiver timeout. */
130     @NonNull
forBroadcastReceiver(@onNull Intent intent, long timeoutDurationMs)131     public static TimeoutRecord forBroadcastReceiver(@NonNull Intent intent,
132             long timeoutDurationMs) {
133         final StringBuilder reason = new StringBuilder("Broadcast of ");
134         intent.toString(reason);
135         reason.append(", waited ");
136         reason.append(timeoutDurationMs);
137         reason.append("ms");
138         return TimeoutRecord.endingNow(TimeoutKind.BROADCAST_RECEIVER, reason.toString());
139     }
140 
141     /** Record for an input dispatch no focused window timeout */
142     @NonNull
forInputDispatchNoFocusedWindow(@onNull String reason)143     public static TimeoutRecord forInputDispatchNoFocusedWindow(@NonNull String reason) {
144         return TimeoutRecord.endingNow(TimeoutKind.INPUT_DISPATCH_NO_FOCUSED_WINDOW, reason);
145     }
146 
147     /** Record for an input dispatch window unresponsive timeout. */
148     @NonNull
forInputDispatchWindowUnresponsive(@onNull String reason)149     public static TimeoutRecord forInputDispatchWindowUnresponsive(@NonNull String reason) {
150         return TimeoutRecord.endingNow(TimeoutKind.INPUT_DISPATCH_WINDOW_UNRESPONSIVE, reason);
151     }
152 
153     /** Record for a service exec timeout. */
154     @NonNull
forServiceExec(@onNull String reason)155     public static TimeoutRecord forServiceExec(@NonNull String reason) {
156         return TimeoutRecord.endingNow(TimeoutKind.SERVICE_EXEC, reason);
157     }
158 
159     /** Record for a service start timeout. */
160     @NonNull
forServiceStartWithEndTime(@onNull String reason, long endUptimeMillis)161     public static TimeoutRecord forServiceStartWithEndTime(@NonNull String reason,
162             long endUptimeMillis) {
163         return new TimeoutRecord(TimeoutKind.SERVICE_START, reason,
164                 endUptimeMillis, /* endTakenBeforeLocks */ true);
165     }
166 
167     /** Record for a content provider timeout. */
168     @NonNull
forContentProvider(@onNull String reason)169     public static TimeoutRecord forContentProvider(@NonNull String reason) {
170         return TimeoutRecord.endingApproximatelyNow(TimeoutKind.CONTENT_PROVIDER, reason);
171     }
172 
173     /** Record for an app registered timeout. */
174     @NonNull
forApp(@onNull String reason)175     public static TimeoutRecord forApp(@NonNull String reason) {
176         return TimeoutRecord.endingApproximatelyNow(TimeoutKind.APP_REGISTERED, reason);
177     }
178 
179     /** Record for a "short foreground service" timeout. */
180     @NonNull
forShortFgsTimeout(String reason)181     public static TimeoutRecord forShortFgsTimeout(String reason) {
182         return TimeoutRecord.endingNow(TimeoutKind.SHORT_FGS_TIMEOUT, reason);
183     }
184 
185     /** Record for a job related timeout. */
186     @NonNull
forJobService(String reason)187     public static TimeoutRecord forJobService(String reason) {
188         return TimeoutRecord.endingNow(TimeoutKind.JOB_SERVICE, reason);
189     }
190 
191     /** Record for app startup timeout. */
192     @NonNull
forAppStart(String reason)193     public static TimeoutRecord forAppStart(String reason) {
194         return TimeoutRecord.endingNow(TimeoutKind.APP_START, reason);
195     }
196 }
197