Create multiple temp allowlist types.

1. In BroadcastOptions, create multiple temp allowlist types,
TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED is the default which
allows both DeviceIdleController temp allowlist behavior and FGS start
 from background.
TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED
 only allows DeviceIdleController temp allowlist behavior.
2. Create FgsStartTempAllowList class to contain the UIDs that is
temporarily allowed to start FGS from background.

Bug: 171305836
Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testTempAllowListType
Change-Id: I32a14d675550cd262976bb8e16a48a070387d318
This commit is contained in:
Hui Yu
2020-12-02 09:37:23 -08:00
parent 29bca68828
commit cc8af26b01
6 changed files with 164 additions and 10 deletions

View File

@@ -607,7 +607,10 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
method public void setDontSendToRestrictedApps(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long);
method public android.os.Bundle toBundle();
field public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
field public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
}
public class DownloadManager {

View File

@@ -16,11 +16,15 @@
package android.app;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.Build;
import android.os.Bundle;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Helper class for building an options Bundle that can be used with
* {@link android.content.Context#sendBroadcast(android.content.Intent)
@@ -30,6 +34,7 @@ import android.os.Bundle;
@SystemApi
public class BroadcastOptions {
private long mTemporaryAppWhitelistDuration;
private @TempAllowListType int mTemporaryAppWhitelistType;
private int mMinManifestReceiverApiLevel = 0;
private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
private boolean mDontSendToRestrictedApps = false;
@@ -42,6 +47,9 @@ public class BroadcastOptions {
static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
= "android:broadcast.temporaryAppWhitelistDuration";
static final String KEY_TEMPORARY_APP_WHITELIST_TYPE
= "android:broadcast.temporaryAppWhitelistType";
/**
* Corresponds to {@link #setMinManifestReceiverApiLevel}.
*/
@@ -66,6 +74,27 @@ public class BroadcastOptions {
static final String KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS =
"android:broadcast.allowBackgroundActivityStarts";
/**
* Allow the temp allowlist behavior, plus allow foreground service start from background.
*/
public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
/**
* Only allow the temp allowlist behavior, not allow foreground service start from
* background.
*/
public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
/**
* The list of temp allowlist types.
* @hide
*/
@IntDef(flag = true, prefix = { "TEMPORARY_WHITELIST_TYPE_" }, value = {
TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface TempAllowListType {}
public static BroadcastOptions makeBasic() {
BroadcastOptions opts = new BroadcastOptions();
return opts;
@@ -77,6 +106,7 @@ public class BroadcastOptions {
/** @hide */
public BroadcastOptions(Bundle opts) {
mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION);
mTemporaryAppWhitelistType = opts.getInt(KEY_TEMPORARY_APP_WHITELIST_TYPE);
mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
Build.VERSION_CODES.CUR_DEVELOPMENT);
@@ -95,6 +125,22 @@ public class BroadcastOptions {
android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
public void setTemporaryAppWhitelistDuration(long duration) {
mTemporaryAppWhitelistDuration = duration;
mTemporaryAppWhitelistType = TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
}
/**
* Set a duration for which the system should temporary place an application on the
* power allowlist when this broadcast is being delivered to it, specify the temp allowlist
* type.
* @param type one of {@link TempAllowListType}
* @param duration the duration in milliseconds; 0 means to not place on allowlist.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
public void setTemporaryAppWhitelistDuration(@TempAllowListType int type, long duration) {
mTemporaryAppWhitelistDuration = duration;
mTemporaryAppWhitelistType = type;
}
/**
@@ -105,6 +151,14 @@ public class BroadcastOptions {
return mTemporaryAppWhitelistDuration;
}
/**
* Return {@link #mTemporaryAppWhitelistType}.
* @hide
*/
public @TempAllowListType int getTemporaryAppWhitelistType() {
return mTemporaryAppWhitelistType;
}
/**
* Set the minimum target API level of receivers of the broadcast. If an application
* is targeting an API level less than this, the broadcast will not be delivered to
@@ -190,6 +244,9 @@ public class BroadcastOptions {
if (mTemporaryAppWhitelistDuration > 0) {
b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration);
}
if (mTemporaryAppWhitelistType != 0) {
b.putInt(KEY_TEMPORARY_APP_WHITELIST_TYPE, mTemporaryAppWhitelistType);
}
if (mMinManifestReceiverApiLevel != 0) {
b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel);
}

View File

@@ -50,6 +50,7 @@ import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.ApplicationExitInfo;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.Notification;
@@ -3201,7 +3202,8 @@ public final class ActiveServices {
+ " for fg-service launch");
}
mAm.tempWhitelistUidLocked(r.appInfo.uid,
SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch");
SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch",
BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED);
}
if (!mPendingServices.contains(r)) {
@@ -5284,8 +5286,9 @@ public final class ActiveServices {
if (ret == FGS_FEATURE_DENIED) {
if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
&& mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
// uid is on DeviceIdleController's allowlist.
&& mAm.isWhitelistedForFgsStartLocked(r.appInfo.uid)) {
// uid is on DeviceIdleController's user/system allowlist
// or AMS's FgsStartTempAllowList.
ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
}
}

View File

@@ -1096,6 +1096,11 @@ public class ActivityManagerService extends IActivityManager.Stub
final PendingTempWhitelists mPendingTempWhitelist = new PendingTempWhitelists(this);
/**
* The temp-allowlist that is allowed to start FGS from background.
*/
final FgsStartTempAllowList mFgsStartTempAllowList = new FgsStartTempAllowList();
/**
* Information about and control over application operations
*/
@@ -5513,6 +5518,12 @@ public class ActivityManagerService extends IActivityManager.Stub
|| mPendingTempWhitelist.indexOfKey(uid) >= 0;
}
boolean isWhitelistedForFgsStartLocked(int uid) {
final int appId = UserHandle.getAppId(uid);
return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, appId) >= 0
|| mFgsStartTempAllowList.isAllowed(uid);
}
/**
* @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
* the whitelist
@@ -15327,17 +15338,22 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
tempWhitelistUidLocked(targetUid, duration, tag);
tempWhitelistUidLocked(targetUid, duration, tag,
BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED);
}
/**
* Whitelists {@code targetUid} to temporarily bypass Power Save mode.
*/
@GuardedBy("this")
void tempWhitelistUidLocked(int targetUid, long duration, String tag) {
void tempWhitelistUidLocked(int targetUid, long duration, String tag, int type) {
mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag));
setUidTempWhitelistStateLocked(targetUid, true);
mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget();
if (type == BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
mFgsStartTempAllowList.add(targetUid, duration);
}
}
void pushTempWhitelist() {

View File

@@ -896,7 +896,8 @@ public final class BroadcastQueue {
return false;
}
final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r) {
final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r,
@BroadcastOptions.TempAllowListType int type) {
if (duration > Integer.MAX_VALUE) {
duration = Integer.MAX_VALUE;
}
@@ -919,9 +920,9 @@ public final class BroadcastQueue {
}
if (DEBUG_BROADCAST) {
Slog.v(TAG, "Broadcast temp whitelist uid=" + uid + " duration=" + duration
+ " : " + b.toString());
+ " type=" + type + " : " + b.toString());
}
mService.tempWhitelistUidLocked(uid, duration, b.toString());
mService.tempWhitelistUidLocked(uid, duration, b.toString(), type);
}
/**
@@ -1318,7 +1319,8 @@ public final class BroadcastQueue {
}
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(filter.owningUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
brOptions.getTemporaryAppWhitelistDuration(), r,
brOptions.getTemporaryAppWhitelistType());
}
}
return;
@@ -1610,7 +1612,8 @@ public final class BroadcastQueue {
(brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0);
if (isActivityCapable) {
scheduleTempWhitelistLocked(receiverUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
brOptions.getTemporaryAppWhitelistDuration(), r,
brOptions.getTemporaryAppWhitelistType());
}
// Broadcast is being executed, its package can't be stopped.

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2020 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 static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseLongArray;
/**
* List of uids that are temporarily allowed to start FGS from background.
*/
final class FgsStartTempAllowList {
private static final int MAX_SIZE = 100;
/**
* The key is the UID, the value is expiration elapse time in ms of this temp-allowed UID.
*/
private final SparseLongArray mTempAllowListFgs = new SparseLongArray();
FgsStartTempAllowList() {
}
void add(int uid, long duration) {
if (duration <= 0) {
Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + uid);
return;
}
// The temp allowlist should be a short list with only a few entries in it.
final int size = mTempAllowListFgs.size();
if (size > MAX_SIZE) {
Slog.w(TAG_AM, "FgsStartTempAllowList length:" + size + " exceeds " + MAX_SIZE);
}
final long now = SystemClock.elapsedRealtime();
for (int index = mTempAllowListFgs.size() - 1; index >= 0; index--) {
if (mTempAllowListFgs.valueAt(index) < now) {
mTempAllowListFgs.removeAt(index);
}
}
final long existingExpirationTime = mTempAllowListFgs.get(uid, -1);
final long expirationTime = now + duration;
if (existingExpirationTime == -1 || existingExpirationTime < expirationTime) {
mTempAllowListFgs.put(uid, expirationTime);
}
}
boolean isAllowed(int uid) {
final int index = mTempAllowListFgs.indexOfKey(uid);
if (index < 0) {
return false;
} else if (mTempAllowListFgs.valueAt(index) < SystemClock.elapsedRealtime()) {
mTempAllowListFgs.removeAt(index);
return false;
} else {
return true;
}
}
}