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.server.am;
18 
19 import static android.os.PowerExemptionManager.REASON_DENIED;
20 
21 import android.annotation.ElapsedRealtimeLong;
22 import android.annotation.NonNull;
23 import android.os.PowerExemptionManager.ReasonCode;
24 import android.os.SystemClock;
25 import android.os.UserHandle;
26 import android.util.Slog;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 
30 import java.io.PrintWriter;
31 import java.util.LinkedList;
32 
33 /**
34  * A helper class to track the occurrences of certain events.
35  */
36 abstract class BaseAppStateEvents<E> {
37     static final boolean DEBUG_BASE_APP_STATE_EVENTS = false;
38     final int mUid;
39     final @NonNull String mPackageName;
40     final @NonNull String mTag;
41     final @NonNull MaxTrackingDurationConfig mMaxTrackingDurationConfig;
42 
43     /**
44      * The events we're tracking.
45      *
46      * <p>
47      * The meaning of the events is up to the derived classes, i.e., it could be a series of
48      * individual events, or a series of event pairs (i.e., start/stop event). The implementations
49      * of {@link #add}, {@link #trim} etc. in this class are based on the individual events.
50      * </p>
51      */
52     final LinkedList<E>[] mEvents;
53 
54     /**
55      * In case the data we're tracking here is ignored, here is why.
56      */
57     @ReasonCode int mExemptReason = REASON_DENIED;
58 
BaseAppStateEvents(int uid, @NonNull String packageName, int numOfEventTypes, @NonNull String tag, @NonNull MaxTrackingDurationConfig maxTrackingDurationConfig)59     BaseAppStateEvents(int uid, @NonNull String packageName, int numOfEventTypes,
60             @NonNull String tag, @NonNull MaxTrackingDurationConfig maxTrackingDurationConfig) {
61         mUid = uid;
62         mPackageName = packageName;
63         mTag = tag;
64         mMaxTrackingDurationConfig = maxTrackingDurationConfig;
65         mEvents = new LinkedList[numOfEventTypes];
66     }
67 
BaseAppStateEvents(@onNull BaseAppStateEvents other)68     BaseAppStateEvents(@NonNull BaseAppStateEvents other) {
69         mUid = other.mUid;
70         mPackageName = other.mPackageName;
71         mTag = other.mTag;
72         mMaxTrackingDurationConfig = other.mMaxTrackingDurationConfig;
73         mEvents = new LinkedList[other.mEvents.length];
74         for (int i = 0; i < mEvents.length; i++) {
75             if (other.mEvents[i] != null) {
76                 mEvents[i] = new LinkedList<E>(other.mEvents[i]);
77             }
78         }
79     }
80 
81     /**
82      * Add an individual event.
83      */
addEvent(E event, long now, int index)84     void addEvent(E event, long now, int index) {
85         if (mEvents[index] == null) {
86             mEvents[index] = new LinkedList<E>();
87         }
88         final LinkedList<E> events = mEvents[index];
89         events.add(event);
90         trimEvents(getEarliest(now), index);
91     }
92 
93     /**
94      * Remove/trim earlier events with start time older than the given timestamp.
95      */
trim(long earliest)96     void trim(long earliest) {
97         for (int i = 0; i < mEvents.length; i++) {
98             trimEvents(earliest, i);
99         }
100     }
101 
102     /**
103      * Remove/trim earlier events with start time older than the given timestamp.
104      */
trimEvents(long earliest, int index)105     abstract void trimEvents(long earliest, int index);
106 
107     /**
108      * @return {@code true} if there is no events being tracked.
109      */
isEmpty()110     boolean isEmpty() {
111         for (int i = 0; i < mEvents.length; i++) {
112             if (mEvents[i] != null && !mEvents[i].isEmpty()) {
113                 return false;
114             }
115         }
116         return true;
117     }
118 
119     /**
120      * @return {@code true} if there is no events being tracked.
121      */
isEmpty(int index)122     boolean isEmpty(int index) {
123         return mEvents[index] == null || mEvents[index].isEmpty();
124     }
125 
126     /**
127      * Merge the events table from another instance.
128      */
add(BaseAppStateEvents other)129     void add(BaseAppStateEvents other) {
130         if (mEvents.length != other.mEvents.length) {
131             if (DEBUG_BASE_APP_STATE_EVENTS) {
132                 Slog.wtf(mTag, "Incompatible event table this=" + this + ", other=" + other);
133             }
134             return;
135         }
136         for (int i = 0; i < mEvents.length; i++) {
137             mEvents[i] = add(mEvents[i], other.mEvents[i]);
138         }
139     }
140 
141     @VisibleForTesting
getRawEvents(int index)142     LinkedList<E> getRawEvents(int index) {
143         return mEvents[index];
144     }
145 
146     /**
147      * Merge the two given events table and return the result.
148      */
add(LinkedList<E> events, LinkedList<E> otherEvents)149     abstract LinkedList<E> add(LinkedList<E> events, LinkedList<E> otherEvents);
150 
151     /**
152      * The number of events since the given time.
153      */
getTotalEventsSince(long since, long now, int index)154     abstract int getTotalEventsSince(long since, long now, int index);
155 
156     /**
157      * The total number of events we are tracking.
158      */
getTotalEvents(long now, int index)159     int getTotalEvents(long now, int index) {
160         return getTotalEventsSince(getEarliest(0), now, index);
161     }
162 
163     /**
164      * @return The earliest possible time we're tracking with given timestamp.
165      */
getEarliest(long now)166     long getEarliest(long now) {
167         return Math.max(0, now - mMaxTrackingDurationConfig.getMaxTrackingDuration());
168     }
169 
dump(PrintWriter pw, String prefix, @ElapsedRealtimeLong long nowElapsed)170     void dump(PrintWriter pw, String prefix, @ElapsedRealtimeLong long nowElapsed) {
171         for (int i = 0; i < mEvents.length; i++) {
172             if (mEvents[i] == null) {
173                 continue;
174             }
175             pw.print(prefix);
176             pw.print(formatEventTypeLabel(i));
177             pw.println(formatEventSummary(nowElapsed, i));
178         }
179     }
180 
formatEventSummary(long now, int index)181     String formatEventSummary(long now, int index) {
182         return Integer.toString(getTotalEvents(now, index));
183     }
184 
formatEventTypeLabel(int index)185     String formatEventTypeLabel(int index) {
186         return Integer.toString(index) + ":";
187     }
188 
189     @Override
toString()190     public String toString() {
191         return mPackageName + "/" + UserHandle.formatUid(mUid)
192                 + " totalEvents[0]=" + formatEventSummary(SystemClock.elapsedRealtime(), 0);
193     }
194 
195     interface Factory<T extends BaseAppStateEvents> {
createAppStateEvents(int uid, String packageName)196         T createAppStateEvents(int uid, String packageName);
createAppStateEvents(T other)197         T createAppStateEvents(T other);
198     }
199 
200     interface MaxTrackingDurationConfig {
201         /**
202          * @return The mximum duration we'd keep tracking.
203          */
getMaxTrackingDuration()204         long getMaxTrackingDuration();
205     }
206 }
207