Merge "Allow apps receiving onMediaButton() callback to start FGS from BG." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
236a41d43e
@@ -29,6 +29,7 @@ import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.annotation.UserHandleAware;
|
||||
import android.content.Context;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -295,6 +296,11 @@ public class PowerExemptionManager {
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_SHELL = 316;
|
||||
/**
|
||||
* Media session callbacks.
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_MEDIA_SESSION_CALLBACK = 317;
|
||||
|
||||
/**
|
||||
* The list of BG-FGS-Launch and temp-allow-list reason code.
|
||||
@@ -354,6 +360,7 @@ public class PowerExemptionManager {
|
||||
REASON_EVENT_SMS,
|
||||
REASON_EVENT_MMS,
|
||||
REASON_SHELL,
|
||||
REASON_MEDIA_SESSION_CALLBACK,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ReasonCode {}
|
||||
@@ -451,6 +458,7 @@ public class PowerExemptionManager {
|
||||
* @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
|
||||
* @param reason a optional human readable reason string, could be null or empty string.
|
||||
*/
|
||||
@UserHandleAware
|
||||
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
|
||||
public void addToTemporaryAllowList(@NonNull String packageName, long durationMs,
|
||||
@ReasonCode int reasonCode, @Nullable String reason) {
|
||||
@@ -474,6 +482,7 @@ public class PowerExemptionManager {
|
||||
* used for logging purposes. Could be null or empty string.
|
||||
* @return The duration (in milliseconds) that the app is allow-listed for
|
||||
*/
|
||||
@UserHandleAware
|
||||
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
|
||||
public long addToTemporaryAllowListForEvent(@NonNull String packageName,
|
||||
@AllowListEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
|
||||
@@ -626,6 +635,8 @@ public class PowerExemptionManager {
|
||||
return "EVENT_MMS";
|
||||
case REASON_SHELL:
|
||||
return "SHELL";
|
||||
case REASON_MEDIA_SESSION_CALLBACK:
|
||||
return "MEDIA_SESSION_CALLBACK";
|
||||
default:
|
||||
return "(unknown:" + reasonCode + ")";
|
||||
}
|
||||
|
||||
@@ -2692,7 +2692,7 @@ public class DeviceIdleController extends SystemService
|
||||
void addPowerSaveTempAllowlistAppChecked(String packageName, long duration,
|
||||
int userId, @ReasonCode int reasonCode, @Nullable String reason)
|
||||
throws RemoteException {
|
||||
getContext().enforceCallingPermission(
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
|
||||
"No permission to change device idle whitelist");
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
@@ -2715,7 +2715,7 @@ public class DeviceIdleController extends SystemService
|
||||
|
||||
void removePowerSaveTempAllowlistAppChecked(String packageName, int userId)
|
||||
throws RemoteException {
|
||||
getContext().enforceCallingPermission(
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
|
||||
"No permission to change device idle whitelist");
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
|
||||
@@ -88,6 +88,14 @@ package com.android.server {
|
||||
|
||||
}
|
||||
|
||||
package com.android.server.am {
|
||||
|
||||
public interface ActivityManagerLocal {
|
||||
method public boolean canStartForegroundService(int, int, @NonNull String);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package com.android.server.role {
|
||||
|
||||
public interface RoleServicePlatformHelper {
|
||||
|
||||
@@ -35,6 +35,14 @@ package com.android.server {
|
||||
|
||||
}
|
||||
|
||||
package com.android.server.am {
|
||||
|
||||
public interface ActivityManagerLocal {
|
||||
method public boolean canStartForegroundService(int, int, @NonNull String);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package com.android.server.role {
|
||||
|
||||
public interface RoleServicePlatformHelper {
|
||||
|
||||
@@ -5409,16 +5409,28 @@ public final class ActiveServices {
|
||||
}
|
||||
}
|
||||
|
||||
boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) {
|
||||
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
|
||||
return true;
|
||||
}
|
||||
final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
|
||||
callingPackage, callingPid, callingUid, null /* serviceRecord */,
|
||||
false /* allowBackgroundActivityStarts */);
|
||||
final @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundLocked(
|
||||
allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */);
|
||||
return allowStartFgs != REASON_DENIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should allow while-in-use permissions in FGS or not.
|
||||
* A typical BG started FGS is not allowed to have while-in-use permissions.
|
||||
* @param callingPackage caller app's package name.
|
||||
* @param callingUid caller app's uid.
|
||||
* @param r the service to start.
|
||||
* @param targetService the service to start.
|
||||
* @return {@link ReasonCode}
|
||||
*/
|
||||
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
|
||||
int callingPid, int callingUid, ServiceRecord r,
|
||||
int callingPid, int callingUid, @Nullable ServiceRecord targetService,
|
||||
boolean allowBackgroundActivityStarts) {
|
||||
int ret = REASON_DENIED;
|
||||
|
||||
@@ -5480,8 +5492,8 @@ public final class ActiveServices {
|
||||
}
|
||||
|
||||
if (ret == REASON_DENIED) {
|
||||
if (r.app != null) {
|
||||
ActiveInstrumentation instr = r.app.getActiveInstrumentation();
|
||||
if (targetService != null && targetService.app != null) {
|
||||
ActiveInstrumentation instr = targetService.app.getActiveInstrumentation();
|
||||
if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
|
||||
ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
|
||||
}
|
||||
@@ -5527,16 +5539,44 @@ public final class ActiveServices {
|
||||
private @ReasonCode int shouldAllowFgsStartForegroundLocked(
|
||||
@ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
|
||||
int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
|
||||
int ret = allowWhileInUse;
|
||||
FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason =
|
||||
r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
|
||||
int ret = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPid, callingUid,
|
||||
callingPackage, r);
|
||||
|
||||
final StringBuilder sb = new StringBuilder(64);
|
||||
final int uidState = mAm.getUidStateLocked(callingUid);
|
||||
final String debugInfo =
|
||||
"[callingPackage: " + callingPackage
|
||||
+ "; callingUid: " + callingUid
|
||||
+ "; uidState: " + ProcessList.makeProcStateString(uidState)
|
||||
+ "; intent: " + intent
|
||||
+ "; code:" + reasonCodeToString(ret)
|
||||
+ "; tempAllowListReason:<"
|
||||
+ (tempAllowListReason == null ? null :
|
||||
(tempAllowListReason.mReason
|
||||
+ ",reasonCode:"
|
||||
+ reasonCodeToString(tempAllowListReason.mReasonCode)
|
||||
+ ",duration:" + tempAllowListReason.mDuration
|
||||
+ ",callingUid:" + tempAllowListReason.mCallingUid))
|
||||
+ ">"
|
||||
+ "; targetSdkVersion:" + r.appInfo.targetSdkVersion
|
||||
+ "]";
|
||||
if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
|
||||
r.mLoggedInfoAllowStartForeground = false;
|
||||
r.mInfoAllowStartForeground = debugInfo;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private @ReasonCode int shouldAllowFgsStartForegroundLocked(@ReasonCode int allowWhileInUse,
|
||||
int callingPid, int callingUid, String callingPackage,
|
||||
@Nullable ServiceRecord targetService) {
|
||||
int ret = allowWhileInUse;
|
||||
|
||||
if (ret == REASON_DENIED) {
|
||||
final int uidState = mAm.getUidStateLocked(callingUid);
|
||||
// Is the calling UID at PROCESS_STATE_TOP or above?
|
||||
if (uidState <= PROCESS_STATE_TOP) {
|
||||
sb.append("uidState=").append(uidState);
|
||||
ret = getReasonCodeFromProcState(uidState);
|
||||
}
|
||||
}
|
||||
@@ -5608,8 +5648,10 @@ public final class ActiveServices {
|
||||
|
||||
// NOTE this should always be the last check.
|
||||
if (ret == REASON_DENIED) {
|
||||
if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid)
|
||||
|| isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) {
|
||||
if (isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) {
|
||||
ret = REASON_EXEMPTED_PACKAGE;
|
||||
} else if (targetService != null && isPackageExemptedFromFgsRestriction(
|
||||
targetService.appInfo.packageName, targetService.appInfo.uid)) {
|
||||
ret = REASON_EXEMPTED_PACKAGE;
|
||||
}
|
||||
}
|
||||
@@ -5622,28 +5664,6 @@ public final class ActiveServices {
|
||||
}
|
||||
}
|
||||
|
||||
final String debugInfo =
|
||||
"[callingPackage: " + callingPackage
|
||||
+ "; callingUid: " + callingUid
|
||||
+ "; uidState: " + ProcessList.makeProcStateString(uidState)
|
||||
+ "; intent: " + intent
|
||||
+ "; code:" + reasonCodeToString(ret)
|
||||
+ "; tempAllowListReason:<" +
|
||||
(tempAllowListReason == null ? null :
|
||||
(tempAllowListReason.mReason
|
||||
+ ",reasonCode:"
|
||||
+ reasonCodeToString(tempAllowListReason.mReasonCode)
|
||||
+ ",duration:" + tempAllowListReason.mDuration
|
||||
+ ",callingUid:" + tempAllowListReason.mCallingUid))
|
||||
+ ">"
|
||||
+ "; extra:" + sb.toString()
|
||||
+ "; targetSdkVersion:" + r.appInfo.targetSdkVersion
|
||||
+ "]";
|
||||
if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
|
||||
r.mLoggedInfoAllowStartForeground = false;
|
||||
r.mInfoAllowStartForeground = debugInfo;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.SystemApi;
|
||||
|
||||
/**
|
||||
* Interface for in-process calls into
|
||||
* {@link android.content.Context#ACTIVITY_SERVICE ActivityManager system service}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
|
||||
public interface ActivityManagerLocal {
|
||||
/**
|
||||
* Checks whether an app will be able to start a foreground service or not.
|
||||
*
|
||||
* @param pid The process id belonging to the app to be checked.
|
||||
* @param uid The UID of the app to be checked.
|
||||
* @param packageName The package name of the app to be checked.
|
||||
* @return whether the app will be able to start a foreground service or not.
|
||||
*/
|
||||
boolean canStartForegroundService(int pid, int uid, @NonNull String packageName);
|
||||
}
|
||||
@@ -345,6 +345,7 @@ import com.android.server.DeviceIdleInternal;
|
||||
import com.android.server.DisplayThread;
|
||||
import com.android.server.IntentResolver;
|
||||
import com.android.server.IoThread;
|
||||
import com.android.server.LocalManagerRegistry;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.LockGuard;
|
||||
import com.android.server.NetworkManagementInternal;
|
||||
@@ -2327,6 +2328,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
mAppOpsService.publish();
|
||||
Slog.d("AppOps", "AppOpsService published");
|
||||
LocalServices.addService(ActivityManagerInternal.class, mInternal);
|
||||
LocalManagerRegistry.addManager(ActivityManagerLocal.class,
|
||||
(ActivityManagerLocal) mInternal);
|
||||
mActivityTaskManager.onActivityManagerInternalAdded();
|
||||
mPendingIntentController.onActivityManagerInternalAdded();
|
||||
mAppProfiler.onActivityManagerInternalAdded();
|
||||
@@ -15086,7 +15089,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public final class LocalService extends ActivityManagerInternal {
|
||||
public final class LocalService extends ActivityManagerInternal
|
||||
implements ActivityManagerLocal {
|
||||
@Override
|
||||
public String checkContentProviderAccess(String authority, int userId) {
|
||||
return mCpHelper.checkContentProviderAccess(authority, userId);
|
||||
@@ -16008,6 +16012,13 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
public void unregisterAnrController(AnrController controller) {
|
||||
mActivityTaskManager.unregisterAnrController(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canStartForegroundService(int pid, int uid, @NonNull String packageName) {
|
||||
synchronized (ActivityManagerService.this) {
|
||||
return mServices.canStartForegroundServiceLocked(pid, uid, packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
|
||||
|
||||
@@ -1068,6 +1068,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
|
||||
public boolean sendMediaButton(String packageName, int pid, int uid,
|
||||
boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
|
||||
try {
|
||||
if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
|
||||
final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction())
|
||||
+ ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode());
|
||||
mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
|
||||
pid, uid, packageName, reason);
|
||||
}
|
||||
if (asSystemService) {
|
||||
mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
|
||||
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb);
|
||||
@@ -1085,6 +1091,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
|
||||
public boolean sendMediaButton(String packageName, int pid, int uid,
|
||||
boolean asSystemService, KeyEvent keyEvent) {
|
||||
try {
|
||||
if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
|
||||
final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction())
|
||||
+ ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode());
|
||||
mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
|
||||
pid, uid, packageName, reason);
|
||||
}
|
||||
if (asSystemService) {
|
||||
mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
|
||||
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null);
|
||||
|
||||
@@ -63,6 +63,7 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteCallbackList;
|
||||
@@ -82,9 +83,11 @@ import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.LocalManagerRegistry;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.Watchdog;
|
||||
import com.android.server.Watchdog.Monitor;
|
||||
import com.android.server.am.ActivityManagerLocal;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -114,6 +117,13 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
*/
|
||||
private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
|
||||
|
||||
/**
|
||||
* Denotes the duration during which an app receiving a media session callback will be
|
||||
* exempted from FGS-from-BG restriction and so will be allowed to start an FGS even if it is
|
||||
* in the background state while it receives a media session callback.
|
||||
*/
|
||||
private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
|
||||
|
||||
private final Context mContext;
|
||||
private final SessionManagerImpl mSessionManagerImpl;
|
||||
private final MessageHandler mHandler = new MessageHandler();
|
||||
@@ -136,6 +146,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
private KeyguardManager mKeyguardManager;
|
||||
private AudioManager mAudioManager;
|
||||
private boolean mHasFeatureLeanback;
|
||||
private ActivityManagerLocal mActivityManagerLocal;
|
||||
|
||||
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
|
||||
// It's always not null after the MediaSessionService is started.
|
||||
@@ -219,6 +230,8 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
final IntentFilter filter = new IntentFilter(
|
||||
NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED);
|
||||
mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter);
|
||||
|
||||
mActivityManagerLocal = LocalManagerRegistry.getManager(ActivityManagerLocal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -538,6 +551,26 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
throw new IllegalArgumentException("packageName is not owned by the calling process");
|
||||
}
|
||||
|
||||
void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
|
||||
int callingPid, int callingUid, String callingPackage, String reason) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
enforcePackageName(callingPackage, callingUid);
|
||||
if (targetUid != callingUid && mActivityManagerLocal.canStartForegroundService(
|
||||
callingPid, callingUid, callingPackage)) {
|
||||
final Context userContext = mContext.createContextAsUser(
|
||||
UserHandle.of(UserHandle.getUserId(targetUid)), /* flags= */ 0);
|
||||
final PowerExemptionManager powerExemptionManager = userContext.getSystemService(
|
||||
PowerExemptionManager.class);
|
||||
powerExemptionManager.addToTemporaryAllowList(targetPackage,
|
||||
FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS,
|
||||
PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a caller's authorization to register an IRemoteControlDisplay.
|
||||
* Authorization is granted if one of the following is true:
|
||||
|
||||
Reference in New Issue
Block a user