Loading services/core/java/com/android/server/am/ActiveServices.java +247 −14 Original line number Diff line number Diff line Loading @@ -137,6 +137,7 @@ import android.content.Context; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; Loading Loading @@ -308,6 +309,12 @@ public final class ActiveServices { */ final ArrayList<ServiceRecord> mPendingFgsNotifications = new ArrayList<>(); /** * Map of ForegroundServiceDelegation to the delegation ServiceRecord. The delegation * ServiceRecord has flag isFgsDelegate set to true. */ final ArrayMap<ForegroundServiceDelegation, ServiceRecord> mFgsDelegations = new ArrayMap<>(); /** * Whether there is a rate limit that suppresses immediate re-deferral of new FGS * notifications from each app. On by default, disabled only by shell command for Loading Loading @@ -3043,7 +3050,7 @@ public final class ActiveServices { ServiceLookupResult res = retrieveServiceLocked(service, instanceName, isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, isBindExternal, allowInstant); isBindExternal, allowInstant, null /* fgsDelegateOptions */); if (res == null) { return 0; } Loading Loading @@ -3501,7 +3508,7 @@ public final class ActiveServices { boolean allowInstant) { return retrieveServiceLocked(service, instanceName, false, 0, null, resolvedType, callingPackage, callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal, allowInstant); isBindExternal, allowInstant, null /* fgsDelegateOptions */); } private ServiceLookupResult retrieveServiceLocked(Intent service, Loading @@ -3509,7 +3516,7 @@ public final class ActiveServices { String sdkSandboxClientAppPackage, String resolvedType, String callingPackage, int callingPid, int callingUid, int userId, boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal, boolean allowInstant) { boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions) { if (isSdkSandboxService && instanceName == null) { throw new IllegalArgumentException("No instanceName provided for sdk sandbox process"); } Loading Loading @@ -3572,6 +3579,53 @@ public final class ActiveServices { } } } if (r == null && fgsDelegateOptions != null) { // Create a ServiceRecord for FGS delegate. final ServiceInfo sInfo = new ServiceInfo(); ApplicationInfo aInfo = null; try { aInfo = AppGlobals.getPackageManager().getApplicationInfo( fgsDelegateOptions.mClientPackageName, ActivityManagerService.STOCK_PM_FLAGS, userId); } catch (RemoteException ex) { // pm is in same process, this will never happen. } if (aInfo == null) { throw new SecurityException("startForegroundServiceDelegate failed, " + "could not resolve client package " + callingPackage); } if (aInfo.uid != fgsDelegateOptions.mClientUid) { throw new SecurityException("startForegroundServiceDelegate failed, " + "uid:" + aInfo.uid + " does not match clientUid:" + fgsDelegateOptions.mClientUid); } sInfo.applicationInfo = aInfo; sInfo.packageName = aInfo.packageName; sInfo.mForegroundServiceType = fgsDelegateOptions.mForegroundServiceTypes; sInfo.processName = aInfo.processName; final ComponentName cn = service.getComponent(); sInfo.name = cn.getClassName(); if (createIfNeeded) { final Intent.FilterComparison filter = new Intent.FilterComparison(service.cloneFilter()); final ServiceRestarter res = new ServiceRestarter(); r = new ServiceRecord(mAm, cn /* name */, cn /* instanceName */, sInfo.applicationInfo.packageName, sInfo.applicationInfo.uid, filter, sInfo, callingFromFg, res, null /* sdkSandboxProcessName */, INVALID_UID /* sdkSandboxClientAppUid */, null /* sdkSandboxClientAppPackage */); res.setService(r); smap.mServicesByInstanceName.put(cn, r); smap.mServicesByIntent.put(filter, r); if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Retrieve created new service: " + r); r.mRecentCallingPackage = callingPackage; r.mRecentCallingUid = callingUid; } return new ServiceLookupResult(r, resolution.getAlias()); } if (r == null) { try { int flags = ActivityManagerService.STOCK_PM_FLAGS Loading Loading @@ -4983,6 +5037,20 @@ public final class ActiveServices { // Bump the process to the top of LRU list mAm.updateLruProcessLocked(r.app, false, null); updateServiceForegroundLocked(r.app.mServices, false); if (r.mIsFgsDelegate) { if (r.mFgsDelegation.mConnection != null) { mAm.mHandler.post(() -> { r.mFgsDelegation.mConnection.onServiceDisconnected( r.mFgsDelegation.mOptions.getComponentName()); }); } for (int i = mFgsDelegations.size() - 1; i >= 0; i--) { if (mFgsDelegations.valueAt(i) == r) { mFgsDelegations.removeAt(i); break; } } } else { try { oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy", oomAdjusted ? 0 : OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); Loading @@ -4994,6 +5062,7 @@ public final class ActiveServices { + r.shortInstanceName, e); serviceProcessGoneLocked(r, enqueueOomAdj); } } } else { if (DEBUG_SERVICE) Slog.v( TAG_SERVICE, "Removed service that has no process: " + r); Loading Loading @@ -7234,7 +7303,12 @@ public final class ActiveServices { ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName), r.mFgsHasNotificationPermission, r.foregroundServiceType, fgsTypeCheckCode); fgsTypeCheckCode, r.mIsFgsDelegate, r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID, r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT ); int event = 0; if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) { Loading Loading @@ -7298,4 +7372,163 @@ public final class ActiveServices { return "UNKNOWN"; } } /** * Start a foreground service delegate. The delegate is not an actual service component, it is * merely a delegate that promotes the client process into foreground service process state. * * @param options an ForegroundServiceDelegationOptions object. * @param connection callback if the delegate is started successfully. * @return true if delegate is started, false otherwise. * @throw SecurityException if PackageManaager can not resolve * {@link ForegroundServiceDelegationOptions#mClientPackageName} or the resolved * package's UID is not same as {@link ForegroundServiceDelegationOptions#mClientUid} */ boolean startForegroundServiceDelegateLocked( @NonNull ForegroundServiceDelegationOptions options, @Nullable ServiceConnection connection) { Slog.v(TAG, "startForegroundServiceDelegateLocked " + options.getDescription()); final ComponentName cn = options.getComponentName(); for (int i = mFgsDelegations.size() - 1; i >= 0; i--) { ForegroundServiceDelegation delegation = mFgsDelegations.keyAt(i); if (delegation.mOptions.isSameDelegate(options)) { Slog.e(TAG, "startForegroundServiceDelegate " + options.getDescription() + " already exists, multiple connections are not allowed"); return false; } } final int callingPid = options.mClientPid; final int callingUid = options.mClientUid; final int userId = UserHandle.getUserId(callingUid); final String callingPackage = options.mClientPackageName; if (!canStartForegroundServiceLocked(callingPid, callingUid, callingPackage)) { Slog.d(TAG, "startForegroundServiceDelegateLocked aborted," + " app is in the background"); return false; } IApplicationThread caller = options.mClientAppThread; ProcessRecord callerApp; if (caller != null) { callerApp = mAm.getRecordForAppLOSP(caller); } else { synchronized (mAm.mPidsSelfLocked) { callerApp = mAm.mPidsSelfLocked.get(callingPid); caller = callerApp.getThread(); } } if (callerApp == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (pid=" + callingPid + ") when startForegroundServiceDelegateLocked " + cn); } Intent intent = new Intent(); intent.setComponent(cn); ServiceLookupResult res = retrieveServiceLocked(intent, null /*instanceName */, false /* isSdkSandboxService */, INVALID_UID /* sdkSandboxClientAppUid */, null /* sdkSandboxClientAppPackage */, null /* resolvedType */, callingPackage, callingPid, callingUid, userId, true /* createIfNeeded */, false /* callingFromFg */, false /* isBindExternal */, false /* allowInstant */ , options); if (res == null || res.record == null) { Slog.d(TAG, "startForegroundServiceDelegateLocked retrieveServiceLocked returns null"); return false; } final ServiceRecord r = res.record; r.setProcess(callerApp, caller, callingPid, null); r.mIsFgsDelegate = true; final ForegroundServiceDelegation delegation = new ForegroundServiceDelegation(options, connection); r.mFgsDelegation = delegation; mFgsDelegations.put(delegation, r); r.isForeground = true; r.mFgsEnterTime = SystemClock.uptimeMillis(); r.foregroundServiceType = options.mForegroundServiceTypes; setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId, false, false); final ProcessServiceRecord psr = callerApp.mServices; final boolean newService = psr.startService(r); // updateOomAdj. updateServiceForegroundLocked(psr, /* oomAdj= */ true); synchronized (mAm.mProcessStats.mLock) { final ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); } } mAm.mBatteryStatsService.noteServiceStartRunning(callingUid, callingPackage, cn.getClassName()); mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService), AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null, true, false, null, false, AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); registerAppOpCallbackLocked(r); logFGSStateChangeLocked(r, FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER, 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN); // Notify the caller. if (connection != null) { mAm.mHandler.post(() -> { connection.onServiceConnected(cn, delegation.mBinder); }); } signalForegroundServiceObserversLocked(r); return true; } /** * Stop the foreground service delegate. This removes the process out of foreground service * process state. * * @param options an ForegroundServiceDelegationOptions object. */ void stopForegroundServiceDelegateLocked(@NonNull ForegroundServiceDelegationOptions options) { ServiceRecord r = null; for (int i = mFgsDelegations.size() - 1; i >= 0; i--) { if (mFgsDelegations.keyAt(i).mOptions.isSameDelegate(options)) { Slog.d(TAG, "stopForegroundServiceDelegateLocked " + options.getDescription()); r = mFgsDelegations.valueAt(i); break; } } if (r != null) { bringDownServiceLocked(r, false); } else { Slog.e(TAG, "stopForegroundServiceDelegateLocked delegate does not exist " + options.getDescription()); } } /** * Stop the foreground service delegate by its ServiceConnection. * This removes the process out of foreground service process state. * * @param connection an ServiceConnection object. */ void stopForegroundServiceDelegateLocked(@NonNull ServiceConnection connection) { ServiceRecord r = null; for (int i = mFgsDelegations.size() - 1; i >= 0; i--) { final ForegroundServiceDelegation d = mFgsDelegations.keyAt(i); if (d.mConnection == connection) { Slog.d(TAG, "stopForegroundServiceDelegateLocked " + d.mOptions.getDescription()); r = mFgsDelegations.valueAt(i); break; } } if (r != null) { bringDownServiceLocked(r, false); } else { Slog.e(TAG, "stopForegroundServiceDelegateLocked delegate does not exist"); } } } services/core/java/com/android/server/am/ActivityManagerLocal.java +25 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.am; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.Context; Loading Loading @@ -92,4 +93,28 @@ public interface ActivityManagerLocal { int clientAppUid, @NonNull String clientAppPackage, @NonNull String processName, @Context.BindServiceFlags int flags) throws RemoteException; /** * Start a foreground service delegate. * @param options foreground service delegate options. * @param connection a service connection served as callback to caller. * @return true if delegate is started successfully, false otherwise. * @hide */ boolean startForegroundServiceDelegate(@NonNull ForegroundServiceDelegationOptions options, @Nullable ServiceConnection connection); /** * Stop a foreground service delegate. * @param options the foreground service delegate options. * @hide */ void stopForegroundServiceDelegate(@NonNull ForegroundServiceDelegationOptions options); /** * Stop a foreground service delegate by service connection. * @param connection service connection used to start delegate previously. * @hide */ void stopForegroundServiceDelegate(@NonNull ServiceConnection connection); } services/core/java/com/android/server/am/ActivityManagerService.java +77 −0 Original line number Diff line number Diff line Loading @@ -18125,6 +18125,30 @@ public class ActivityManagerService extends IActivityManager.Stub mUidObserverController.register(observer, which, cutpoint, callingPackage, Binder.getCallingUid()); } @Override public boolean startForegroundServiceDelegate( @NonNull ForegroundServiceDelegationOptions options, @Nullable ServiceConnection connection) { synchronized (ActivityManagerService.this) { return mServices.startForegroundServiceDelegateLocked(options, connection); } } @Override public void stopForegroundServiceDelegate( @NonNull ForegroundServiceDelegationOptions options) { synchronized (ActivityManagerService.this) { mServices.stopForegroundServiceDelegateLocked(options); } } @Override public void stopForegroundServiceDelegate(@NonNull ServiceConnection connection) { synchronized (ActivityManagerService.this) { mServices.stopForegroundServiceDelegateLocked(connection); } } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) { Loading Loading @@ -18313,6 +18337,59 @@ public class ActivityManagerService extends IActivityManager.Stub } } /** * Start/stop foreground service delegate on a app's process. * This interface is intended for the shell command to use. */ void setForegroundServiceDelegate(String packageName, int uid, boolean isStart, @ForegroundServiceDelegationOptions.DelegationService int delegateService, String clientInstanceName) { final int callingUid = Binder.getCallingUid(); if (callingUid != SYSTEM_UID && callingUid != ROOT_UID && callingUid != SHELL_UID) { throw new SecurityException( "No permission to start/stop foreground service delegate"); } final long callingId = Binder.clearCallingIdentity(); try { boolean foundPid = false; synchronized (this) { ArrayList<ForegroundServiceDelegationOptions> delegates = new ArrayList<>(); synchronized (mPidsSelfLocked) { for (int i = 0; i < mPidsSelfLocked.size(); i++) { final ProcessRecord p = mPidsSelfLocked.valueAt(i); final IApplicationThread thread = p.getThread(); if (p.uid == uid && thread != null) { foundPid = true; int pid = mPidsSelfLocked.keyAt(i); ForegroundServiceDelegationOptions options = new ForegroundServiceDelegationOptions(pid, uid, packageName, null /* clientAppThread */, false /* isSticky */, clientInstanceName, 0 /* foregroundServiceType */, delegateService); delegates.add(options); } } } for (int i = delegates.size() - 1; i >= 0; i--) { final ForegroundServiceDelegationOptions options = delegates.get(i); if (isStart) { ((ActivityManagerLocal) mInternal).startForegroundServiceDelegate(options, null /* connection */); } else { ((ActivityManagerLocal) mInternal).stopForegroundServiceDelegate(options); } } } if (!foundPid) { Slog.e(TAG, "setForegroundServiceDelegate can not find process for packageName:" + packageName + " uid:" + uid); } } finally { Binder.restoreCallingIdentity(callingId); } } /** * Force the settings cache to be loaded */ services/core/java/com/android/server/am/ActivityManagerShellCommand.java +43 −0 Original line number Diff line number Diff line Loading @@ -369,6 +369,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runResetDropboxRateLimiter(); case "list-secondary-displays-for-starting-users": return runListSecondaryDisplaysForStartingUsers(pw); case "set-foreground-service-delegate": return runSetForegroundServiceDelegate(pw); default: return handleDefaultCommands(cmd); } Loading Loading @@ -3592,6 +3594,45 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } int runSetForegroundServiceDelegate(PrintWriter pw) throws RemoteException { int userId = UserHandle.USER_CURRENT; String opt; while ((opt = getNextOption()) != null) { if (opt.equals("--user")) { userId = UserHandle.parseUserArg(getNextArgRequired()); } else { getErrPrintWriter().println("Error: Unknown option: " + opt); return -1; } } final String packageName = getNextArgRequired(); final String action = getNextArgRequired(); boolean isStart = true; if ("start".equals(action)) { isStart = true; } else if ("stop".equals(action)) { isStart = false; } else { pw.println("Error: action is either start or stop"); return -1; } int uid = INVALID_UID; try { final PackageManager pm = mInternal.mContext.getPackageManager(); uid = pm.getPackageUidAsUser(packageName, PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), userId); } catch (PackageManager.NameNotFoundException e) { pw.println("Error: userId:" + userId + " package:" + packageName + " is not found"); return -1; } mInternal.setForegroundServiceDelegate(packageName, uid, isStart, ForegroundServiceDelegationOptions.DELEGATION_SERVICE_SPECIAL_USE, "FgsDelegate"); return 0; } int runResetDropboxRateLimiter() throws RemoteException { mInternal.resetDropboxRateLimiter(); return 0; Loading Loading @@ -3968,6 +4009,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" list-secondary-displays-for-starting-users"); pw.println(" Lists the id of displays that can be used to start users on " + "background."); pw.println(" set-foreground-service-delegate [--user <USER_ID>] <PACKAGE> start|stop"); pw.println(" Start/stop an app's foreground service delegate."); pw.println(); Intent.printIntentArgsHelp(pw, ""); } Loading services/core/java/com/android/server/am/ForegroundServiceDelegation.java 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.Nullable; import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; /** * A foreground service delegate which has client options and connection callback. */ public class ForegroundServiceDelegation { public final IBinder mBinder = new Binder(); @NonNull public final ForegroundServiceDelegationOptions mOptions; @Nullable public final ServiceConnection mConnection; public ForegroundServiceDelegation(@NonNull ForegroundServiceDelegationOptions options, @Nullable ServiceConnection connection) { mOptions = options; mConnection = connection; } } Loading
services/core/java/com/android/server/am/ActiveServices.java +247 −14 Original line number Diff line number Diff line Loading @@ -137,6 +137,7 @@ import android.content.Context; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; Loading Loading @@ -308,6 +309,12 @@ public final class ActiveServices { */ final ArrayList<ServiceRecord> mPendingFgsNotifications = new ArrayList<>(); /** * Map of ForegroundServiceDelegation to the delegation ServiceRecord. The delegation * ServiceRecord has flag isFgsDelegate set to true. */ final ArrayMap<ForegroundServiceDelegation, ServiceRecord> mFgsDelegations = new ArrayMap<>(); /** * Whether there is a rate limit that suppresses immediate re-deferral of new FGS * notifications from each app. On by default, disabled only by shell command for Loading Loading @@ -3043,7 +3050,7 @@ public final class ActiveServices { ServiceLookupResult res = retrieveServiceLocked(service, instanceName, isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, isBindExternal, allowInstant); isBindExternal, allowInstant, null /* fgsDelegateOptions */); if (res == null) { return 0; } Loading Loading @@ -3501,7 +3508,7 @@ public final class ActiveServices { boolean allowInstant) { return retrieveServiceLocked(service, instanceName, false, 0, null, resolvedType, callingPackage, callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal, allowInstant); isBindExternal, allowInstant, null /* fgsDelegateOptions */); } private ServiceLookupResult retrieveServiceLocked(Intent service, Loading @@ -3509,7 +3516,7 @@ public final class ActiveServices { String sdkSandboxClientAppPackage, String resolvedType, String callingPackage, int callingPid, int callingUid, int userId, boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal, boolean allowInstant) { boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions) { if (isSdkSandboxService && instanceName == null) { throw new IllegalArgumentException("No instanceName provided for sdk sandbox process"); } Loading Loading @@ -3572,6 +3579,53 @@ public final class ActiveServices { } } } if (r == null && fgsDelegateOptions != null) { // Create a ServiceRecord for FGS delegate. final ServiceInfo sInfo = new ServiceInfo(); ApplicationInfo aInfo = null; try { aInfo = AppGlobals.getPackageManager().getApplicationInfo( fgsDelegateOptions.mClientPackageName, ActivityManagerService.STOCK_PM_FLAGS, userId); } catch (RemoteException ex) { // pm is in same process, this will never happen. } if (aInfo == null) { throw new SecurityException("startForegroundServiceDelegate failed, " + "could not resolve client package " + callingPackage); } if (aInfo.uid != fgsDelegateOptions.mClientUid) { throw new SecurityException("startForegroundServiceDelegate failed, " + "uid:" + aInfo.uid + " does not match clientUid:" + fgsDelegateOptions.mClientUid); } sInfo.applicationInfo = aInfo; sInfo.packageName = aInfo.packageName; sInfo.mForegroundServiceType = fgsDelegateOptions.mForegroundServiceTypes; sInfo.processName = aInfo.processName; final ComponentName cn = service.getComponent(); sInfo.name = cn.getClassName(); if (createIfNeeded) { final Intent.FilterComparison filter = new Intent.FilterComparison(service.cloneFilter()); final ServiceRestarter res = new ServiceRestarter(); r = new ServiceRecord(mAm, cn /* name */, cn /* instanceName */, sInfo.applicationInfo.packageName, sInfo.applicationInfo.uid, filter, sInfo, callingFromFg, res, null /* sdkSandboxProcessName */, INVALID_UID /* sdkSandboxClientAppUid */, null /* sdkSandboxClientAppPackage */); res.setService(r); smap.mServicesByInstanceName.put(cn, r); smap.mServicesByIntent.put(filter, r); if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Retrieve created new service: " + r); r.mRecentCallingPackage = callingPackage; r.mRecentCallingUid = callingUid; } return new ServiceLookupResult(r, resolution.getAlias()); } if (r == null) { try { int flags = ActivityManagerService.STOCK_PM_FLAGS Loading Loading @@ -4983,6 +5037,20 @@ public final class ActiveServices { // Bump the process to the top of LRU list mAm.updateLruProcessLocked(r.app, false, null); updateServiceForegroundLocked(r.app.mServices, false); if (r.mIsFgsDelegate) { if (r.mFgsDelegation.mConnection != null) { mAm.mHandler.post(() -> { r.mFgsDelegation.mConnection.onServiceDisconnected( r.mFgsDelegation.mOptions.getComponentName()); }); } for (int i = mFgsDelegations.size() - 1; i >= 0; i--) { if (mFgsDelegations.valueAt(i) == r) { mFgsDelegations.removeAt(i); break; } } } else { try { oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy", oomAdjusted ? 0 : OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); Loading @@ -4994,6 +5062,7 @@ public final class ActiveServices { + r.shortInstanceName, e); serviceProcessGoneLocked(r, enqueueOomAdj); } } } else { if (DEBUG_SERVICE) Slog.v( TAG_SERVICE, "Removed service that has no process: " + r); Loading Loading @@ -7234,7 +7303,12 @@ public final class ActiveServices { ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName), r.mFgsHasNotificationPermission, r.foregroundServiceType, fgsTypeCheckCode); fgsTypeCheckCode, r.mIsFgsDelegate, r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID, r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT ); int event = 0; if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) { Loading Loading @@ -7298,4 +7372,163 @@ public final class ActiveServices { return "UNKNOWN"; } } /** * Start a foreground service delegate. The delegate is not an actual service component, it is * merely a delegate that promotes the client process into foreground service process state. * * @param options an ForegroundServiceDelegationOptions object. * @param connection callback if the delegate is started successfully. * @return true if delegate is started, false otherwise. * @throw SecurityException if PackageManaager can not resolve * {@link ForegroundServiceDelegationOptions#mClientPackageName} or the resolved * package's UID is not same as {@link ForegroundServiceDelegationOptions#mClientUid} */ boolean startForegroundServiceDelegateLocked( @NonNull ForegroundServiceDelegationOptions options, @Nullable ServiceConnection connection) { Slog.v(TAG, "startForegroundServiceDelegateLocked " + options.getDescription()); final ComponentName cn = options.getComponentName(); for (int i = mFgsDelegations.size() - 1; i >= 0; i--) { ForegroundServiceDelegation delegation = mFgsDelegations.keyAt(i); if (delegation.mOptions.isSameDelegate(options)) { Slog.e(TAG, "startForegroundServiceDelegate " + options.getDescription() + " already exists, multiple connections are not allowed"); return false; } } final int callingPid = options.mClientPid; final int callingUid = options.mClientUid; final int userId = UserHandle.getUserId(callingUid); final String callingPackage = options.mClientPackageName; if (!canStartForegroundServiceLocked(callingPid, callingUid, callingPackage)) { Slog.d(TAG, "startForegroundServiceDelegateLocked aborted," + " app is in the background"); return false; } IApplicationThread caller = options.mClientAppThread; ProcessRecord callerApp; if (caller != null) { callerApp = mAm.getRecordForAppLOSP(caller); } else { synchronized (mAm.mPidsSelfLocked) { callerApp = mAm.mPidsSelfLocked.get(callingPid); caller = callerApp.getThread(); } } if (callerApp == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (pid=" + callingPid + ") when startForegroundServiceDelegateLocked " + cn); } Intent intent = new Intent(); intent.setComponent(cn); ServiceLookupResult res = retrieveServiceLocked(intent, null /*instanceName */, false /* isSdkSandboxService */, INVALID_UID /* sdkSandboxClientAppUid */, null /* sdkSandboxClientAppPackage */, null /* resolvedType */, callingPackage, callingPid, callingUid, userId, true /* createIfNeeded */, false /* callingFromFg */, false /* isBindExternal */, false /* allowInstant */ , options); if (res == null || res.record == null) { Slog.d(TAG, "startForegroundServiceDelegateLocked retrieveServiceLocked returns null"); return false; } final ServiceRecord r = res.record; r.setProcess(callerApp, caller, callingPid, null); r.mIsFgsDelegate = true; final ForegroundServiceDelegation delegation = new ForegroundServiceDelegation(options, connection); r.mFgsDelegation = delegation; mFgsDelegations.put(delegation, r); r.isForeground = true; r.mFgsEnterTime = SystemClock.uptimeMillis(); r.foregroundServiceType = options.mForegroundServiceTypes; setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId, false, false); final ProcessServiceRecord psr = callerApp.mServices; final boolean newService = psr.startService(r); // updateOomAdj. updateServiceForegroundLocked(psr, /* oomAdj= */ true); synchronized (mAm.mProcessStats.mLock) { final ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); } } mAm.mBatteryStatsService.noteServiceStartRunning(callingUid, callingPackage, cn.getClassName()); mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService), AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null, true, false, null, false, AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); registerAppOpCallbackLocked(r); logFGSStateChangeLocked(r, FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER, 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN); // Notify the caller. if (connection != null) { mAm.mHandler.post(() -> { connection.onServiceConnected(cn, delegation.mBinder); }); } signalForegroundServiceObserversLocked(r); return true; } /** * Stop the foreground service delegate. This removes the process out of foreground service * process state. * * @param options an ForegroundServiceDelegationOptions object. */ void stopForegroundServiceDelegateLocked(@NonNull ForegroundServiceDelegationOptions options) { ServiceRecord r = null; for (int i = mFgsDelegations.size() - 1; i >= 0; i--) { if (mFgsDelegations.keyAt(i).mOptions.isSameDelegate(options)) { Slog.d(TAG, "stopForegroundServiceDelegateLocked " + options.getDescription()); r = mFgsDelegations.valueAt(i); break; } } if (r != null) { bringDownServiceLocked(r, false); } else { Slog.e(TAG, "stopForegroundServiceDelegateLocked delegate does not exist " + options.getDescription()); } } /** * Stop the foreground service delegate by its ServiceConnection. * This removes the process out of foreground service process state. * * @param connection an ServiceConnection object. */ void stopForegroundServiceDelegateLocked(@NonNull ServiceConnection connection) { ServiceRecord r = null; for (int i = mFgsDelegations.size() - 1; i >= 0; i--) { final ForegroundServiceDelegation d = mFgsDelegations.keyAt(i); if (d.mConnection == connection) { Slog.d(TAG, "stopForegroundServiceDelegateLocked " + d.mOptions.getDescription()); r = mFgsDelegations.valueAt(i); break; } } if (r != null) { bringDownServiceLocked(r, false); } else { Slog.e(TAG, "stopForegroundServiceDelegateLocked delegate does not exist"); } } }
services/core/java/com/android/server/am/ActivityManagerLocal.java +25 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.am; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.Context; Loading Loading @@ -92,4 +93,28 @@ public interface ActivityManagerLocal { int clientAppUid, @NonNull String clientAppPackage, @NonNull String processName, @Context.BindServiceFlags int flags) throws RemoteException; /** * Start a foreground service delegate. * @param options foreground service delegate options. * @param connection a service connection served as callback to caller. * @return true if delegate is started successfully, false otherwise. * @hide */ boolean startForegroundServiceDelegate(@NonNull ForegroundServiceDelegationOptions options, @Nullable ServiceConnection connection); /** * Stop a foreground service delegate. * @param options the foreground service delegate options. * @hide */ void stopForegroundServiceDelegate(@NonNull ForegroundServiceDelegationOptions options); /** * Stop a foreground service delegate by service connection. * @param connection service connection used to start delegate previously. * @hide */ void stopForegroundServiceDelegate(@NonNull ServiceConnection connection); }
services/core/java/com/android/server/am/ActivityManagerService.java +77 −0 Original line number Diff line number Diff line Loading @@ -18125,6 +18125,30 @@ public class ActivityManagerService extends IActivityManager.Stub mUidObserverController.register(observer, which, cutpoint, callingPackage, Binder.getCallingUid()); } @Override public boolean startForegroundServiceDelegate( @NonNull ForegroundServiceDelegationOptions options, @Nullable ServiceConnection connection) { synchronized (ActivityManagerService.this) { return mServices.startForegroundServiceDelegateLocked(options, connection); } } @Override public void stopForegroundServiceDelegate( @NonNull ForegroundServiceDelegationOptions options) { synchronized (ActivityManagerService.this) { mServices.stopForegroundServiceDelegateLocked(options); } } @Override public void stopForegroundServiceDelegate(@NonNull ServiceConnection connection) { synchronized (ActivityManagerService.this) { mServices.stopForegroundServiceDelegateLocked(connection); } } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) { Loading Loading @@ -18313,6 +18337,59 @@ public class ActivityManagerService extends IActivityManager.Stub } } /** * Start/stop foreground service delegate on a app's process. * This interface is intended for the shell command to use. */ void setForegroundServiceDelegate(String packageName, int uid, boolean isStart, @ForegroundServiceDelegationOptions.DelegationService int delegateService, String clientInstanceName) { final int callingUid = Binder.getCallingUid(); if (callingUid != SYSTEM_UID && callingUid != ROOT_UID && callingUid != SHELL_UID) { throw new SecurityException( "No permission to start/stop foreground service delegate"); } final long callingId = Binder.clearCallingIdentity(); try { boolean foundPid = false; synchronized (this) { ArrayList<ForegroundServiceDelegationOptions> delegates = new ArrayList<>(); synchronized (mPidsSelfLocked) { for (int i = 0; i < mPidsSelfLocked.size(); i++) { final ProcessRecord p = mPidsSelfLocked.valueAt(i); final IApplicationThread thread = p.getThread(); if (p.uid == uid && thread != null) { foundPid = true; int pid = mPidsSelfLocked.keyAt(i); ForegroundServiceDelegationOptions options = new ForegroundServiceDelegationOptions(pid, uid, packageName, null /* clientAppThread */, false /* isSticky */, clientInstanceName, 0 /* foregroundServiceType */, delegateService); delegates.add(options); } } } for (int i = delegates.size() - 1; i >= 0; i--) { final ForegroundServiceDelegationOptions options = delegates.get(i); if (isStart) { ((ActivityManagerLocal) mInternal).startForegroundServiceDelegate(options, null /* connection */); } else { ((ActivityManagerLocal) mInternal).stopForegroundServiceDelegate(options); } } } if (!foundPid) { Slog.e(TAG, "setForegroundServiceDelegate can not find process for packageName:" + packageName + " uid:" + uid); } } finally { Binder.restoreCallingIdentity(callingId); } } /** * Force the settings cache to be loaded */
services/core/java/com/android/server/am/ActivityManagerShellCommand.java +43 −0 Original line number Diff line number Diff line Loading @@ -369,6 +369,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runResetDropboxRateLimiter(); case "list-secondary-displays-for-starting-users": return runListSecondaryDisplaysForStartingUsers(pw); case "set-foreground-service-delegate": return runSetForegroundServiceDelegate(pw); default: return handleDefaultCommands(cmd); } Loading Loading @@ -3592,6 +3594,45 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } int runSetForegroundServiceDelegate(PrintWriter pw) throws RemoteException { int userId = UserHandle.USER_CURRENT; String opt; while ((opt = getNextOption()) != null) { if (opt.equals("--user")) { userId = UserHandle.parseUserArg(getNextArgRequired()); } else { getErrPrintWriter().println("Error: Unknown option: " + opt); return -1; } } final String packageName = getNextArgRequired(); final String action = getNextArgRequired(); boolean isStart = true; if ("start".equals(action)) { isStart = true; } else if ("stop".equals(action)) { isStart = false; } else { pw.println("Error: action is either start or stop"); return -1; } int uid = INVALID_UID; try { final PackageManager pm = mInternal.mContext.getPackageManager(); uid = pm.getPackageUidAsUser(packageName, PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), userId); } catch (PackageManager.NameNotFoundException e) { pw.println("Error: userId:" + userId + " package:" + packageName + " is not found"); return -1; } mInternal.setForegroundServiceDelegate(packageName, uid, isStart, ForegroundServiceDelegationOptions.DELEGATION_SERVICE_SPECIAL_USE, "FgsDelegate"); return 0; } int runResetDropboxRateLimiter() throws RemoteException { mInternal.resetDropboxRateLimiter(); return 0; Loading Loading @@ -3968,6 +4009,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" list-secondary-displays-for-starting-users"); pw.println(" Lists the id of displays that can be used to start users on " + "background."); pw.println(" set-foreground-service-delegate [--user <USER_ID>] <PACKAGE> start|stop"); pw.println(" Start/stop an app's foreground service delegate."); pw.println(); Intent.printIntentArgsHelp(pw, ""); } Loading
services/core/java/com/android/server/am/ForegroundServiceDelegation.java 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.Nullable; import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; /** * A foreground service delegate which has client options and connection callback. */ public class ForegroundServiceDelegation { public final IBinder mBinder = new Binder(); @NonNull public final ForegroundServiceDelegationOptions mOptions; @Nullable public final ServiceConnection mConnection; public ForegroundServiceDelegation(@NonNull ForegroundServiceDelegationOptions options, @Nullable ServiceConnection connection) { mOptions = options; mConnection = connection; } }