Loading core/java/android/util/SparseSetArray.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 android.util; /** * A sparse array of ArraySets, which is suitable to hold userid->packages association. * * @hide */ public class SparseSetArray<T> { private final SparseArray<ArraySet<T>> mData = new SparseArray<>(); public SparseSetArray() { } /** * Add a value at index n. * @return FALSE when the value already existed at the given index, TRUE otherwise. */ public boolean add(int n, T value) { ArraySet<T> set = mData.get(n); if (set == null) { set = new ArraySet<>(); mData.put(n, set); } if (set.contains(value)) { return true; } set.add(value); return false; } /** * @return whether a value exists at index n. */ public boolean contains(int n, T value) { final ArraySet<T> set = mData.get(n); if (set == null) { return false; } return set.contains(value); } /** * Remove a value from index n. * @return TRUE when the value existed at the given index and removed, FALSE otherwise. */ public boolean remove(int n, T value) { final ArraySet<T> set = mData.get(n); if (set == null) { return false; } final boolean ret = set.remove(value); if (set.size() == 0) { mData.remove(n); } return ret; } /** * Remove all values from index n. */ public void remove(int n) { mData.remove(n); } public int size() { return mData.size(); } public int keyAt(int index) { return mData.keyAt(index); } public int sizeAt(int index) { final ArraySet<T> set = mData.valueAt(index); if (set == null) { return 0; } return set.size(); } public T valueAt(int intIndex, int valueIndex) { return mData.valueAt(intIndex).valueAt(valueIndex); } } core/proto/android/server/forceappstandbytracker.proto +15 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/server/statlogger.proto"; package com.android.server; option java_multiple_files = true; Loading Loading @@ -53,6 +55,17 @@ message ForceAppStandbyTrackerProto { // Whether force app standby for small battery device setting is enabled optional bool force_all_apps_standby_for_small_battery = 7; // Whether device is charging optional bool is_charging = 8; // Whether device is plugged in to the charger optional bool is_plugged_in = 8; // Performance stats. optional StatLoggerProto stats = 9; message ExemptedPackage { optional int32 userId = 1; optional string package_name = 2; } // Packages that are in the EXEMPT bucket. repeated ExemptedPackage exempted_packages = 10; } core/proto/android/server/statlogger.proto 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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. */ syntax = "proto2"; package com.android.server; option java_multiple_files = true; // Dump from StatLogger. message StatLoggerProto { message Event { optional int32 eventId = 1; optional string label = 2; optional int32 count = 3; optional int64 total_duration_micros = 4; } repeated Event events = 1; } services/core/java/com/android/server/ForceAppStandbyTracker.java +156 −5 Original line number Diff line number Diff line Loading @@ -22,6 +22,9 @@ import android.app.AppOpsManager; import android.app.AppOpsManager.PackageOps; import android.app.IActivityManager; import android.app.IUidObserver; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading @@ -42,6 +45,7 @@ import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.SparseSetArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; Loading @@ -50,6 +54,7 @@ import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage; import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages; import java.io.PrintWriter; Loading @@ -74,7 +79,7 @@ import java.util.List; */ public class ForceAppStandbyTracker { private static final String TAG = "ForceAppStandbyTracker"; private static final boolean DEBUG = false; private static final boolean DEBUG = true; @GuardedBy("ForceAppStandbyTracker.class") private static ForceAppStandbyTracker sInstance; Loading @@ -89,6 +94,8 @@ public class ForceAppStandbyTracker { AppOpsManager mAppOpsManager; IAppOpsService mAppOpsService; PowerManagerInternal mPowerManagerInternal; StandbyTracker mStandbyTracker; UsageStatsManagerInternal mUsageStatsManagerInternal; private final MyHandler mHandler; Loading @@ -113,6 +120,12 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") private int[] mTempWhitelistedAppIds = mPowerWhitelistedAllAppIds; /** * Per-user packages that are in the EXEMPT bucket. */ @GuardedBy("mLock") private final SparseSetArray<String> mExemptedPackages = new SparseSetArray<>(); @GuardedBy("mLock") final ArraySet<Listener> mListeners = new ArraySet<>(); Loading Loading @@ -146,6 +159,28 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") boolean mForcedAppStandbyEnabled; interface Stats { int UID_STATE_CHANGED = 0; int RUN_ANY_CHANGED = 1; int ALL_UNWHITELISTED = 2; int ALL_WHITELIST_CHANGED = 3; int TEMP_WHITELIST_CHANGED = 4; int EXEMPT_CHANGED = 5; int FORCE_ALL_CHANGED = 6; int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 7; } private final StatLogger mStatLogger = new StatLogger(new String[] { "UID_STATE_CHANGED", "RUN_ANY_CHANGED", "ALL_UNWHITELISTED", "ALL_WHITELIST_CHANGED", "TEMP_WHITELIST_CHANGED", "EXEMPT_CHANGED", "FORCE_ALL_CHANGED", "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED", }); @VisibleForTesting class FeatureFlagsObserver extends ContentObserver { FeatureFlagsObserver() { Loading @@ -162,12 +197,11 @@ public class ForceAppStandbyTracker { } boolean isForcedAppStandbyEnabled() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1; return injectGetGlobalSettingInt(Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1; } boolean isForcedAppStandbyForSmallBatteryEnabled() { return Settings.Global.getInt(mContext.getContentResolver(), return injectGetGlobalSettingInt( Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1; } Loading Loading @@ -258,6 +292,17 @@ public class ForceAppStandbyTracker { // only for affected app-ids. updateAllJobs(); // Note when an app is just put in the temp whitelist, we do *not* drain pending alarms. } /** * This is called when the EXEMPT bucket is updated. */ private void onExemptChanged(ForceAppStandbyTracker sender) { // This doesn't happen very often, so just re-evaluate all jobs / alarms. updateAllJobs(); unblockAllUnrestrictedAlarms(); } /** Loading Loading @@ -346,11 +391,16 @@ public class ForceAppStandbyTracker { mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager()); mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService()); mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal()); mUsageStatsManagerInternal = Preconditions.checkNotNull( injectUsageStatsManagerInternal()); mFlagsObserver = new FeatureFlagsObserver(); mFlagsObserver.register(); mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled(); mForceAllAppStandbyForSmallBattery = mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled(); mStandbyTracker = new StandbyTracker(); mUsageStatsManagerInternal.addAppIdleStateChangeListener(mStandbyTracker); try { mIActivityManager.registerUidObserver(new UidObserver(), Loading Loading @@ -407,11 +457,21 @@ public class ForceAppStandbyTracker { return LocalServices.getService(PowerManagerInternal.class); } @VisibleForTesting UsageStatsManagerInternal injectUsageStatsManagerInternal() { return LocalServices.getService(UsageStatsManagerInternal.class); } @VisibleForTesting boolean isSmallBatteryDevice() { return ActivityManager.isSmallBatteryDevice(); } @VisibleForTesting int injectGetGlobalSettingInt(String key, int def) { return Settings.Global.getInt(mContext.getContentResolver(), key, def); } /** * Update {@link #mRunAnyRestrictedPackages} with the current app ops state. */ Loading Loading @@ -604,6 +664,30 @@ public class ForceAppStandbyTracker { } } final class StandbyTracker extends AppIdleStateChangeListener { @Override public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) { if (DEBUG) { Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId + (idle ? " idle" : " active") + " " + bucket); } final boolean changed; if (bucket == UsageStatsManager.STANDBY_BUCKET_EXEMPTED) { changed = mExemptedPackages.add(userId, packageName); } else { changed = mExemptedPackages.remove(userId, packageName); } if (changed) { mHandler.notifyExemptChanged(); } } @Override public void onParoleStateChanged(boolean isParoleOn) { } } private Listener[] cloneListeners() { synchronized (mLock) { return mListeners.toArray(new Listener[mListeners.size()]); Loading @@ -619,6 +703,7 @@ public class ForceAppStandbyTracker { private static final int MSG_FORCE_ALL_CHANGED = 6; private static final int MSG_USER_REMOVED = 7; private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8; private static final int MSG_EXEMPT_CHANGED = 9; public MyHandler(Looper looper) { super(looper); Loading Loading @@ -652,6 +737,10 @@ public class ForceAppStandbyTracker { obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget(); } public void notifyExemptChanged() { obtainMessage(MSG_EXEMPT_CHANGED).sendToTarget(); } public void doUserRemoved(int userId) { obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget(); } Loading @@ -672,37 +761,57 @@ public class ForceAppStandbyTracker { } final ForceAppStandbyTracker sender = ForceAppStandbyTracker.this; long start = mStatLogger.getTime(); switch (msg.what) { case MSG_UID_STATE_CHANGED: for (Listener l : cloneListeners()) { l.onUidForegroundStateChanged(sender, msg.arg1); } mStatLogger.logDurationStat(Stats.UID_STATE_CHANGED, start); return; case MSG_RUN_ANY_CHANGED: for (Listener l : cloneListeners()) { l.onRunAnyAppOpsChanged(sender, msg.arg1, (String) msg.obj); } mStatLogger.logDurationStat(Stats.RUN_ANY_CHANGED, start); return; case MSG_ALL_UNWHITELISTED: for (Listener l : cloneListeners()) { l.onPowerSaveUnwhitelisted(sender); } mStatLogger.logDurationStat(Stats.ALL_UNWHITELISTED, start); return; case MSG_ALL_WHITELIST_CHANGED: for (Listener l : cloneListeners()) { l.onPowerSaveWhitelistedChanged(sender); } mStatLogger.logDurationStat(Stats.ALL_WHITELIST_CHANGED, start); return; case MSG_TEMP_WHITELIST_CHANGED: for (Listener l : cloneListeners()) { l.onTempPowerSaveWhitelistChanged(sender); } mStatLogger.logDurationStat(Stats.TEMP_WHITELIST_CHANGED, start); return; case MSG_EXEMPT_CHANGED: for (Listener l : cloneListeners()) { l.onExemptChanged(sender); } mStatLogger.logDurationStat(Stats.EXEMPT_CHANGED, start); return; case MSG_FORCE_ALL_CHANGED: for (Listener l : cloneListeners()) { l.onForceAllAppsStandbyChanged(sender); } mStatLogger.logDurationStat(Stats.FORCE_ALL_CHANGED, start); return; case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED: // Feature flag for forced app standby changed. final boolean unblockAlarms; Loading @@ -715,7 +824,10 @@ public class ForceAppStandbyTracker { l.unblockAllUnrestrictedAlarms(); } } mStatLogger.logDurationStat( Stats.FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED, start); return; case MSG_USER_REMOVED: handleUserRemoved(msg.arg1); return; Loading @@ -742,6 +854,7 @@ public class ForceAppStandbyTracker { mForegroundUids.removeAt(i); } } mExemptedPackages.remove(removedUserId); } } Loading Loading @@ -860,6 +973,10 @@ public class ForceAppStandbyTracker { if (exemptOnBatterySaver) { return false; } final int userId = UserHandle.getUserId(uid); if (mExemptedPackages.contains(userId, packageName)) { return false; } return mForceAllAppsStandby; } } Loading Loading @@ -965,6 +1082,23 @@ public class ForceAppStandbyTracker { pw.print("Temp whitelist appids: "); pw.println(Arrays.toString(mTempWhitelistedAppIds)); pw.print(indent); pw.println("Exempted packages:"); for (int i = 0; i < mExemptedPackages.size(); i++) { pw.print(indent); pw.print(" User "); pw.print(mExemptedPackages.keyAt(i)); pw.println(); for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) { pw.print(indent); pw.print(" "); pw.print(mExemptedPackages.valueAt(i, j)); pw.println(); } } pw.println(); pw.print(indent); pw.println("Restricted packages:"); for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) { Loading @@ -975,6 +1109,8 @@ public class ForceAppStandbyTracker { pw.print(uidAndPackage.second); pw.println(); } mStatLogger.dump(pw, indent); } } Loading @@ -987,7 +1123,7 @@ public class ForceAppStandbyTracker { isSmallBatteryDevice()); proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY, mForceAllAppStandbyForSmallBattery); proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn); proto.write(ForceAppStandbyTrackerProto.IS_PLUGGED_IN, mIsPluggedIn); for (int i = 0; i < mForegroundUids.size(); i++) { if (mForegroundUids.valueAt(i)) { Loading @@ -1004,6 +1140,18 @@ public class ForceAppStandbyTracker { proto.write(ForceAppStandbyTrackerProto.TEMP_POWER_SAVE_WHITELIST_APP_IDS, appId); } for (int i = 0; i < mExemptedPackages.size(); i++) { for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) { final long token2 = proto.start( ForceAppStandbyTrackerProto.EXEMPTED_PACKAGES); proto.write(ExemptedPackage.USER_ID, mExemptedPackages.keyAt(i)); proto.write(ExemptedPackage.PACKAGE_NAME, mExemptedPackages.valueAt(i, j)); proto.end(token2); } } for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) { final long token2 = proto.start( ForceAppStandbyTrackerProto.RUN_ANY_IN_BACKGROUND_RESTRICTED_PACKAGES); Loading @@ -1012,6 +1160,9 @@ public class ForceAppStandbyTracker { uidAndPackage.second); proto.end(token2); } mStatLogger.dumpProto(proto, ForceAppStandbyTrackerProto.STATS); proto.end(token); } } Loading services/core/java/com/android/server/StatLogger.java 0 → 100644 +108 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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; import android.os.SystemClock; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.server.StatLoggerProto.Event; import java.io.PrintWriter; /** * Simple class to keep track of the number of times certain events happened and their durations for * benchmarking. * * TODO Update shortcut service to switch to it. * * @hide */ public class StatLogger { private final Object mLock = new Object(); private final int SIZE; @GuardedBy("mLock") private final int[] mCountStats; @GuardedBy("mLock") private final long[] mDurationStats; private final String[] mLabels; public StatLogger(String[] eventLabels) { SIZE = eventLabels.length; mCountStats = new int[SIZE]; mDurationStats = new long[SIZE]; mLabels = eventLabels; } /** * Return the current time in the internal time unit. * Call it before an event happens, and * give it back to the {@link #logDurationStat(int, long)}} after the event. */ public long getTime() { return SystemClock.elapsedRealtimeNanos() / 1000; } /** * @see {@link #getTime()} */ public void logDurationStat(int eventId, long start) { synchronized (mLock) { mCountStats[eventId]++; mDurationStats[eventId] += (getTime() - start); } } public void dump(PrintWriter pw, String prefix) { synchronized (mLock) { pw.print(prefix); pw.println("Stats:"); for (int i = 0; i < SIZE; i++) { pw.print(prefix); pw.print(" "); final int count = mCountStats[i]; final double durationMs = mDurationStats[i] / 1000.0; pw.println(String.format("%s: count=%d, total=%.1fms, avg=%.3fms", mLabels[i], count, durationMs, (count == 0 ? 0 : ((double) durationMs) / count))); } } } public void dumpProto(ProtoOutputStream proto, long fieldId) { synchronized (mLock) { final long outer = proto.start(fieldId); for (int i = 0; i < mLabels.length; i++) { final long inner = proto.start(StatLoggerProto.EVENTS); proto.write(Event.EVENT_ID, i); proto.write(Event.LABEL, mLabels[i]); proto.write(Event.COUNT, mCountStats[i]); proto.write(Event.TOTAL_DURATION_MICROS, mDurationStats[i]); proto.end(inner); } proto.end(outer); } } } Loading
core/java/android/util/SparseSetArray.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 android.util; /** * A sparse array of ArraySets, which is suitable to hold userid->packages association. * * @hide */ public class SparseSetArray<T> { private final SparseArray<ArraySet<T>> mData = new SparseArray<>(); public SparseSetArray() { } /** * Add a value at index n. * @return FALSE when the value already existed at the given index, TRUE otherwise. */ public boolean add(int n, T value) { ArraySet<T> set = mData.get(n); if (set == null) { set = new ArraySet<>(); mData.put(n, set); } if (set.contains(value)) { return true; } set.add(value); return false; } /** * @return whether a value exists at index n. */ public boolean contains(int n, T value) { final ArraySet<T> set = mData.get(n); if (set == null) { return false; } return set.contains(value); } /** * Remove a value from index n. * @return TRUE when the value existed at the given index and removed, FALSE otherwise. */ public boolean remove(int n, T value) { final ArraySet<T> set = mData.get(n); if (set == null) { return false; } final boolean ret = set.remove(value); if (set.size() == 0) { mData.remove(n); } return ret; } /** * Remove all values from index n. */ public void remove(int n) { mData.remove(n); } public int size() { return mData.size(); } public int keyAt(int index) { return mData.keyAt(index); } public int sizeAt(int index) { final ArraySet<T> set = mData.valueAt(index); if (set == null) { return 0; } return set.size(); } public T valueAt(int intIndex, int valueIndex) { return mData.valueAt(intIndex).valueAt(valueIndex); } }
core/proto/android/server/forceappstandbytracker.proto +15 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/server/statlogger.proto"; package com.android.server; option java_multiple_files = true; Loading Loading @@ -53,6 +55,17 @@ message ForceAppStandbyTrackerProto { // Whether force app standby for small battery device setting is enabled optional bool force_all_apps_standby_for_small_battery = 7; // Whether device is charging optional bool is_charging = 8; // Whether device is plugged in to the charger optional bool is_plugged_in = 8; // Performance stats. optional StatLoggerProto stats = 9; message ExemptedPackage { optional int32 userId = 1; optional string package_name = 2; } // Packages that are in the EXEMPT bucket. repeated ExemptedPackage exempted_packages = 10; }
core/proto/android/server/statlogger.proto 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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. */ syntax = "proto2"; package com.android.server; option java_multiple_files = true; // Dump from StatLogger. message StatLoggerProto { message Event { optional int32 eventId = 1; optional string label = 2; optional int32 count = 3; optional int64 total_duration_micros = 4; } repeated Event events = 1; }
services/core/java/com/android/server/ForceAppStandbyTracker.java +156 −5 Original line number Diff line number Diff line Loading @@ -22,6 +22,9 @@ import android.app.AppOpsManager; import android.app.AppOpsManager.PackageOps; import android.app.IActivityManager; import android.app.IUidObserver; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading @@ -42,6 +45,7 @@ import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.SparseSetArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; Loading @@ -50,6 +54,7 @@ import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage; import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages; import java.io.PrintWriter; Loading @@ -74,7 +79,7 @@ import java.util.List; */ public class ForceAppStandbyTracker { private static final String TAG = "ForceAppStandbyTracker"; private static final boolean DEBUG = false; private static final boolean DEBUG = true; @GuardedBy("ForceAppStandbyTracker.class") private static ForceAppStandbyTracker sInstance; Loading @@ -89,6 +94,8 @@ public class ForceAppStandbyTracker { AppOpsManager mAppOpsManager; IAppOpsService mAppOpsService; PowerManagerInternal mPowerManagerInternal; StandbyTracker mStandbyTracker; UsageStatsManagerInternal mUsageStatsManagerInternal; private final MyHandler mHandler; Loading @@ -113,6 +120,12 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") private int[] mTempWhitelistedAppIds = mPowerWhitelistedAllAppIds; /** * Per-user packages that are in the EXEMPT bucket. */ @GuardedBy("mLock") private final SparseSetArray<String> mExemptedPackages = new SparseSetArray<>(); @GuardedBy("mLock") final ArraySet<Listener> mListeners = new ArraySet<>(); Loading Loading @@ -146,6 +159,28 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") boolean mForcedAppStandbyEnabled; interface Stats { int UID_STATE_CHANGED = 0; int RUN_ANY_CHANGED = 1; int ALL_UNWHITELISTED = 2; int ALL_WHITELIST_CHANGED = 3; int TEMP_WHITELIST_CHANGED = 4; int EXEMPT_CHANGED = 5; int FORCE_ALL_CHANGED = 6; int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 7; } private final StatLogger mStatLogger = new StatLogger(new String[] { "UID_STATE_CHANGED", "RUN_ANY_CHANGED", "ALL_UNWHITELISTED", "ALL_WHITELIST_CHANGED", "TEMP_WHITELIST_CHANGED", "EXEMPT_CHANGED", "FORCE_ALL_CHANGED", "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED", }); @VisibleForTesting class FeatureFlagsObserver extends ContentObserver { FeatureFlagsObserver() { Loading @@ -162,12 +197,11 @@ public class ForceAppStandbyTracker { } boolean isForcedAppStandbyEnabled() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1; return injectGetGlobalSettingInt(Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1; } boolean isForcedAppStandbyForSmallBatteryEnabled() { return Settings.Global.getInt(mContext.getContentResolver(), return injectGetGlobalSettingInt( Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1; } Loading Loading @@ -258,6 +292,17 @@ public class ForceAppStandbyTracker { // only for affected app-ids. updateAllJobs(); // Note when an app is just put in the temp whitelist, we do *not* drain pending alarms. } /** * This is called when the EXEMPT bucket is updated. */ private void onExemptChanged(ForceAppStandbyTracker sender) { // This doesn't happen very often, so just re-evaluate all jobs / alarms. updateAllJobs(); unblockAllUnrestrictedAlarms(); } /** Loading Loading @@ -346,11 +391,16 @@ public class ForceAppStandbyTracker { mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager()); mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService()); mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal()); mUsageStatsManagerInternal = Preconditions.checkNotNull( injectUsageStatsManagerInternal()); mFlagsObserver = new FeatureFlagsObserver(); mFlagsObserver.register(); mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled(); mForceAllAppStandbyForSmallBattery = mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled(); mStandbyTracker = new StandbyTracker(); mUsageStatsManagerInternal.addAppIdleStateChangeListener(mStandbyTracker); try { mIActivityManager.registerUidObserver(new UidObserver(), Loading Loading @@ -407,11 +457,21 @@ public class ForceAppStandbyTracker { return LocalServices.getService(PowerManagerInternal.class); } @VisibleForTesting UsageStatsManagerInternal injectUsageStatsManagerInternal() { return LocalServices.getService(UsageStatsManagerInternal.class); } @VisibleForTesting boolean isSmallBatteryDevice() { return ActivityManager.isSmallBatteryDevice(); } @VisibleForTesting int injectGetGlobalSettingInt(String key, int def) { return Settings.Global.getInt(mContext.getContentResolver(), key, def); } /** * Update {@link #mRunAnyRestrictedPackages} with the current app ops state. */ Loading Loading @@ -604,6 +664,30 @@ public class ForceAppStandbyTracker { } } final class StandbyTracker extends AppIdleStateChangeListener { @Override public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) { if (DEBUG) { Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId + (idle ? " idle" : " active") + " " + bucket); } final boolean changed; if (bucket == UsageStatsManager.STANDBY_BUCKET_EXEMPTED) { changed = mExemptedPackages.add(userId, packageName); } else { changed = mExemptedPackages.remove(userId, packageName); } if (changed) { mHandler.notifyExemptChanged(); } } @Override public void onParoleStateChanged(boolean isParoleOn) { } } private Listener[] cloneListeners() { synchronized (mLock) { return mListeners.toArray(new Listener[mListeners.size()]); Loading @@ -619,6 +703,7 @@ public class ForceAppStandbyTracker { private static final int MSG_FORCE_ALL_CHANGED = 6; private static final int MSG_USER_REMOVED = 7; private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8; private static final int MSG_EXEMPT_CHANGED = 9; public MyHandler(Looper looper) { super(looper); Loading Loading @@ -652,6 +737,10 @@ public class ForceAppStandbyTracker { obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget(); } public void notifyExemptChanged() { obtainMessage(MSG_EXEMPT_CHANGED).sendToTarget(); } public void doUserRemoved(int userId) { obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget(); } Loading @@ -672,37 +761,57 @@ public class ForceAppStandbyTracker { } final ForceAppStandbyTracker sender = ForceAppStandbyTracker.this; long start = mStatLogger.getTime(); switch (msg.what) { case MSG_UID_STATE_CHANGED: for (Listener l : cloneListeners()) { l.onUidForegroundStateChanged(sender, msg.arg1); } mStatLogger.logDurationStat(Stats.UID_STATE_CHANGED, start); return; case MSG_RUN_ANY_CHANGED: for (Listener l : cloneListeners()) { l.onRunAnyAppOpsChanged(sender, msg.arg1, (String) msg.obj); } mStatLogger.logDurationStat(Stats.RUN_ANY_CHANGED, start); return; case MSG_ALL_UNWHITELISTED: for (Listener l : cloneListeners()) { l.onPowerSaveUnwhitelisted(sender); } mStatLogger.logDurationStat(Stats.ALL_UNWHITELISTED, start); return; case MSG_ALL_WHITELIST_CHANGED: for (Listener l : cloneListeners()) { l.onPowerSaveWhitelistedChanged(sender); } mStatLogger.logDurationStat(Stats.ALL_WHITELIST_CHANGED, start); return; case MSG_TEMP_WHITELIST_CHANGED: for (Listener l : cloneListeners()) { l.onTempPowerSaveWhitelistChanged(sender); } mStatLogger.logDurationStat(Stats.TEMP_WHITELIST_CHANGED, start); return; case MSG_EXEMPT_CHANGED: for (Listener l : cloneListeners()) { l.onExemptChanged(sender); } mStatLogger.logDurationStat(Stats.EXEMPT_CHANGED, start); return; case MSG_FORCE_ALL_CHANGED: for (Listener l : cloneListeners()) { l.onForceAllAppsStandbyChanged(sender); } mStatLogger.logDurationStat(Stats.FORCE_ALL_CHANGED, start); return; case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED: // Feature flag for forced app standby changed. final boolean unblockAlarms; Loading @@ -715,7 +824,10 @@ public class ForceAppStandbyTracker { l.unblockAllUnrestrictedAlarms(); } } mStatLogger.logDurationStat( Stats.FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED, start); return; case MSG_USER_REMOVED: handleUserRemoved(msg.arg1); return; Loading @@ -742,6 +854,7 @@ public class ForceAppStandbyTracker { mForegroundUids.removeAt(i); } } mExemptedPackages.remove(removedUserId); } } Loading Loading @@ -860,6 +973,10 @@ public class ForceAppStandbyTracker { if (exemptOnBatterySaver) { return false; } final int userId = UserHandle.getUserId(uid); if (mExemptedPackages.contains(userId, packageName)) { return false; } return mForceAllAppsStandby; } } Loading Loading @@ -965,6 +1082,23 @@ public class ForceAppStandbyTracker { pw.print("Temp whitelist appids: "); pw.println(Arrays.toString(mTempWhitelistedAppIds)); pw.print(indent); pw.println("Exempted packages:"); for (int i = 0; i < mExemptedPackages.size(); i++) { pw.print(indent); pw.print(" User "); pw.print(mExemptedPackages.keyAt(i)); pw.println(); for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) { pw.print(indent); pw.print(" "); pw.print(mExemptedPackages.valueAt(i, j)); pw.println(); } } pw.println(); pw.print(indent); pw.println("Restricted packages:"); for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) { Loading @@ -975,6 +1109,8 @@ public class ForceAppStandbyTracker { pw.print(uidAndPackage.second); pw.println(); } mStatLogger.dump(pw, indent); } } Loading @@ -987,7 +1123,7 @@ public class ForceAppStandbyTracker { isSmallBatteryDevice()); proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY, mForceAllAppStandbyForSmallBattery); proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn); proto.write(ForceAppStandbyTrackerProto.IS_PLUGGED_IN, mIsPluggedIn); for (int i = 0; i < mForegroundUids.size(); i++) { if (mForegroundUids.valueAt(i)) { Loading @@ -1004,6 +1140,18 @@ public class ForceAppStandbyTracker { proto.write(ForceAppStandbyTrackerProto.TEMP_POWER_SAVE_WHITELIST_APP_IDS, appId); } for (int i = 0; i < mExemptedPackages.size(); i++) { for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) { final long token2 = proto.start( ForceAppStandbyTrackerProto.EXEMPTED_PACKAGES); proto.write(ExemptedPackage.USER_ID, mExemptedPackages.keyAt(i)); proto.write(ExemptedPackage.PACKAGE_NAME, mExemptedPackages.valueAt(i, j)); proto.end(token2); } } for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) { final long token2 = proto.start( ForceAppStandbyTrackerProto.RUN_ANY_IN_BACKGROUND_RESTRICTED_PACKAGES); Loading @@ -1012,6 +1160,9 @@ public class ForceAppStandbyTracker { uidAndPackage.second); proto.end(token2); } mStatLogger.dumpProto(proto, ForceAppStandbyTrackerProto.STATS); proto.end(token); } } Loading
services/core/java/com/android/server/StatLogger.java 0 → 100644 +108 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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; import android.os.SystemClock; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.server.StatLoggerProto.Event; import java.io.PrintWriter; /** * Simple class to keep track of the number of times certain events happened and their durations for * benchmarking. * * TODO Update shortcut service to switch to it. * * @hide */ public class StatLogger { private final Object mLock = new Object(); private final int SIZE; @GuardedBy("mLock") private final int[] mCountStats; @GuardedBy("mLock") private final long[] mDurationStats; private final String[] mLabels; public StatLogger(String[] eventLabels) { SIZE = eventLabels.length; mCountStats = new int[SIZE]; mDurationStats = new long[SIZE]; mLabels = eventLabels; } /** * Return the current time in the internal time unit. * Call it before an event happens, and * give it back to the {@link #logDurationStat(int, long)}} after the event. */ public long getTime() { return SystemClock.elapsedRealtimeNanos() / 1000; } /** * @see {@link #getTime()} */ public void logDurationStat(int eventId, long start) { synchronized (mLock) { mCountStats[eventId]++; mDurationStats[eventId] += (getTime() - start); } } public void dump(PrintWriter pw, String prefix) { synchronized (mLock) { pw.print(prefix); pw.println("Stats:"); for (int i = 0; i < SIZE; i++) { pw.print(prefix); pw.print(" "); final int count = mCountStats[i]; final double durationMs = mDurationStats[i] / 1000.0; pw.println(String.format("%s: count=%d, total=%.1fms, avg=%.3fms", mLabels[i], count, durationMs, (count == 0 ? 0 : ((double) durationMs) / count))); } } } public void dumpProto(ProtoOutputStream proto, long fieldId) { synchronized (mLock) { final long outer = proto.start(fieldId); for (int i = 0; i < mLabels.length; i++) { final long inner = proto.start(StatLoggerProto.EVENTS); proto.write(Event.EVENT_ID, i); proto.write(Event.LABEL, mLabels[i]); proto.write(Event.COUNT, mCountStats[i]); proto.write(Event.TOTAL_DURATION_MICROS, mDurationStats[i]); proto.end(inner); } proto.end(outer); } } }