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