Merge "cmd deviceidle tempwhitelist -r should clear FGS tempallowlist too" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-05-26 22:28:50 +00:00
committed by Android (Google) Code Review
5 changed files with 116 additions and 39 deletions

View File

@@ -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

View File

@@ -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(
@@ -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();
/**
@@ -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) {
@@ -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);
}

View File

@@ -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.
@@ -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();
@@ -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.
@@ -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));
}
}
}
@@ -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()) {
@@ -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);
}
}
}
}
}

View File

@@ -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);
}

View File

@@ -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
@@ -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");
@@ -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));
}
@@ -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.
@@ -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());
}
}