blob: c6900b279c45fb9abad7a79d0c96ed51a6c6c35c [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.am;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.Context;
import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.am.BaseAppStateEvents.MaxTrackingDurationConfig;
import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.LinkedList;
/**
* Base class to track certain state event of apps.
*/
abstract class BaseAppStateEventsTracker
<T extends BaseAppStateEventsPolicy, U extends BaseAppStateEvents>
extends BaseAppStateTracker<T> implements BaseAppStateEvents.Factory<U> {
static final boolean DEBUG_BASE_APP_STATE_EVENTS_TRACKER = false;
@GuardedBy("mLock")
final UidProcessMap<U> mPkgEvents = new UidProcessMap<>();
@GuardedBy("mLock")
final ArraySet<Integer> mTopUids = new ArraySet<>();
BaseAppStateEventsTracker(Context context, AppRestrictionController controller,
Constructor<? extends Injector<T>> injector, Object outerContext) {
super(context, controller, injector, outerContext);
}
@VisibleForTesting
void reset() {
synchronized (mLock) {
mPkgEvents.clear();
mTopUids.clear();
}
}
@GuardedBy("mLock")
U getUidEventsLocked(int uid) {
U events = null;
final ArrayMap<String, U> map = mPkgEvents.getMap().get(uid);
if (map == null) {
return null;
}
for (int i = map.size() - 1; i >= 0; i--) {
final U event = map.valueAt(i);
if (event != null) {
if (events == null) {
events = createAppStateEvents(uid, event.mPackageName);
}
events.add(event);
}
}
return events;
}
void trim(long earliest) {
synchronized (mLock) {
trimLocked(earliest);
}
}
@GuardedBy("mLock")
void trimLocked(long earliest) {
final SparseArray<ArrayMap<String, U>> map = mPkgEvents.getMap();
for (int i = map.size() - 1; i >= 0; i--) {
final ArrayMap<String, U> val = map.valueAt(i);
for (int j = val.size() - 1; j >= 0; j--) {
final U v = val.valueAt(j);
v.trim(earliest);
if (v.isEmpty()) {
val.removeAt(j);
}
}
if (val.size() == 0) {
map.removeAt(i);
}
}
}
boolean isUidOnTop(int uid) {
synchronized (mLock) {
return mTopUids.contains(uid);
}
}
@GuardedBy("mLock")
void onUntrackingUidLocked(int uid) {
}
@Override
void onUidProcStateChanged(final int uid, final int procState) {
synchronized (mLock) {
if (mPkgEvents.getMap().indexOfKey(uid) < 0) {
// If we're not tracking its events, ignore its UID state changes.
return;
}
onUidProcStateChangedUncheckedLocked(uid, procState);
}
}
@GuardedBy("mLock")
void onUidProcStateChangedUncheckedLocked(final int uid, final int procState) {
if (procState < ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
mTopUids.add(uid);
} else {
mTopUids.remove(uid);
}
}
@Override
void onUidGone(final int uid) {
synchronized (mLock) {
mTopUids.remove(uid);
}
}
@Override
void onUidRemoved(final int uid) {
synchronized (mLock) {
mPkgEvents.getMap().remove(uid);
onUntrackingUidLocked(uid);
}
}
@Override
void onUserRemoved(final @UserIdInt int userId) {
synchronized (mLock) {
final SparseArray<ArrayMap<String, U>> map = mPkgEvents.getMap();
for (int i = map.size() - 1; i >= 0; i--) {
final int uid = map.keyAt(i);
if (UserHandle.getUserId(uid) == userId) {
map.removeAt(i);
onUntrackingUidLocked(uid);
}
}
}
}
@Override
void dump(PrintWriter pw, String prefix) {
final T policy = mInjector.getPolicy();
synchronized (mLock) {
final long now = SystemClock.elapsedRealtime();
final SparseArray<ArrayMap<String, U>> map = mPkgEvents.getMap();
for (int i = map.size() - 1; i >= 0; i--) {
final int uid = map.keyAt(i);
final ArrayMap<String, U> val = map.valueAt(i);
for (int j = val.size() - 1; j >= 0; j--) {
final String packageName = val.keyAt(j);
final U events = val.valueAt(j);
dumpEventHeaderLocked(pw, prefix, packageName, uid, events, policy);
dumpEventLocked(pw, prefix, events, now);
}
}
}
dumpOthers(pw, prefix);
policy.dump(pw, prefix);
}
void dumpOthers(PrintWriter pw, String prefix) {
}
@GuardedBy("mLock")
void dumpEventHeaderLocked(PrintWriter pw, String prefix, String packageName, int uid, U events,
T policy) {
pw.print(prefix);
pw.print("* ");
pw.print(packageName);
pw.print('/');
pw.print(UserHandle.formatUid(uid));
pw.print(" exemption=");
pw.println(policy.getExemptionReasonString(packageName, uid, events.mExemptReason));
}
@GuardedBy("mLock")
void dumpEventLocked(PrintWriter pw, String prefix, U events, long now) {
events.dump(pw, " " + prefix, now);
}
abstract static class BaseAppStateEventsPolicy<V extends BaseAppStateEventsTracker>
extends BaseAppStatePolicy<V> implements MaxTrackingDurationConfig {
/**
* The key to the maximum duration we'd keep tracking, events earlier than that
* will be discarded.
*/
final @NonNull String mKeyMaxTrackingDuration;
/**
* The default to the {@link #mMaxTrackingDuration}.
*/
final long mDefaultMaxTrackingDuration;
/**
* The maximum duration we'd keep tracking, events earlier than that will be discarded.
*/
volatile long mMaxTrackingDuration;
BaseAppStateEventsPolicy(@NonNull Injector<?> injector, @NonNull V tracker,
@NonNull String keyTrackerEnabled, boolean defaultTrackerEnabled,
@NonNull String keyMaxTrackingDuration, long defaultMaxTrackingDuration) {
super(injector, tracker, keyTrackerEnabled, defaultTrackerEnabled);
mKeyMaxTrackingDuration = keyMaxTrackingDuration;
mDefaultMaxTrackingDuration = defaultMaxTrackingDuration;
}
@Override
public void onPropertiesChanged(String name) {
if (mKeyMaxTrackingDuration.equals(name)) {
updateMaxTrackingDuration();
} else {
super.onPropertiesChanged(name);
}
}
@Override
public void onSystemReady() {
super.onSystemReady();
updateMaxTrackingDuration();
}
/**
* Called when the maximum duration we'd keep tracking has been changed.
*/
public abstract void onMaxTrackingDurationChanged(long maxDuration);
void updateMaxTrackingDuration() {
long max = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
mKeyMaxTrackingDuration, mDefaultMaxTrackingDuration);
if (max != mMaxTrackingDuration) {
mMaxTrackingDuration = max;
onMaxTrackingDurationChanged(max);
}
}
@Override
public long getMaxTrackingDuration() {
return mMaxTrackingDuration;
}
String getExemptionReasonString(String packageName, int uid, @ReasonCode int reason) {
return PowerExemptionManager.reasonCodeToString(reason);
}
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
if (isEnabled()) {
pw.print(prefix);
pw.print(mKeyMaxTrackingDuration);
pw.print('=');
pw.println(mMaxTrackingDuration);
}
}
}
/**
* Simple event table, with only one track of events.
*/
static class SimplePackageEvents extends BaseAppStateTimeEvents {
static final int DEFAULT_INDEX = 0;
SimplePackageEvents(int uid, String packageName,
MaxTrackingDurationConfig maxTrackingDurationConfig) {
super(uid, packageName, 1, TAG, maxTrackingDurationConfig);
mEvents[DEFAULT_INDEX] = new LinkedList<Long>();
}
long getTotalEvents(long now) {
return getTotalEvents(now, DEFAULT_INDEX);
}
long getTotalEventsSince(long since, long now) {
return getTotalEventsSince(since, now, DEFAULT_INDEX);
}
@Override
String formatEventTypeLabel(int index) {
return "";
}
}
}