diff options
6 files changed, 58 insertions, 13 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 505b498e3cf6..d0fd92294979 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -187,6 +187,16 @@ class ContextImpl extends Context { private static final String XATTR_INODE_CODE_CACHE = "user.inode_code_cache"; /** + * Special intent extra that critical system apps can use to hide the notification for a + * foreground service. This extra should be placed in the intent passed into {@link + * #startForegroundService(Intent)}. + * + * @hide + */ + private static final String EXTRA_HIDDEN_FOREGROUND_SERVICE = + "android.intent.extra.HIDDEN_FOREGROUND_SERVICE"; + + /** * Map from package name, to preference name, to cached preferences. */ @GuardedBy("ContextImpl.class") @@ -1697,9 +1707,12 @@ class ContextImpl extends Context { try { validateServiceIntent(service); service.prepareToLeaveProcess(this); + final boolean hideForegroundNotification = requireForeground + && service.getBooleanExtra(EXTRA_HIDDEN_FOREGROUND_SERVICE, false); ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground, + hideForegroundNotification, getOpPackageName(), getAttributionTag(), user.getIdentifier()); if (cn != null) { if (cn.getPackageName().equals("!")) { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 945957738f8e..2abe9cf9fce5 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -155,7 +155,8 @@ interface IActivityManager { boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta); PendingIntent getRunningServiceControlPanel(in ComponentName service); ComponentName startService(in IApplicationThread caller, in Intent service, - in String resolvedType, boolean requireForeground, in String callingPackage, + in String resolvedType, boolean requireForeground, + boolean hideForegroundNotification, in String callingPackage, in String callingFeatureId, int userId); @UnsupportedAppUsage int stopService(in IApplicationThread caller, in Intent service, diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3f867f656c24..7d88a8a4ec01 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -453,16 +453,16 @@ public final class ActiveServices { } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, - int callingPid, int callingUid, boolean fgRequired, String callingPackage, - @Nullable String callingFeatureId, final int userId) + int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification, + String callingPackage, @Nullable String callingFeatureId, final int userId) throws TransactionTooLargeException { return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired, - callingPackage, callingFeatureId, userId, false); + hideFgNotification, callingPackage, callingFeatureId, userId, false); } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, - int callingPid, int callingUid, boolean fgRequired, String callingPackage, - @Nullable String callingFeatureId, final int userId, + int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification, + String callingPackage, @Nullable String callingFeatureId, final int userId, boolean allowBackgroundActivityStarts) throws TransactionTooLargeException { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); @@ -609,6 +609,7 @@ public final class ActiveServices { r.startRequested = true; r.delayedStop = false; r.fgRequired = fgRequired; + r.hideFgNotification = hideFgNotification; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, neededGrants, callingUid)); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 82abb988cb2c..44e3bbf91565 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1438,6 +1438,10 @@ public class ActivityManagerService extends IActivityManager.Stub final Injector mInjector; + /** The package verifier app. */ + private String mPackageVerifier; + private int mPackageVerifierUid = UserHandle.USER_NULL; + static final class ProcessChangeItem { static final int CHANGE_ACTIVITIES = 1<<0; static final int CHANGE_FOREGROUND_SERVICES = 1<<1; @@ -2362,6 +2366,18 @@ public class ActivityManagerService extends IActivityManager.Stub if (phase == PHASE_SYSTEM_SERVICES_READY) { mService.mBatteryStatsService.systemServicesReady(); mService.mServices.systemServicesReady(); + mService.mPackageVerifier = ArrayUtils.firstOrNull( + LocalServices.getService(PackageManagerInternal.class).getKnownPackageNames( + PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM)); + if (mService.mPackageVerifier != null) { + try { + mService.mPackageVerifierUid = + getContext().getPackageManager().getPackageUid( + mService.mPackageVerifier, UserHandle.USER_SYSTEM); + } catch (NameNotFoundException e) { + Slog.wtf(TAG, "Package manager couldn't get package verifier uid", e); + } + } } else if (phase == PHASE_ACTIVITY_MANAGER_READY) { mService.startBroadcastObservers(); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { @@ -15007,8 +15023,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public ComponentName startService(IApplicationThread caller, Intent service, - String resolvedType, boolean requireForeground, String callingPackage, - String callingFeatureId, int userId) + String resolvedType, boolean requireForeground, boolean hideForegroundNotification, + String callingPackage, String callingFeatureId, int userId) throws TransactionTooLargeException { enforceNotIsolatedCaller("startService"); // Refuse possible leaked file descriptors @@ -15020,17 +15036,27 @@ public class ActivityManagerService extends IActivityManager.Stub throw new IllegalArgumentException("callingPackage cannot be null"); } + final int callingUid = Binder.getCallingUid(); + if (requireForeground && hideForegroundNotification) { + if (!UserHandle.isSameApp(callingUid, mPackageVerifierUid) + || !callingPackage.equals(mPackageVerifier)) { + throw new IllegalArgumentException( + "Only the package verifier can hide its foreground service notification"); + } + Slog.i(TAG, "Foreground service notification hiding requested by " + callingPackage); + } + if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground); synchronized(this) { final int callingPid = Binder.getCallingPid(); - final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); ComponentName res; try { res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, - requireForeground, callingPackage, callingFeatureId, userId); + requireForeground, hideForegroundNotification, + callingPackage, callingFeatureId, userId); } finally { Binder.restoreCallingIdentity(origId); } @@ -19470,7 +19496,7 @@ public class ActivityManagerService extends IActivityManager.Stub ComponentName res; try { res = mServices.startServiceLocked(null, service, - resolvedType, -1, uid, fgRequired, callingPackage, + resolvedType, -1, uid, fgRequired, false, callingPackage, callingFeatureId, userId, allowBackgroundActivityStarts); } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 149e3baa90e7..a512cca7bac4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -654,7 +654,7 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println("Starting service: " + intent); pw.flush(); ComponentName cn = mInterface.startService(null, intent, intent.getType(), - asForeground, SHELL_PACKAGE_NAME, null, mUserId); + asForeground, false, SHELL_PACKAGE_NAME, null, mUserId); if (cn == null) { err.println("Error: Not found; no service started."); return -1; diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 828ac71eccfe..19d5a3125f3a 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -104,6 +104,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT? boolean delayed; // are we waiting to start this service in the background? boolean fgRequired; // is the service required to go foreground after starting? + boolean hideFgNotification; // Hide the fg service notification boolean fgWaiting; // is a timeout for going foreground already scheduled? boolean isForeground; // is service currently in foreground mode? int foregroundId; // Notification ID of last foreground req. @@ -823,6 +824,9 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } public void postNotification() { + if (hideFgNotification) { + return; + } final int appUid = appInfo.uid; final int appPid = app.pid; if (foregroundId != 0 && foregroundNoti != null) { @@ -915,7 +919,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } if (localForegroundNoti.getSmallIcon() == null) { // Notifications whose icon is 0 are defined to not show - // a notification, silently ignoring it. We don't want to + // a notification. We don't want to // just ignore it, we want to prevent the service from // being foreground. throw new RuntimeException("invalid service notification: " |