Loading apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +1 −1 Original line number Diff line number Diff line Loading @@ -547,7 +547,7 @@ public class DeviceIdleController extends SystemService private int[] mPowerSaveWhitelistUserAppIdArray = new int[0]; /** * List of end times for UIDs that are temporarily marked as being allowed to access * List of end times for app-IDs that are temporarily marked as being allowed to access * the network and acquire wakelocks. Times are in milliseconds. */ private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes Loading services/core/java/com/android/server/am/ActivityManagerService.java +15 −11 Original line number Diff line number Diff line Loading @@ -1236,7 +1236,7 @@ public class ActivityManagerService extends IActivityManager.Stub * The temp-allowlist that is allowed to start FGS from background. */ @CompositeRWLock({"this", "mProcLock"}) final FgsTempAllowList<Integer, FgsTempAllowListItem> mFgsStartTempAllowList = final FgsTempAllowList<FgsTempAllowListItem> mFgsStartTempAllowList = new FgsTempAllowList(); static final FgsTempAllowListItem FAKE_TEMP_ALLOW_LIST_ITEM = new FgsTempAllowListItem( Loading @@ -1246,7 +1246,7 @@ public class ActivityManagerService extends IActivityManager.Stub * List of uids that are allowed to have while-in-use permission when FGS is started from * background. */ private final FgsTempAllowList<Integer, String> mFgsWhileInUseTempAllowList = private final FgsTempAllowList<String> mFgsWhileInUseTempAllowList = new FgsTempAllowList(); /** Loading Loading @@ -9372,22 +9372,17 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" mFgsStartTempAllowList:"); final long currentTimeNow = System.currentTimeMillis(); final long elapsedRealtimeNow = SystemClock.elapsedRealtime(); final Set<Integer> uids = new ArraySet<>(mFgsStartTempAllowList.keySet()); for (Integer uid : uids) { final Pair<Long, FgsTempAllowListItem> entry = mFgsStartTempAllowList.get(uid); if (entry == null) { continue; } mFgsStartTempAllowList.forEach((uid, entry) -> { pw.print(" " + UserHandle.formatUid(uid) + ": "); entry.second.dump(pw); pw.println(); pw.print("ms expiration="); entry.second.dump(pw); pw.print(" expiration="); // Convert entry.mExpirationTime, which is an elapsed time since boot, // to a time since epoch (i.e. System.currentTimeMillis()-based time.) final long expirationInCurrentTime = currentTimeNow - elapsedRealtimeNow + entry.first; TimeUtils.dumpTimeWithDelta(pw, expirationInCurrentTime, currentTimeNow); pw.println(); } }); } if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { Loading Loading @@ -15345,10 +15340,19 @@ public class ActivityManagerService extends IActivityManager.Stub mDeviceIdleTempAllowlist = appids; if (adding) { if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { // Note, the device idle temp-allowlist are by app-ids, but here // mFgsStartTempAllowList contains UIDs. mFgsStartTempAllowList.add(changingUid, durationMs, new FgsTempAllowListItem(durationMs, reasonCode, reason, callingUid)); } } else { // Note in the removing case, we need to remove all the UIDs matching // the appId, because DeviceIdle's temp-allowlist are based on AppIds, // not UIDs. // For eacmple, "cmd deviceidle tempallowlist -r PACKAGE" will // not only remove this app for user 0, but for all users. mFgsStartTempAllowList.removeAppId(UserHandle.getAppId(changingUid)); } setAppIdTempAllowlistStateLSP(changingUid, adding); } services/core/java/com/android/server/am/FgsTempAllowList.java +46 −23 Original line number Diff line number Diff line Loading @@ -20,11 +20,12 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import android.annotation.Nullable; import android.os.SystemClock; import android.util.ArrayMap; import android.os.UserHandle; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import java.util.Set; import java.util.function.BiConsumer; /** * List of keys that have expiration time. Loading @@ -33,19 +34,18 @@ import java.util.Set; * * <p>This is used for both FGS-BG-start restriction, and FGS-while-in-use permissions check.</p> * * <p>Note: the underlying data structure is an {@link ArrayMap}, for performance reason, it is only * suitable to hold up to hundreds of entries.</p> * @param <K> type of the key. * <p>Note: the underlying data structure is an {@link SparseArray}, for performance reason, * it is only suitable to hold up to hundreds of entries.</p> * @param <E> type of the additional optional info. */ public class FgsTempAllowList<K, E> { public class FgsTempAllowList<E> { private static final int DEFAULT_MAX_SIZE = 100; /** * The value is Pair type, Pair.first is the expirationTime(an elapsedRealtime), * Pair.second is the optional information entry about this key. */ private final ArrayMap<K, Pair<Long, E>> mTempAllowList = new ArrayMap<>(); private final SparseArray<Pair<Long, E>> mTempAllowList = new SparseArray<>(); private int mMaxSize = DEFAULT_MAX_SIZE; private final Object mLock = new Object(); Loading @@ -70,15 +70,14 @@ public class FgsTempAllowList<K, E> { /** * Add a key and its duration with optional info into the temp allowlist. * @param key * @param durationMs temp-allowlisted duration in milliseconds. * @param entry additional optional information of this key, could be null. */ public void add(K key, long durationMs, @Nullable E entry) { public void add(int uid, long durationMs, @Nullable E entry) { synchronized (mLock) { if (durationMs <= 0) { Slog.e(TAG_AM, "FgsTempAllowList bad duration:" + durationMs + " key: " + key); + uid); return; } // The temp allowlist should be a short list with only a few entries in it. Loading @@ -94,10 +93,10 @@ public class FgsTempAllowList<K, E> { } } } final Pair<Long, E> existing = mTempAllowList.get(key); final Pair<Long, E> existing = mTempAllowList.get(uid); final long expirationTime = now + durationMs; if (existing == null || existing.first < expirationTime) { mTempAllowList.put(key, new Pair(expirationTime, entry)); mTempAllowList.put(uid, new Pair(expirationTime, entry)); } } } Loading @@ -105,13 +104,12 @@ public class FgsTempAllowList<K, E> { /** * If the key has not expired (AKA allowed), return its non-null value. * If the key has expired, return null. * @param key * @return */ @Nullable public Pair<Long, E> get(K key) { public Pair<Long, E> get(int uid) { synchronized (mLock) { final int index = mTempAllowList.indexOfKey(key); final int index = mTempAllowList.indexOfKey(uid); if (index < 0) { return null; } else if (mTempAllowList.valueAt(index).first < SystemClock.elapsedRealtime()) { Loading @@ -126,23 +124,48 @@ public class FgsTempAllowList<K, E> { /** * If the key has not expired (AKA allowed), return true. * If the key has expired, return false. * @param key * @return */ public boolean isAllowed(K key) { Pair<Long, E> entry = get(key); public boolean isAllowed(int uid) { Pair<Long, E> entry = get(uid); return entry != null; } public void remove(K key) { /** * Remove a given UID. */ public void removeUid(int uid) { synchronized (mLock) { mTempAllowList.remove(key); mTempAllowList.remove(uid); } } public Set<K> keySet() { /** * Remove by appId. */ public void removeAppId(int appId) { synchronized (mLock) { return mTempAllowList.keySet(); // Find all UIDs matching the appId. for (int i = mTempAllowList.size() - 1; i >= 0; i--) { final int uid = mTempAllowList.keyAt(i); if (UserHandle.getAppId(uid) == appId) { mTempAllowList.removeAt(i); } } } } /** * Iterate over the entries. */ public void forEach(BiConsumer<Integer, Pair<Long, E>> callback) { synchronized (mLock) { for (int i = 0; i < mTempAllowList.size(); i++) { final int uid = mTempAllowList.keyAt(i); final Pair<Long, E> entry = mTempAllowList.valueAt(i); if (entry != null) { callback.accept(uid, entry); } } } } } services/core/java/com/android/server/am/ProcessList.java +1 −1 Original line number Diff line number Diff line Loading @@ -3074,7 +3074,7 @@ public final class ProcessList { UidRecord.CHANGE_GONE); EventLogTags.writeAmUidStopped(uid); mActiveUids.remove(uid); mService.mFgsStartTempAllowList.remove(record.info.uid); mService.mFgsStartTempAllowList.removeUid(record.info.uid); mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT, ActivityManager.PROCESS_CAPABILITY_NONE); } Loading services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java +53 −3 Original line number Diff line number Diff line Loading @@ -29,6 +29,9 @@ import android.util.Pair; import org.junit.Test; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; /** * Build/Install/Run: * atest FrameworksServicesTests:TempAllowListTest Loading @@ -41,7 +44,7 @@ public class FgsTempAllowListTest { */ @Test public void testIsAllowed() { FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList(); FgsTempAllowList<String> allowList = new FgsTempAllowList(); allowList.add(10001, 2000, "description1"); allowList.add(10002, 2000, "description2"); Loading @@ -55,7 +58,7 @@ public class FgsTempAllowListTest { assertNotNull(entry2); assertEquals(entry2.second, "description2"); allowList.remove(10001); allowList.removeUid(10001); assertFalse(allowList.isAllowed(10001)); assertNull(allowList.get(10001)); } Loading @@ -65,7 +68,7 @@ public class FgsTempAllowListTest { */ @Test public void testExpired() { FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList(); FgsTempAllowList<String> allowList = new FgsTempAllowList(); // temp allow for 2000ms. allowList.add(10001, 2000, "uid1-2000ms"); // sleep for 3000ms. Loading @@ -74,4 +77,51 @@ public class FgsTempAllowListTest { assertFalse(allowList.isAllowed(10001)); assertNull(allowList.get(10001)); } @Test public void testRemoveAppId() { FgsTempAllowList<String> allowList = new FgsTempAllowList(); allowList.add(10001, 2000, "description1"); allowList.add(10002, 2000, "description2"); allowList.add(10_10001, 2000, "description3"); assertTrue(allowList.isAllowed(10001)); assertTrue(allowList.isAllowed(10002)); assertTrue(allowList.isAllowed(10_10001)); allowList.removeAppId(10001); assertFalse(allowList.isAllowed(10001)); assertTrue(allowList.isAllowed(10002)); assertFalse(allowList.isAllowed(10_10001)); } @Test public void testForEach() { final FgsTempAllowList<String> allowList = new FgsTempAllowList(); // Call forEach(), return the sum of all the UIDs, and make sure the item is // "uid" + uid. final Supplier<Integer> callForEach = () -> { final AtomicInteger sum = new AtomicInteger(); sum.set(0); allowList.forEach((uid, entry) -> { sum.set(sum.get() + uid); assertEquals(entry.second, "uid" + uid); }); return sum.get(); }; // Call on th empty list. assertEquals(0, (int) callForEach.get()); // Add one item. allowList.add(1, 2000, "uid1"); assertEquals(1, (int) callForEach.get()); // Add one more item. allowList.add(10, 2000, "uid10"); assertEquals(11, (int) callForEach.get()); } } Loading
apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +1 −1 Original line number Diff line number Diff line Loading @@ -547,7 +547,7 @@ public class DeviceIdleController extends SystemService private int[] mPowerSaveWhitelistUserAppIdArray = new int[0]; /** * List of end times for UIDs that are temporarily marked as being allowed to access * List of end times for app-IDs that are temporarily marked as being allowed to access * the network and acquire wakelocks. Times are in milliseconds. */ private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes Loading
services/core/java/com/android/server/am/ActivityManagerService.java +15 −11 Original line number Diff line number Diff line Loading @@ -1236,7 +1236,7 @@ public class ActivityManagerService extends IActivityManager.Stub * The temp-allowlist that is allowed to start FGS from background. */ @CompositeRWLock({"this", "mProcLock"}) final FgsTempAllowList<Integer, FgsTempAllowListItem> mFgsStartTempAllowList = final FgsTempAllowList<FgsTempAllowListItem> mFgsStartTempAllowList = new FgsTempAllowList(); static final FgsTempAllowListItem FAKE_TEMP_ALLOW_LIST_ITEM = new FgsTempAllowListItem( Loading @@ -1246,7 +1246,7 @@ public class ActivityManagerService extends IActivityManager.Stub * List of uids that are allowed to have while-in-use permission when FGS is started from * background. */ private final FgsTempAllowList<Integer, String> mFgsWhileInUseTempAllowList = private final FgsTempAllowList<String> mFgsWhileInUseTempAllowList = new FgsTempAllowList(); /** Loading Loading @@ -9372,22 +9372,17 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" mFgsStartTempAllowList:"); final long currentTimeNow = System.currentTimeMillis(); final long elapsedRealtimeNow = SystemClock.elapsedRealtime(); final Set<Integer> uids = new ArraySet<>(mFgsStartTempAllowList.keySet()); for (Integer uid : uids) { final Pair<Long, FgsTempAllowListItem> entry = mFgsStartTempAllowList.get(uid); if (entry == null) { continue; } mFgsStartTempAllowList.forEach((uid, entry) -> { pw.print(" " + UserHandle.formatUid(uid) + ": "); entry.second.dump(pw); pw.println(); pw.print("ms expiration="); entry.second.dump(pw); pw.print(" expiration="); // Convert entry.mExpirationTime, which is an elapsed time since boot, // to a time since epoch (i.e. System.currentTimeMillis()-based time.) final long expirationInCurrentTime = currentTimeNow - elapsedRealtimeNow + entry.first; TimeUtils.dumpTimeWithDelta(pw, expirationInCurrentTime, currentTimeNow); pw.println(); } }); } if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { Loading Loading @@ -15345,10 +15340,19 @@ public class ActivityManagerService extends IActivityManager.Stub mDeviceIdleTempAllowlist = appids; if (adding) { if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { // Note, the device idle temp-allowlist are by app-ids, but here // mFgsStartTempAllowList contains UIDs. mFgsStartTempAllowList.add(changingUid, durationMs, new FgsTempAllowListItem(durationMs, reasonCode, reason, callingUid)); } } else { // Note in the removing case, we need to remove all the UIDs matching // the appId, because DeviceIdle's temp-allowlist are based on AppIds, // not UIDs. // For eacmple, "cmd deviceidle tempallowlist -r PACKAGE" will // not only remove this app for user 0, but for all users. mFgsStartTempAllowList.removeAppId(UserHandle.getAppId(changingUid)); } setAppIdTempAllowlistStateLSP(changingUid, adding); }
services/core/java/com/android/server/am/FgsTempAllowList.java +46 −23 Original line number Diff line number Diff line Loading @@ -20,11 +20,12 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import android.annotation.Nullable; import android.os.SystemClock; import android.util.ArrayMap; import android.os.UserHandle; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import java.util.Set; import java.util.function.BiConsumer; /** * List of keys that have expiration time. Loading @@ -33,19 +34,18 @@ import java.util.Set; * * <p>This is used for both FGS-BG-start restriction, and FGS-while-in-use permissions check.</p> * * <p>Note: the underlying data structure is an {@link ArrayMap}, for performance reason, it is only * suitable to hold up to hundreds of entries.</p> * @param <K> type of the key. * <p>Note: the underlying data structure is an {@link SparseArray}, for performance reason, * it is only suitable to hold up to hundreds of entries.</p> * @param <E> type of the additional optional info. */ public class FgsTempAllowList<K, E> { public class FgsTempAllowList<E> { private static final int DEFAULT_MAX_SIZE = 100; /** * The value is Pair type, Pair.first is the expirationTime(an elapsedRealtime), * Pair.second is the optional information entry about this key. */ private final ArrayMap<K, Pair<Long, E>> mTempAllowList = new ArrayMap<>(); private final SparseArray<Pair<Long, E>> mTempAllowList = new SparseArray<>(); private int mMaxSize = DEFAULT_MAX_SIZE; private final Object mLock = new Object(); Loading @@ -70,15 +70,14 @@ public class FgsTempAllowList<K, E> { /** * Add a key and its duration with optional info into the temp allowlist. * @param key * @param durationMs temp-allowlisted duration in milliseconds. * @param entry additional optional information of this key, could be null. */ public void add(K key, long durationMs, @Nullable E entry) { public void add(int uid, long durationMs, @Nullable E entry) { synchronized (mLock) { if (durationMs <= 0) { Slog.e(TAG_AM, "FgsTempAllowList bad duration:" + durationMs + " key: " + key); + uid); return; } // The temp allowlist should be a short list with only a few entries in it. Loading @@ -94,10 +93,10 @@ public class FgsTempAllowList<K, E> { } } } final Pair<Long, E> existing = mTempAllowList.get(key); final Pair<Long, E> existing = mTempAllowList.get(uid); final long expirationTime = now + durationMs; if (existing == null || existing.first < expirationTime) { mTempAllowList.put(key, new Pair(expirationTime, entry)); mTempAllowList.put(uid, new Pair(expirationTime, entry)); } } } Loading @@ -105,13 +104,12 @@ public class FgsTempAllowList<K, E> { /** * If the key has not expired (AKA allowed), return its non-null value. * If the key has expired, return null. * @param key * @return */ @Nullable public Pair<Long, E> get(K key) { public Pair<Long, E> get(int uid) { synchronized (mLock) { final int index = mTempAllowList.indexOfKey(key); final int index = mTempAllowList.indexOfKey(uid); if (index < 0) { return null; } else if (mTempAllowList.valueAt(index).first < SystemClock.elapsedRealtime()) { Loading @@ -126,23 +124,48 @@ public class FgsTempAllowList<K, E> { /** * If the key has not expired (AKA allowed), return true. * If the key has expired, return false. * @param key * @return */ public boolean isAllowed(K key) { Pair<Long, E> entry = get(key); public boolean isAllowed(int uid) { Pair<Long, E> entry = get(uid); return entry != null; } public void remove(K key) { /** * Remove a given UID. */ public void removeUid(int uid) { synchronized (mLock) { mTempAllowList.remove(key); mTempAllowList.remove(uid); } } public Set<K> keySet() { /** * Remove by appId. */ public void removeAppId(int appId) { synchronized (mLock) { return mTempAllowList.keySet(); // Find all UIDs matching the appId. for (int i = mTempAllowList.size() - 1; i >= 0; i--) { final int uid = mTempAllowList.keyAt(i); if (UserHandle.getAppId(uid) == appId) { mTempAllowList.removeAt(i); } } } } /** * Iterate over the entries. */ public void forEach(BiConsumer<Integer, Pair<Long, E>> callback) { synchronized (mLock) { for (int i = 0; i < mTempAllowList.size(); i++) { final int uid = mTempAllowList.keyAt(i); final Pair<Long, E> entry = mTempAllowList.valueAt(i); if (entry != null) { callback.accept(uid, entry); } } } } }
services/core/java/com/android/server/am/ProcessList.java +1 −1 Original line number Diff line number Diff line Loading @@ -3074,7 +3074,7 @@ public final class ProcessList { UidRecord.CHANGE_GONE); EventLogTags.writeAmUidStopped(uid); mActiveUids.remove(uid); mService.mFgsStartTempAllowList.remove(record.info.uid); mService.mFgsStartTempAllowList.removeUid(record.info.uid); mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT, ActivityManager.PROCESS_CAPABILITY_NONE); } Loading
services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java +53 −3 Original line number Diff line number Diff line Loading @@ -29,6 +29,9 @@ import android.util.Pair; import org.junit.Test; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; /** * Build/Install/Run: * atest FrameworksServicesTests:TempAllowListTest Loading @@ -41,7 +44,7 @@ public class FgsTempAllowListTest { */ @Test public void testIsAllowed() { FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList(); FgsTempAllowList<String> allowList = new FgsTempAllowList(); allowList.add(10001, 2000, "description1"); allowList.add(10002, 2000, "description2"); Loading @@ -55,7 +58,7 @@ public class FgsTempAllowListTest { assertNotNull(entry2); assertEquals(entry2.second, "description2"); allowList.remove(10001); allowList.removeUid(10001); assertFalse(allowList.isAllowed(10001)); assertNull(allowList.get(10001)); } Loading @@ -65,7 +68,7 @@ public class FgsTempAllowListTest { */ @Test public void testExpired() { FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList(); FgsTempAllowList<String> allowList = new FgsTempAllowList(); // temp allow for 2000ms. allowList.add(10001, 2000, "uid1-2000ms"); // sleep for 3000ms. Loading @@ -74,4 +77,51 @@ public class FgsTempAllowListTest { assertFalse(allowList.isAllowed(10001)); assertNull(allowList.get(10001)); } @Test public void testRemoveAppId() { FgsTempAllowList<String> allowList = new FgsTempAllowList(); allowList.add(10001, 2000, "description1"); allowList.add(10002, 2000, "description2"); allowList.add(10_10001, 2000, "description3"); assertTrue(allowList.isAllowed(10001)); assertTrue(allowList.isAllowed(10002)); assertTrue(allowList.isAllowed(10_10001)); allowList.removeAppId(10001); assertFalse(allowList.isAllowed(10001)); assertTrue(allowList.isAllowed(10002)); assertFalse(allowList.isAllowed(10_10001)); } @Test public void testForEach() { final FgsTempAllowList<String> allowList = new FgsTempAllowList(); // Call forEach(), return the sum of all the UIDs, and make sure the item is // "uid" + uid. final Supplier<Integer> callForEach = () -> { final AtomicInteger sum = new AtomicInteger(); sum.set(0); allowList.forEach((uid, entry) -> { sum.set(sum.get() + uid); assertEquals(entry.second, "uid" + uid); }); return sum.get(); }; // Call on th empty list. assertEquals(0, (int) callForEach.get()); // Add one item. allowList.add(1, 2000, "uid1"); assertEquals(1, (int) callForEach.get()); // Add one more item. allowList.add(10, 2000, "uid10"); assertEquals(11, (int) callForEach.get()); } }