Merge "Allow apps receiving onMediaButton() callback to start FGS from BG." into sc-dev

This commit is contained in:
Sudheer Shanka
2021-03-16 01:16:10 +00:00
committed by Android (Google) Code Review
9 changed files with 176 additions and 34 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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