| /* |
| * Copyright (C) 2006 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 android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; |
| import static android.app.PendingIntent.FLAG_IMMUTABLE; |
| import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; |
| import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BOUND_SERVICE; |
| import static android.os.PowerExemptionManager.REASON_DENIED; |
| import static android.os.PowerExemptionManager.reasonCodeToString; |
| import static android.os.Process.INVALID_UID; |
| |
| import static com.android.internal.util.Preconditions.checkArgument; |
| import static com.android.server.am.ActiveServices.TAG_SERVICE; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE; |
| import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; |
| import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.BackgroundStartPrivileges; |
| import android.app.IApplicationThread; |
| import android.app.Notification; |
| import android.app.PendingIntent; |
| import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ServiceInfo; |
| import android.net.Uri; |
| import android.os.Binder; |
| import android.os.Build; |
| import android.os.IBinder; |
| import android.os.PowerExemptionManager; |
| import android.os.SystemClock; |
| import android.os.UserHandle; |
| import android.provider.Settings; |
| import android.util.ArrayMap; |
| import android.util.Slog; |
| import android.util.TimeUtils; |
| import android.util.proto.ProtoOutputStream; |
| import android.util.proto.ProtoUtils; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.app.procstats.ServiceState; |
| import com.android.server.LocalServices; |
| import com.android.server.notification.NotificationManagerInternal; |
| import com.android.server.uri.NeededUriGrants; |
| import com.android.server.uri.UriPermissionOwner; |
| |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * A running application service. |
| */ |
| final class ServiceRecord extends Binder implements ComponentName.WithComponentName { |
| private static final String TAG = TAG_WITH_CLASS_NAME ? "ServiceRecord" : TAG_AM; |
| |
| // Maximum number of delivery attempts before giving up. |
| static final int MAX_DELIVERY_COUNT = 3; |
| |
| // Maximum number of times it can fail during execution before giving up. |
| static final int MAX_DONE_EXECUTING_COUNT = 6; |
| |
| final ActivityManagerService ams; |
| final ComponentName name; // service component. |
| final ComponentName instanceName; // service component's per-instance name. |
| final String shortInstanceName; // instanceName.flattenToShortString(). |
| final String definingPackageName; |
| // Can be different from appInfo.packageName for external services |
| final int definingUid; |
| // Can be different from appInfo.uid for external services |
| final Intent.FilterComparison intent; |
| // original intent used to find service. |
| final ServiceInfo serviceInfo; |
| // all information about the service. |
| ApplicationInfo appInfo; |
| // information about service's app. |
| final int userId; // user that this service is running as |
| final String packageName; // the package implementing intent's component |
| final String processName; // process where this component wants to run |
| final String permission;// permission needed to access service |
| final boolean exported; // from ServiceInfo.exported |
| final Runnable restarter; // used to schedule retries of starting the service |
| final long createRealTime; // when this service was created |
| final boolean isSdkSandbox; // whether this is a sdk sandbox service |
| final int sdkSandboxClientAppUid; // the app uid for which this sdk sandbox service is running |
| final String sdkSandboxClientAppPackage; // the app package for which this sdk sandbox service |
| // is running |
| final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings |
| = new ArrayMap<Intent.FilterComparison, IntentBindRecord>(); |
| // All active bindings to the service. |
| private final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections |
| = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>(); |
| // IBinder -> ConnectionRecord of all bound clients |
| |
| ProcessRecord app; // where this service is running or null. |
| ProcessRecord isolationHostProc; // process which we've started for this service (used for |
| // isolated and sdk sandbox processes) |
| ServiceState tracker; // tracking service execution, may be null |
| ServiceState restartTracker; // tracking service restart |
| boolean allowlistManager; // 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 fgWaiting; // is a timeout for going foreground already scheduled? |
| boolean isNotAppComponentUsage; // is service binding not considered component/package usage? |
| boolean isForeground; // is service currently in foreground mode? |
| boolean inSharedIsolatedProcess; // is the service in a shared isolated process |
| int foregroundId; // Notification ID of last foreground req. |
| Notification foregroundNoti; // Notification record of foreground state. |
| long fgDisplayTime; // time at which the FGS notification should become visible |
| int foregroundServiceType; // foreground service types. |
| long lastActivity; // last time there was some activity on the service. |
| long startingBgTimeout; // time at which we scheduled this for a delayed start. |
| boolean startRequested; // someone explicitly called start? |
| boolean delayedStop; // service has been stopped but is in a delayed start? |
| boolean stopIfKilled; // last onStart() said to stop if service killed? |
| boolean callStart; // last onStart() has asked to always be called on restart. |
| int startCommandResult; // last result from onStartCommand(), only for dumpsys. |
| int executeNesting; // number of outstanding operations keeping foreground. |
| boolean executeFg; // should we be executing in the foreground? |
| long executingStart; // start time of last execute request. |
| boolean createdFromFg; // was this service last created due to a foreground process call? |
| int crashCount; // number of times proc has crashed with service running |
| int totalRestartCount; // number of times we have had to restart. |
| int restartCount; // number of restarts performed in a row. |
| long restartDelay; // delay until next restart attempt. |
| long restartTime; // time of last restart. |
| long nextRestartTime; // time when restartDelay will expire. |
| boolean destroying; // set when we have started destroying the service |
| long destroyTime; // time at which destory was initiated. |
| int pendingConnectionGroup; // To be filled in to ProcessRecord once it connects |
| int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects |
| |
| /** |
| * The last time (in uptime timebase) a bind request was made with BIND_ALMOST_PERCEPTIBLE for |
| * this service while on TOP. |
| */ |
| long lastTopAlmostPerceptibleBindRequestUptimeMs; |
| |
| // any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag? |
| private boolean mIsAllowedBgActivityStartsByBinding; |
| // used to clean up the state of mIsAllowedBgActivityStartsByStart after a timeout |
| private Runnable mCleanUpAllowBgActivityStartsByStartCallback; |
| private ProcessRecord mAppForAllowingBgActivityStartsByStart; |
| // These are the privileges that currently allow bg activity starts by service start. |
| // Each time the contents of this list change #mBackgroundStartPrivilegesByStartMerged has to |
| // be updated to reflect the merged state. The merged state retains the attribution to the |
| // originating token only if it is the only cause for being privileged. |
| @GuardedBy("ams") |
| private ArrayList<BackgroundStartPrivileges> mBackgroundStartPrivilegesByStart = |
| new ArrayList<>(); |
| |
| // merged privileges for mBackgroundStartPrivilegesByStart (for performance) |
| private BackgroundStartPrivileges mBackgroundStartPrivilegesByStartMerged = |
| BackgroundStartPrivileges.NONE; |
| |
| // Reason code for allow while-in-use permissions in foreground service. |
| // If it's not DENIED, while-in-use permissions are allowed. |
| // while-in-use permissions in FGS started from background might be restricted. |
| @PowerExemptionManager.ReasonCode |
| int mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED; |
| |
| // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state. |
| boolean mAllowWhileInUsePermissionInFgsAtEntering; |
| /** Allow scheduling user-initiated jobs from the background. */ |
| boolean mAllowUiJobScheduling; |
| |
| // the most recent package that start/bind this service. |
| String mRecentCallingPackage; |
| // the most recent uid that start/bind this service. |
| int mRecentCallingUid; |
| // ApplicationInfo of the most recent callingPackage that start/bind this service. |
| @Nullable ApplicationInfo mRecentCallerApplicationInfo; |
| |
| // The uptime when the service enters FGS state. |
| long mFgsEnterTime = 0; |
| // The uptime when the service exits FGS state. |
| long mFgsExitTime = 0; |
| // FGS notification is deferred. |
| boolean mFgsNotificationDeferred; |
| // FGS notification was deferred. |
| boolean mFgsNotificationWasDeferred; |
| // FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place. |
| boolean mFgsNotificationShown; |
| // Whether FGS package has permissions to show notifications. |
| boolean mFgsHasNotificationPermission; |
| |
| // allow the service becomes foreground service? Service started from background may not be |
| // allowed to become a foreground service. |
| @PowerExemptionManager.ReasonCode |
| int mAllowStartForegroundNoBinding = REASON_DENIED; |
| // A copy of mAllowStartForeground's value when the service is entering FGS state. |
| @PowerExemptionManager.ReasonCode |
| int mAllowStartForegroundAtEntering = REASON_DENIED; |
| // Debug info why mAllowStartForeground is allowed or denied. |
| String mInfoAllowStartForeground; |
| // Debug info if mAllowStartForeground is allowed because of a temp-allowlist. |
| ActivityManagerService.FgsTempAllowListItem mInfoTempFgsAllowListReason; |
| // Is the same mInfoAllowStartForeground string has been logged before? Used for dedup. |
| boolean mLoggedInfoAllowStartForeground; |
| |
| @PowerExemptionManager.ReasonCode |
| int mAllowWIUInBindService = REASON_DENIED; |
| |
| @PowerExemptionManager.ReasonCode |
| int mAllowWIUByBindings = REASON_DENIED; |
| |
| @PowerExemptionManager.ReasonCode |
| int mAllowStartInBindService = REASON_DENIED; |
| |
| @PowerExemptionManager.ReasonCode |
| int mAllowStartByBindings = REASON_DENIED; |
| |
| @PowerExemptionManager.ReasonCode |
| int getFgsAllowWIU() { |
| return mAllowWhileInUsePermissionInFgsReasonNoBinding != REASON_DENIED |
| ? mAllowWhileInUsePermissionInFgsReasonNoBinding |
| : mAllowWIUInBindService; |
| } |
| |
| boolean isFgsAllowedWIU() { |
| return getFgsAllowWIU() != REASON_DENIED; |
| } |
| |
| @PowerExemptionManager.ReasonCode |
| int getFgsAllowStart() { |
| return mAllowStartForegroundNoBinding != REASON_DENIED |
| ? mAllowStartForegroundNoBinding |
| : (mAllowStartByBindings != REASON_DENIED |
| ? mAllowStartByBindings |
| : mAllowStartInBindService); |
| } |
| |
| boolean isFgsAllowedStart() { |
| return getFgsAllowStart() != REASON_DENIED; |
| } |
| |
| void clearFgsAllowWIU() { |
| mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED; |
| mAllowWIUInBindService = REASON_DENIED; |
| mAllowWIUByBindings = REASON_DENIED; |
| } |
| |
| void clearFgsAllowStart() { |
| mAllowStartForegroundNoBinding = REASON_DENIED; |
| mAllowStartInBindService = REASON_DENIED; |
| mAllowStartByBindings = REASON_DENIED; |
| } |
| |
| @PowerExemptionManager.ReasonCode |
| int reasonOr(@PowerExemptionManager.ReasonCode int first, |
| @PowerExemptionManager.ReasonCode int second) { |
| return first != REASON_DENIED ? first : second; |
| } |
| |
| boolean allowedChanged(@PowerExemptionManager.ReasonCode int first, |
| @PowerExemptionManager.ReasonCode int second) { |
| return (first == REASON_DENIED) != (second == REASON_DENIED); |
| } |
| |
| String changeMessage(@PowerExemptionManager.ReasonCode int first, |
| @PowerExemptionManager.ReasonCode int second) { |
| return reasonOr(first, second) == REASON_DENIED ? "DENIED" |
| : ("ALLOWED (" |
| + reasonCodeToString(first) |
| + "+" |
| + reasonCodeToString(second) |
| + ")"); |
| } |
| |
| private String getFgsInfoForWtf() { |
| return " cmp: " + this.getComponentName().toShortString() |
| + " sdk: " + this.appInfo.targetSdkVersion |
| ; |
| } |
| |
| void maybeLogFgsLogicChange() { |
| final int origWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding, |
| mAllowWIUInBindService); |
| final int newWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding, |
| mAllowWIUByBindings); |
| final int origStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartInBindService); |
| final int newStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartByBindings); |
| |
| final boolean wiuChanged = allowedChanged(origWiu, newWiu); |
| final boolean startChanged = allowedChanged(origStart, newStart); |
| |
| if (!wiuChanged && !startChanged) { |
| return; |
| } |
| final String message = "FGS logic changed:" |
| + (wiuChanged ? " [WIU changed]" : "") |
| + (startChanged ? " [BFSL changed]" : "") |
| + " OW:" // Orig-WIU |
| + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding, |
| mAllowWIUInBindService) |
| + " NW:" // New-WIU |
| + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding, mAllowWIUByBindings) |
| + " OS:" // Orig-start |
| + changeMessage(mAllowStartForegroundNoBinding, mAllowStartInBindService) |
| + " NS:" // New-start |
| + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings) |
| + getFgsInfoForWtf(); |
| Slog.wtf(TAG_SERVICE, message); |
| } |
| |
| // The number of times Service.startForeground() is called, after this service record is |
| // created. (i.e. due to "bound" or "start".) It never decreases, even when stopForeground() |
| // is called. |
| int mStartForegroundCount; |
| |
| // This is a service record of a FGS delegate (not a service record of a real service) |
| boolean mIsFgsDelegate; |
| @Nullable ForegroundServiceDelegation mFgsDelegation; |
| |
| String stringName; // caching of toString |
| |
| private int lastStartId; // identifier of most recent start request. |
| |
| boolean mKeepWarming; // Whether or not it'll keep critical code path of the host warm |
| |
| /** |
| * The original earliest restart time, which considers the number of crashes, etc., |
| * but doesn't include the extra delays we put in between to scatter the restarts; |
| * it's the earliest time this auto service restart could happen alone(except those |
| * batch restarts which happens at time of process attach). |
| */ |
| long mEarliestRestartTime; |
| |
| /** |
| * The original time when the service start is scheduled, it does NOT include the reschedules. |
| * |
| * <p>The {@link #restartDelay} would be updated when its restart is rescheduled, but this field |
| * won't, so it could be used when dumping how long the restart is delayed actually.</p> |
| */ |
| long mRestartSchedulingTime; |
| |
| /** |
| * The snapshot process state when the service is requested (either start or bind). |
| */ |
| int mProcessStateOnRequest; |
| |
| static class StartItem { |
| final ServiceRecord sr; |
| final boolean taskRemoved; |
| final int id; |
| final int callingId; |
| final String mCallingProcessName; |
| final Intent intent; |
| final NeededUriGrants neededGrants; |
| final @Nullable String mCallingPackageName; |
| final int mCallingProcessState; |
| long deliveredTime; |
| int deliveryCount; |
| int doneExecutingCount; |
| UriPermissionOwner uriPermissions; |
| |
| String stringName; // caching of toString |
| |
| StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, |
| Intent _intent, NeededUriGrants _neededGrants, int _callingId, |
| String callingProcessName, @Nullable String callingPackageName, |
| int callingProcessState) { |
| sr = _sr; |
| taskRemoved = _taskRemoved; |
| id = _id; |
| intent = _intent; |
| neededGrants = _neededGrants; |
| callingId = _callingId; |
| mCallingProcessName = callingProcessName; |
| mCallingPackageName = callingPackageName; |
| mCallingProcessState = callingProcessState; |
| } |
| |
| UriPermissionOwner getUriPermissionsLocked() { |
| if (uriPermissions == null) { |
| uriPermissions = new UriPermissionOwner(sr.ams.mUgmInternal, this); |
| } |
| return uriPermissions; |
| } |
| |
| void removeUriPermissionsLocked() { |
| if (uriPermissions != null) { |
| uriPermissions.removeUriPermissions(); |
| uriPermissions = null; |
| } |
| } |
| |
| public void dumpDebug(ProtoOutputStream proto, long fieldId, long now) { |
| long token = proto.start(fieldId); |
| proto.write(ServiceRecordProto.StartItem.ID, id); |
| ProtoUtils.toDuration(proto, |
| ServiceRecordProto.StartItem.DURATION, deliveredTime, now); |
| proto.write(ServiceRecordProto.StartItem.DELIVERY_COUNT, deliveryCount); |
| proto.write(ServiceRecordProto.StartItem.DONE_EXECUTING_COUNT, doneExecutingCount); |
| if (intent != null) { |
| intent.dumpDebug(proto, ServiceRecordProto.StartItem.INTENT, true, true, |
| true, false); |
| } |
| if (neededGrants != null) { |
| neededGrants.dumpDebug(proto, ServiceRecordProto.StartItem.NEEDED_GRANTS); |
| } |
| if (uriPermissions != null) { |
| uriPermissions.dumpDebug(proto, ServiceRecordProto.StartItem.URI_PERMISSIONS); |
| } |
| proto.end(token); |
| } |
| |
| public String toString() { |
| if (stringName != null) { |
| return stringName; |
| } |
| StringBuilder sb = new StringBuilder(128); |
| sb.append("ServiceRecord{") |
| .append(Integer.toHexString(System.identityHashCode(sr))) |
| .append(' ').append(sr.shortInstanceName) |
| .append(" StartItem ") |
| .append(Integer.toHexString(System.identityHashCode(this))) |
| .append(" id=").append(id).append('}'); |
| return stringName = sb.toString(); |
| } |
| } |
| |
| final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>(); |
| // start() arguments which been delivered. |
| final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>(); |
| // start() arguments that haven't yet been delivered. |
| |
| /** |
| * Information specific to "SHORT_SERVICE" FGS. |
| */ |
| class ShortFgsInfo { |
| /** Time FGS started */ |
| private final long mStartTime; |
| |
| /** |
| * Copied from {@link #mStartForegroundCount}. If this is different from the parent's, |
| * that means this instance is stale. |
| */ |
| private int mStartForegroundCount; |
| |
| /** Service's "start ID" when this short-service started. */ |
| private int mStartId; |
| |
| ShortFgsInfo(long startTime) { |
| mStartTime = startTime; |
| update(); |
| } |
| |
| /** |
| * Update {@link #mStartForegroundCount} and {@link #mStartId}. |
| * (but not {@link #mStartTime}) |
| */ |
| public void update() { |
| this.mStartForegroundCount = ServiceRecord.this.mStartForegroundCount; |
| this.mStartId = getLastStartId(); |
| } |
| |
| long getStartTime() { |
| return mStartTime; |
| } |
| |
| int getStartForegroundCount() { |
| return mStartForegroundCount; |
| } |
| |
| int getStartId() { |
| return mStartId; |
| } |
| |
| /** |
| * @return whether this {@link ShortFgsInfo} is still "current" or not -- i.e. |
| * it's "start foreground count" is the same as that of the ServiceRecord's. |
| * |
| * Note, we do _not_ check the "start id" here, because the start id increments if the |
| * app calls startService() or startForegroundService() on the same service, |
| * but that will _not_ update the ShortFgsInfo, and will not extend the timeout. |
| */ |
| boolean isCurrent() { |
| return this.mStartForegroundCount == ServiceRecord.this.mStartForegroundCount; |
| } |
| |
| /** Time when Service.onTimeout() should be called */ |
| long getTimeoutTime() { |
| return mStartTime + ams.mConstants.mShortFgsTimeoutDuration; |
| } |
| |
| /** Time when the procstate should be lowered. */ |
| long getProcStateDemoteTime() { |
| return mStartTime + ams.mConstants.mShortFgsTimeoutDuration |
| + ams.mConstants.mShortFgsProcStateExtraWaitDuration; |
| } |
| |
| /** Time when the app should be declared ANR. */ |
| long getAnrTime() { |
| return mStartTime + ams.mConstants.mShortFgsTimeoutDuration |
| + ams.mConstants.mShortFgsAnrExtraWaitDuration; |
| } |
| |
| String getDescription() { |
| return "sfc=" + this.mStartForegroundCount |
| + " sid=" + this.mStartId |
| + " stime=" + this.mStartTime |
| + " tt=" + this.getTimeoutTime() |
| + " dt=" + this.getProcStateDemoteTime() |
| + " at=" + this.getAnrTime(); |
| } |
| } |
| |
| /** |
| * Keep track of short-fgs specific information. This field gets cleared when the timeout |
| * stops. |
| */ |
| private ShortFgsInfo mShortFgsInfo; |
| |
| void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) { |
| final int N = list.size(); |
| for (int i=0; i<N; i++) { |
| StartItem si = list.get(i); |
| pw.print(prefix); pw.print("#"); pw.print(i); |
| pw.print(" id="); pw.print(si.id); |
| if (now != 0) { |
| pw.print(" dur="); |
| TimeUtils.formatDuration(si.deliveredTime, now, pw); |
| } |
| if (si.deliveryCount != 0) { |
| pw.print(" dc="); pw.print(si.deliveryCount); |
| } |
| if (si.doneExecutingCount != 0) { |
| pw.print(" dxc="); pw.print(si.doneExecutingCount); |
| } |
| pw.println(""); |
| pw.print(prefix); pw.print(" intent="); |
| if (si.intent != null) pw.println(si.intent.toString()); |
| else pw.println("null"); |
| if (si.neededGrants != null) { |
| pw.print(prefix); pw.print(" neededGrants="); |
| pw.println(si.neededGrants); |
| } |
| if (si.uriPermissions != null) { |
| si.uriPermissions.dump(pw, prefix); |
| } |
| } |
| } |
| |
| void dumpDebug(ProtoOutputStream proto, long fieldId) { |
| long token = proto.start(fieldId); |
| proto.write(ServiceRecordProto.SHORT_NAME, this.shortInstanceName); |
| proto.write(ServiceRecordProto.IS_RUNNING, app != null); |
| if (app != null) { |
| proto.write(ServiceRecordProto.PID, app.getPid()); |
| } |
| if (intent != null) { |
| intent.getIntent().dumpDebug(proto, ServiceRecordProto.INTENT, false, true, false, |
| false); |
| } |
| proto.write(ServiceRecordProto.PACKAGE_NAME, packageName); |
| proto.write(ServiceRecordProto.PROCESS_NAME, processName); |
| proto.write(ServiceRecordProto.PERMISSION, permission); |
| |
| long now = SystemClock.uptimeMillis(); |
| long nowReal = SystemClock.elapsedRealtime(); |
| if (appInfo != null) { |
| long appInfoToken = proto.start(ServiceRecordProto.APPINFO); |
| proto.write(ServiceRecordProto.AppInfo.BASE_DIR, appInfo.sourceDir); |
| if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) { |
| proto.write(ServiceRecordProto.AppInfo.RES_DIR, appInfo.publicSourceDir); |
| } |
| proto.write(ServiceRecordProto.AppInfo.DATA_DIR, appInfo.dataDir); |
| proto.end(appInfoToken); |
| } |
| if (app != null) { |
| app.dumpDebug(proto, ServiceRecordProto.APP); |
| } |
| if (isolationHostProc != null) { |
| isolationHostProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC); |
| } |
| proto.write(ServiceRecordProto.WHITELIST_MANAGER, allowlistManager); |
| proto.write(ServiceRecordProto.DELAYED, delayed); |
| if (isForeground || foregroundId != 0) { |
| long fgToken = proto.start(ServiceRecordProto.FOREGROUND); |
| proto.write(ServiceRecordProto.Foreground.ID, foregroundId); |
| foregroundNoti.dumpDebug(proto, ServiceRecordProto.Foreground.NOTIFICATION); |
| proto.write(ServiceRecordProto.Foreground.FOREGROUND_SERVICE_TYPE, |
| foregroundServiceType); |
| proto.end(fgToken); |
| } |
| ProtoUtils.toDuration(proto, ServiceRecordProto.CREATE_REAL_TIME, createRealTime, nowReal); |
| ProtoUtils.toDuration(proto, |
| ServiceRecordProto.STARTING_BG_TIMEOUT, startingBgTimeout, now); |
| ProtoUtils.toDuration(proto, ServiceRecordProto.LAST_ACTIVITY_TIME, lastActivity, now); |
| ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now); |
| proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg); |
| proto.write(ServiceRecordProto.ALLOW_WHILE_IN_USE_PERMISSION_IN_FGS, |
| isFgsAllowedWIU()); |
| |
| if (startRequested || delayedStop || lastStartId != 0) { |
| long startToken = proto.start(ServiceRecordProto.START); |
| proto.write(ServiceRecordProto.Start.START_REQUESTED, startRequested); |
| proto.write(ServiceRecordProto.Start.DELAYED_STOP, delayedStop); |
| proto.write(ServiceRecordProto.Start.STOP_IF_KILLED, stopIfKilled); |
| proto.write(ServiceRecordProto.Start.LAST_START_ID, lastStartId); |
| proto.write(ServiceRecordProto.Start.START_COMMAND_RESULT, startCommandResult); |
| proto.end(startToken); |
| } |
| |
| if (executeNesting != 0) { |
| long executNestingToken = proto.start(ServiceRecordProto.EXECUTE); |
| proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_NESTING, executeNesting); |
| proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_FG, executeFg); |
| ProtoUtils.toDuration(proto, |
| ServiceRecordProto.ExecuteNesting.EXECUTING_START, executingStart, now); |
| proto.end(executNestingToken); |
| } |
| if (destroying || destroyTime != 0) { |
| ProtoUtils.toDuration(proto, ServiceRecordProto.DESTORY_TIME, destroyTime, now); |
| } |
| if (crashCount != 0 || restartCount != 0 || (nextRestartTime - mRestartSchedulingTime) != 0 |
| || nextRestartTime != 0) { |
| long crashToken = proto.start(ServiceRecordProto.CRASH); |
| proto.write(ServiceRecordProto.Crash.RESTART_COUNT, restartCount); |
| ProtoUtils.toDuration(proto, ServiceRecordProto.Crash.RESTART_DELAY, |
| (nextRestartTime - mRestartSchedulingTime), now); |
| ProtoUtils.toDuration(proto, |
| ServiceRecordProto.Crash.NEXT_RESTART_TIME, nextRestartTime, now); |
| proto.write(ServiceRecordProto.Crash.CRASH_COUNT, crashCount); |
| proto.end(crashToken); |
| } |
| |
| if (deliveredStarts.size() > 0) { |
| final int N = deliveredStarts.size(); |
| for (int i = 0; i < N; i++) { |
| deliveredStarts.get(i).dumpDebug(proto, |
| ServiceRecordProto.DELIVERED_STARTS, now); |
| } |
| } |
| if (pendingStarts.size() > 0) { |
| final int N = pendingStarts.size(); |
| for (int i = 0; i < N; i++) { |
| pendingStarts.get(i).dumpDebug(proto, ServiceRecordProto.PENDING_STARTS, now); |
| } |
| } |
| if (bindings.size() > 0) { |
| final int N = bindings.size(); |
| for (int i=0; i<N; i++) { |
| IntentBindRecord b = bindings.valueAt(i); |
| b.dumpDebug(proto, ServiceRecordProto.BINDINGS); |
| } |
| } |
| if (connections.size() > 0) { |
| final int N = connections.size(); |
| for (int conni=0; conni<N; conni++) { |
| ArrayList<ConnectionRecord> c = connections.valueAt(conni); |
| for (int i=0; i<c.size(); i++) { |
| c.get(i).dumpDebug(proto, ServiceRecordProto.CONNECTIONS); |
| } |
| } |
| } |
| if (mShortFgsInfo != null && mShortFgsInfo.isCurrent()) { |
| final long shortFgsToken = proto.start(ServiceRecordProto.SHORT_FGS_INFO); |
| proto.write(ServiceRecordProto.ShortFgsInfo.START_TIME, |
| mShortFgsInfo.getStartTime()); |
| proto.write(ServiceRecordProto.ShortFgsInfo.START_ID, |
| mShortFgsInfo.getStartId()); |
| proto.write(ServiceRecordProto.ShortFgsInfo.TIMEOUT_TIME, |
| mShortFgsInfo.getTimeoutTime()); |
| proto.write(ServiceRecordProto.ShortFgsInfo.PROC_STATE_DEMOTE_TIME, |
| mShortFgsInfo.getProcStateDemoteTime()); |
| proto.write(ServiceRecordProto.ShortFgsInfo.ANR_TIME, |
| mShortFgsInfo.getAnrTime()); |
| proto.end(shortFgsToken); |
| } |
| |
| proto.end(token); |
| } |
| |
| void dump(PrintWriter pw, String prefix) { |
| pw.print(prefix); pw.print("intent={"); |
| pw.print(intent.getIntent().toShortString(false, true, false, false)); |
| pw.println('}'); |
| pw.print(prefix); pw.print("packageName="); pw.println(packageName); |
| pw.print(prefix); pw.print("processName="); pw.println(processName); |
| if (permission != null) { |
| pw.print(prefix); pw.print("permission="); pw.println(permission); |
| } |
| long now = SystemClock.uptimeMillis(); |
| long nowReal = SystemClock.elapsedRealtime(); |
| if (appInfo != null) { |
| pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir); |
| if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) { |
| pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir); |
| } |
| pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir); |
| } |
| pw.print(prefix); pw.print("app="); pw.println(app); |
| if (isolationHostProc != null) { |
| pw.print(prefix); pw.print("isolationHostProc="); pw.println(isolationHostProc); |
| } |
| if (allowlistManager) { |
| pw.print(prefix); pw.print("allowlistManager="); pw.println(allowlistManager); |
| } |
| if (mIsAllowedBgActivityStartsByBinding) { |
| pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByBinding="); |
| pw.println(mIsAllowedBgActivityStartsByBinding); |
| } |
| if (mBackgroundStartPrivilegesByStartMerged.allowsAny()) { |
| pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart="); |
| pw.println(mBackgroundStartPrivilegesByStartMerged); |
| } |
| pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason="); |
| pw.println(PowerExemptionManager.reasonCodeToString( |
| mAllowWhileInUsePermissionInFgsReasonNoBinding)); |
| |
| pw.print(prefix); pw.print("mAllowWIUInBindService="); |
| pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUInBindService)); |
| pw.print(prefix); pw.print("mAllowWIUByBindings="); |
| pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUByBindings)); |
| |
| pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling); |
| pw.print(prefix); pw.print("recentCallingPackage="); |
| pw.println(mRecentCallingPackage); |
| pw.print(prefix); pw.print("recentCallingUid="); |
| pw.println(mRecentCallingUid); |
| pw.print(prefix); pw.print("allowStartForeground="); |
| pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartForegroundNoBinding)); |
| pw.print(prefix); pw.print("mAllowStartInBindService="); |
| pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartInBindService)); |
| pw.print(prefix); pw.print("mAllowStartByBindings="); |
| pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartByBindings)); |
| |
| pw.print(prefix); pw.print("startForegroundCount="); |
| pw.println(mStartForegroundCount); |
| pw.print(prefix); pw.print("infoAllowStartForeground="); |
| pw.println(mInfoAllowStartForeground); |
| |
| if (delayed) { |
| pw.print(prefix); pw.print("delayed="); pw.println(delayed); |
| } |
| if (isForeground || foregroundId != 0) { |
| pw.print(prefix); pw.print("isForeground="); pw.print(isForeground); |
| pw.print(" foregroundId="); pw.print(foregroundId); |
| pw.printf(" types=%08X", foregroundServiceType); |
| pw.print(" foregroundNoti="); pw.println(foregroundNoti); |
| |
| if (isShortFgs() && mShortFgsInfo != null) { |
| pw.print(prefix); pw.print("isShortFgs=true"); |
| pw.print(" startId="); pw.print(mShortFgsInfo.getStartId()); |
| pw.print(" startForegroundCount="); |
| pw.print(mShortFgsInfo.getStartForegroundCount()); |
| pw.print(" startTime="); |
| TimeUtils.formatDuration(mShortFgsInfo.getStartTime(), now, pw); |
| pw.print(" timeout="); |
| TimeUtils.formatDuration(mShortFgsInfo.getTimeoutTime(), now, pw); |
| pw.print(" demoteTime="); |
| TimeUtils.formatDuration(mShortFgsInfo.getProcStateDemoteTime(), now, pw); |
| pw.print(" anrTime="); |
| TimeUtils.formatDuration(mShortFgsInfo.getAnrTime(), now, pw); |
| pw.println(); |
| } |
| } |
| if (mIsFgsDelegate) { |
| pw.print(prefix); pw.print("isFgsDelegate="); pw.println(mIsFgsDelegate); |
| } |
| pw.print(prefix); pw.print("createTime="); |
| TimeUtils.formatDuration(createRealTime, nowReal, pw); |
| pw.print(" startingBgTimeout="); |
| TimeUtils.formatDuration(startingBgTimeout, now, pw); |
| pw.println(); |
| pw.print(prefix); pw.print("lastActivity="); |
| TimeUtils.formatDuration(lastActivity, now, pw); |
| pw.print(" restartTime="); |
| TimeUtils.formatDuration(restartTime, now, pw); |
| pw.print(" createdFromFg="); pw.println(createdFromFg); |
| if (pendingConnectionGroup != 0) { |
| pw.print(prefix); pw.print(" pendingConnectionGroup="); |
| pw.print(pendingConnectionGroup); |
| pw.print(" Importance="); pw.println(pendingConnectionImportance); |
| } |
| if (startRequested || delayedStop || lastStartId != 0) { |
| pw.print(prefix); pw.print("startRequested="); pw.print(startRequested); |
| pw.print(" delayedStop="); pw.print(delayedStop); |
| pw.print(" stopIfKilled="); pw.print(stopIfKilled); |
| pw.print(" callStart="); pw.print(callStart); |
| pw.print(" lastStartId="); pw.println(lastStartId); |
| pw.print(" startCommandResult="); pw.println(startCommandResult); |
| } |
| if (executeNesting != 0) { |
| pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting); |
| pw.print(" executeFg="); pw.print(executeFg); |
| pw.print(" executingStart="); |
| TimeUtils.formatDuration(executingStart, now, pw); |
| pw.println(); |
| } |
| if (destroying || destroyTime != 0) { |
| pw.print(prefix); pw.print("destroying="); pw.print(destroying); |
| pw.print(" destroyTime="); |
| TimeUtils.formatDuration(destroyTime, now, pw); |
| pw.println(); |
| } |
| if (crashCount != 0 || restartCount != 0 |
| || (nextRestartTime - mRestartSchedulingTime) != 0 || nextRestartTime != 0) { |
| pw.print(prefix); pw.print("restartCount="); pw.print(restartCount); |
| pw.print(" restartDelay="); |
| TimeUtils.formatDuration(nextRestartTime - mRestartSchedulingTime, now, pw); |
| pw.print(" nextRestartTime="); |
| TimeUtils.formatDuration(nextRestartTime, now, pw); |
| pw.print(" crashCount="); pw.println(crashCount); |
| } |
| if (deliveredStarts.size() > 0) { |
| pw.print(prefix); pw.println("Delivered Starts:"); |
| dumpStartList(pw, prefix, deliveredStarts, now); |
| } |
| if (pendingStarts.size() > 0) { |
| pw.print(prefix); pw.println("Pending Starts:"); |
| dumpStartList(pw, prefix, pendingStarts, 0); |
| } |
| if (bindings.size() > 0) { |
| pw.print(prefix); pw.println("Bindings:"); |
| for (int i=0; i<bindings.size(); i++) { |
| IntentBindRecord b = bindings.valueAt(i); |
| pw.print(prefix); pw.print("* IntentBindRecord{"); |
| pw.print(Integer.toHexString(System.identityHashCode(b))); |
| if ((b.collectFlags()&Context.BIND_AUTO_CREATE) != 0) { |
| pw.append(" CREATE"); |
| } |
| pw.println("}:"); |
| b.dumpInService(pw, prefix + " "); |
| } |
| } |
| if (connections.size() > 0) { |
| pw.print(prefix); pw.println("All Connections:"); |
| for (int conni=0; conni<connections.size(); conni++) { |
| ArrayList<ConnectionRecord> c = connections.valueAt(conni); |
| for (int i=0; i<c.size(); i++) { |
| pw.print(prefix); pw.print(" "); pw.println(c.get(i)); |
| } |
| } |
| } |
| } |
| |
| /** Used only for tests */ |
| private ServiceRecord(ActivityManagerService ams) { |
| this.ams = ams; |
| name = null; |
| instanceName = null; |
| shortInstanceName = null; |
| definingPackageName = null; |
| definingUid = 0; |
| intent = null; |
| serviceInfo = null; |
| userId = 0; |
| packageName = null; |
| processName = null; |
| permission = null; |
| exported = false; |
| restarter = null; |
| createRealTime = 0; |
| isSdkSandbox = false; |
| sdkSandboxClientAppUid = 0; |
| sdkSandboxClientAppPackage = null; |
| inSharedIsolatedProcess = false; |
| } |
| |
| public static ServiceRecord newEmptyInstanceForTest(ActivityManagerService ams) { |
| return new ServiceRecord(ams); |
| } |
| |
| ServiceRecord(ActivityManagerService ams, ComponentName name, |
| ComponentName instanceName, String definingPackageName, int definingUid, |
| Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg, |
| Runnable restarter) { |
| this(ams, name, instanceName, definingPackageName, definingUid, intent, sInfo, callerIsFg, |
| restarter, sInfo.processName, INVALID_UID, null, false); |
| } |
| |
| ServiceRecord(ActivityManagerService ams, ComponentName name, |
| ComponentName instanceName, String definingPackageName, int definingUid, |
| Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg, |
| Runnable restarter, String processName, int sdkSandboxClientAppUid, |
| String sdkSandboxClientAppPackage, boolean inSharedIsolatedProcess) { |
| this.ams = ams; |
| this.name = name; |
| this.instanceName = instanceName; |
| shortInstanceName = instanceName.flattenToShortString(); |
| this.definingPackageName = definingPackageName; |
| this.definingUid = definingUid; |
| this.intent = intent; |
| serviceInfo = sInfo; |
| appInfo = sInfo.applicationInfo; |
| packageName = sInfo.applicationInfo.packageName; |
| this.isSdkSandbox = sdkSandboxClientAppUid != INVALID_UID; |
| this.sdkSandboxClientAppUid = sdkSandboxClientAppUid; |
| this.sdkSandboxClientAppPackage = sdkSandboxClientAppPackage; |
| this.inSharedIsolatedProcess = inSharedIsolatedProcess; |
| this.processName = processName; |
| permission = sInfo.permission; |
| exported = sInfo.exported; |
| this.restarter = restarter; |
| createRealTime = SystemClock.elapsedRealtime(); |
| lastActivity = SystemClock.uptimeMillis(); |
| userId = UserHandle.getUserId(appInfo.uid); |
| createdFromFg = callerIsFg; |
| updateKeepWarmLocked(); |
| // initialize notification permission state; this'll be updated whenever there's an attempt |
| // to post or update a notification, but that doesn't cover the time before the first |
| // notification |
| updateFgsHasNotificationPermission(); |
| } |
| |
| public ServiceState getTracker() { |
| if (tracker != null) { |
| return tracker; |
| } |
| if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { |
| tracker = ams.mProcessStats.getServiceState(serviceInfo.packageName, |
| serviceInfo.applicationInfo.uid, |
| serviceInfo.applicationInfo.longVersionCode, |
| serviceInfo.processName, serviceInfo.name); |
| if (tracker != null) { |
| tracker.applyNewOwner(this); |
| } |
| } |
| return tracker; |
| } |
| |
| public void forceClearTracker() { |
| if (tracker != null) { |
| tracker.clearCurrentOwner(this, true); |
| tracker = null; |
| } |
| } |
| |
| public void makeRestarting(int memFactor, long now) { |
| if (restartTracker == null) { |
| if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { |
| restartTracker = ams.mProcessStats.getServiceState( |
| serviceInfo.packageName, |
| serviceInfo.applicationInfo.uid, |
| serviceInfo.applicationInfo.longVersionCode, |
| serviceInfo.processName, serviceInfo.name); |
| } |
| if (restartTracker == null) { |
| return; |
| } |
| } |
| restartTracker.setRestarting(true, memFactor, now); |
| } |
| |
| public void setProcess(ProcessRecord proc, IApplicationThread thread, int pid, |
| UidRecord uidRecord) { |
| if (proc != null) { |
| // We're starting a new process for this service, but a previous one is allowed to start |
| // background activities. Remove that ability now (unless the new process is the same as |
| // the previous one, which is a common case). |
| if (mAppForAllowingBgActivityStartsByStart != null) { |
| if (mAppForAllowingBgActivityStartsByStart != proc) { |
| mAppForAllowingBgActivityStartsByStart |
| .removeBackgroundStartPrivileges(this); |
| ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback); |
| } |
| } |
| // Make sure the cleanup callback knows about the new process. |
| mAppForAllowingBgActivityStartsByStart = |
| mBackgroundStartPrivilegesByStartMerged.allowsAny() |
| ? proc : null; |
| BackgroundStartPrivileges backgroundStartPrivileges = |
| getBackgroundStartPrivilegesWithExclusiveToken(); |
| if (backgroundStartPrivileges.allowsAny()) { |
| proc.addOrUpdateBackgroundStartPrivileges(this, |
| backgroundStartPrivileges); |
| } else { |
| proc.removeBackgroundStartPrivileges(this); |
| } |
| } |
| if (app != null && app != proc) { |
| // If the old app is allowed to start bg activities because of a service start, leave it |
| // that way until the cleanup callback runs. Otherwise we can remove its bg activity |
| // start ability immediately (it can't be bound now). |
| if (mBackgroundStartPrivilegesByStartMerged.allowsNothing()) { |
| app.removeBackgroundStartPrivileges(this); |
| } |
| app.mServices.updateBoundClientUids(); |
| app.mServices.updateHostingComonentTypeForBindingsLocked(); |
| } |
| app = proc; |
| updateProcessStateOnRequest(); |
| if (pendingConnectionGroup > 0 && proc != null) { |
| final ProcessServiceRecord psr = proc.mServices; |
| psr.setConnectionService(this); |
| psr.setConnectionGroup(pendingConnectionGroup); |
| psr.setConnectionImportance(pendingConnectionImportance); |
| pendingConnectionGroup = pendingConnectionImportance = 0; |
| } |
| if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) { |
| for (int conni = connections.size() - 1; conni >= 0; conni--) { |
| ArrayList<ConnectionRecord> cr = connections.valueAt(conni); |
| for (int i = 0; i < cr.size(); i++) { |
| final ConnectionRecord conn = cr.get(i); |
| if (proc != null) { |
| conn.startAssociationIfNeeded(); |
| } else { |
| conn.stopAssociation(); |
| } |
| } |
| } |
| } |
| if (proc != null) { |
| proc.mServices.updateBoundClientUids(); |
| proc.mServices.updateHostingComonentTypeForBindingsLocked(); |
| } |
| } |
| |
| void updateProcessStateOnRequest() { |
| mProcessStateOnRequest = app != null && app.getThread() != null && !app.isKilled() |
| ? app.mState.getCurProcState() : PROCESS_STATE_NONEXISTENT; |
| } |
| |
| @NonNull |
| ArrayMap<IBinder, ArrayList<ConnectionRecord>> getConnections() { |
| return connections; |
| } |
| |
| void addConnection(IBinder binder, ConnectionRecord c) { |
| ArrayList<ConnectionRecord> clist = connections.get(binder); |
| if (clist == null) { |
| clist = new ArrayList<>(); |
| connections.put(binder, clist); |
| } |
| clist.add(c); |
| |
| // if we have a process attached, add bound client uid of this connection to it |
| if (app != null) { |
| app.mServices.addBoundClientUid(c.clientUid, c.clientPackageName, c.getFlags()); |
| app.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_BOUND_SERVICE); |
| } |
| } |
| |
| void removeConnection(IBinder binder) { |
| connections.remove(binder); |
| // if we have a process attached, tell it to update the state of bound clients |
| if (app != null) { |
| app.mServices.updateBoundClientUids(); |
| app.mServices.updateHostingComonentTypeForBindingsLocked(); |
| } |
| } |
| |
| /** |
| * @return {@code true} if the killed service which was started by {@link Context#startService} |
| * has no reason to start again. Note this condition doesn't consider the bindings. |
| */ |
| boolean canStopIfKilled(boolean isStartCanceled) { |
| if (isShortFgs()) { // Short-FGS should always stop if killed. |
| return true; |
| } |
| return startRequested && (stopIfKilled || isStartCanceled) && pendingStarts.isEmpty(); |
| } |
| |
| void updateIsAllowedBgActivityStartsByBinding() { |
| boolean isAllowedByBinding = false; |
| for (int conni = connections.size() - 1; conni >= 0; conni--) { |
| ArrayList<ConnectionRecord> cr = connections.valueAt(conni); |
| for (int i = 0; i < cr.size(); i++) { |
| if (cr.get(i).hasFlag(Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS)) { |
| isAllowedByBinding = true; |
| break; |
| } |
| } |
| if (isAllowedByBinding) { |
| break; |
| } |
| } |
| setAllowedBgActivityStartsByBinding(isAllowedByBinding); |
| } |
| |
| void setAllowedBgActivityStartsByBinding(boolean newValue) { |
| mIsAllowedBgActivityStartsByBinding = newValue; |
| updateParentProcessBgActivityStartsToken(); |
| } |
| |
| /** |
| * Called when the service is started with allowBackgroundActivityStarts set. We allow |
| * it for background activity starts, setting up a callback to remove this ability after a |
| * timeout. Note that the ability for starting background activities persists for the process |
| * even if the service is subsequently stopped. |
| */ |
| void allowBgActivityStartsOnServiceStart(BackgroundStartPrivileges backgroundStartPrivileges) { |
| checkArgument(backgroundStartPrivileges.allowsAny()); |
| mBackgroundStartPrivilegesByStart.add(backgroundStartPrivileges); |
| setAllowedBgActivityStartsByStart( |
| backgroundStartPrivileges.merge(mBackgroundStartPrivilegesByStartMerged)); |
| if (app != null) { |
| mAppForAllowingBgActivityStartsByStart = app; |
| } |
| |
| // This callback is stateless, so we create it once when we first need it. |
| if (mCleanUpAllowBgActivityStartsByStartCallback == null) { |
| mCleanUpAllowBgActivityStartsByStartCallback = () -> { |
| synchronized (ams) { |
| mBackgroundStartPrivilegesByStart.remove(0); |
| if (!mBackgroundStartPrivilegesByStart.isEmpty()) { |
| // recalculate the merged token |
| mBackgroundStartPrivilegesByStartMerged = |
| BackgroundStartPrivileges.merge(mBackgroundStartPrivilegesByStart); |
| |
| // There are other callbacks in the queue, let's just update the originating |
| // token |
| if (mBackgroundStartPrivilegesByStartMerged.allowsAny()) { |
| // mAppForAllowingBgActivityStartsByStart can be null here for example |
| // if get 2 calls to allowBgActivityStartsOnServiceStart() without a |
| // process attached to this ServiceRecord, so we need to perform a null |
| // check here. |
| if (mAppForAllowingBgActivityStartsByStart != null) { |
| mAppForAllowingBgActivityStartsByStart |
| .addOrUpdateBackgroundStartPrivileges(this, |
| getBackgroundStartPrivilegesWithExclusiveToken()); |
| } |
| } else { |
| Slog.wtf(TAG, |
| "Service callback to revoke bg activity starts by service " |
| + "start triggered but " |
| + "mBackgroundStartPrivilegesByStartMerged = " |
| + mBackgroundStartPrivilegesByStartMerged |
| + ". This should never happen."); |
| } |
| } else { |
| // Last callback on the queue |
| if (app == mAppForAllowingBgActivityStartsByStart) { |
| // The process we allowed is still running the service. We remove |
| // the ability by start, but it may still be allowed via bound |
| // connections. |
| setAllowedBgActivityStartsByStart(BackgroundStartPrivileges.NONE); |
| } else if (mAppForAllowingBgActivityStartsByStart != null) { |
| // The process we allowed is not running the service. It therefore can't |
| // be bound so we can unconditionally remove the ability. |
| mAppForAllowingBgActivityStartsByStart |
| .removeBackgroundStartPrivileges(ServiceRecord.this); |
| } |
| mAppForAllowingBgActivityStartsByStart = null; |
| } |
| } |
| }; |
| } |
| |
| // Existing callbacks will only update the originating token, only when the last callback is |
| // executed is the grant revoked. |
| ams.mHandler.postDelayed(mCleanUpAllowBgActivityStartsByStartCallback, |
| ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT); |
| } |
| |
| void updateAllowUiJobScheduling(boolean allowUiJobScheduling) { |
| if (mAllowUiJobScheduling == allowUiJobScheduling) { |
| return; |
| } |
| mAllowUiJobScheduling = allowUiJobScheduling; |
| } |
| |
| private void setAllowedBgActivityStartsByStart(BackgroundStartPrivileges newValue) { |
| if (mBackgroundStartPrivilegesByStartMerged == newValue) { |
| return; |
| } |
| mBackgroundStartPrivilegesByStartMerged = newValue; |
| updateParentProcessBgActivityStartsToken(); |
| } |
| |
| /** |
| * Whether the process this service runs in should be temporarily allowed to start |
| * activities from background depends on the current state of both |
| * {@code mIsAllowedBgActivityStartsByStart} and |
| * {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord |
| * should be contributing as a token in parent ProcessRecord. |
| * |
| * @see com.android.server.am.ProcessRecord#addOrUpdateBackgroundStartPrivileges(Binder, |
| * BackgroundStartPrivileges) |
| * @see com.android.server.am.ProcessRecord#removeBackgroundStartPrivileges(Binder) |
| */ |
| private void updateParentProcessBgActivityStartsToken() { |
| if (app == null) { |
| return; |
| } |
| BackgroundStartPrivileges backgroundStartPrivileges = |
| getBackgroundStartPrivilegesWithExclusiveToken(); |
| if (backgroundStartPrivileges.allowsAny()) { |
| // if the token is already there it's safe to "re-add it" - we're dealing with |
| // a set of Binder objects |
| app.addOrUpdateBackgroundStartPrivileges(this, |
| backgroundStartPrivileges); |
| } else { |
| app.removeBackgroundStartPrivileges(this); |
| } |
| } |
| |
| /** |
| * Returns {@link BackgroundStartPrivileges} that represents the privileges a specific |
| * originating token or a generic aggregate token. |
| * |
| * If all privileges are associated with the same token (i.e. the service is only allowed due |
| * to starts) the token will be retained, otherwise (e.g. the privileges were granted by |
| * bindings) the originating token will be empty. |
| */ |
| @Nullable |
| private BackgroundStartPrivileges getBackgroundStartPrivilegesWithExclusiveToken() { |
| if (mIsAllowedBgActivityStartsByBinding) { |
| return BackgroundStartPrivileges.ALLOW_BAL; |
| } |
| if (mBackgroundStartPrivilegesByStart.isEmpty()) { |
| return BackgroundStartPrivileges.NONE; |
| } |
| return mBackgroundStartPrivilegesByStartMerged; |
| } |
| |
| @GuardedBy("ams") |
| void updateKeepWarmLocked() { |
| mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name) |
| && (ams.mUserController.getCurrentUserId() == userId |
| || ams.mUserController.isCurrentProfile(userId) |
| || ams.isSingleton(processName, appInfo, instanceName.getClassName(), |
| serviceInfo.flags)); |
| } |
| |
| public AppBindRecord retrieveAppBindingLocked(Intent intent, |
| ProcessRecord app, ProcessRecord attributedApp) { |
| Intent.FilterComparison filter = new Intent.FilterComparison(intent); |
| IntentBindRecord i = bindings.get(filter); |
| if (i == null) { |
| i = new IntentBindRecord(this, filter); |
| bindings.put(filter, i); |
| } |
| AppBindRecord a = i.apps.get(app); |
| if (a != null) { |
| return a; |
| } |
| a = new AppBindRecord(this, i, app, attributedApp); |
| i.apps.put(app, a); |
| return a; |
| } |
| |
| public boolean hasAutoCreateConnections() { |
| // XXX should probably keep a count of the number of auto-create |
| // connections directly in the service. |
| for (int conni=connections.size()-1; conni>=0; conni--) { |
| ArrayList<ConnectionRecord> cr = connections.valueAt(conni); |
| for (int i=0; i<cr.size(); i++) { |
| if (cr.get(i).hasFlag(Context.BIND_AUTO_CREATE)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public void updateAllowlistManager() { |
| allowlistManager = false; |
| for (int conni=connections.size()-1; conni>=0; conni--) { |
| ArrayList<ConnectionRecord> cr = connections.valueAt(conni); |
| for (int i=0; i<cr.size(); i++) { |
| if (cr.get(i).hasFlag(Context.BIND_ALLOW_WHITELIST_MANAGEMENT)) { |
| allowlistManager = true; |
| return; |
| } |
| } |
| } |
| } |
| |
| public void resetRestartCounter() { |
| restartCount = 0; |
| restartDelay = 0; |
| restartTime = 0; |
| mEarliestRestartTime = 0; |
| mRestartSchedulingTime = 0; |
| } |
| |
| public StartItem findDeliveredStart(int id, boolean taskRemoved, boolean remove) { |
| final int N = deliveredStarts.size(); |
| for (int i=0; i<N; i++) { |
| StartItem si = deliveredStarts.get(i); |
| if (si.id == id && si.taskRemoved == taskRemoved) { |
| if (remove) deliveredStarts.remove(i); |
| return si; |
| } |
| } |
| |
| return null; |
| } |
| |
| public int getLastStartId() { |
| return lastStartId; |
| } |
| |
| public int makeNextStartId() { |
| lastStartId++; |
| if (lastStartId < 1) { |
| lastStartId = 1; |
| } |
| return lastStartId; |
| } |
| |
| private void updateFgsHasNotificationPermission() { |
| // Do asynchronous communication with notification manager to avoid deadlocks. |
| final String localPackageName = packageName; |
| final int appUid = appInfo.uid; |
| |
| ams.mHandler.post(new Runnable() { |
| public void run() { |
| NotificationManagerInternal nm = LocalServices.getService( |
| NotificationManagerInternal.class); |
| if (nm == null) { |
| return; |
| } |
| // Record whether the package has permission to notify the user |
| mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage( |
| localPackageName, appUid); |
| } |
| }); |
| } |
| |
| public void postNotification(boolean byForegroundService) { |
| if (isForeground && foregroundNoti != null && app != null) { |
| final int appUid = appInfo.uid; |
| final int appPid = app.getPid(); |
| // Do asynchronous communication with notification manager to |
| // avoid deadlocks. |
| final String localPackageName = packageName; |
| final int localForegroundId = foregroundId; |
| final Notification _foregroundNoti = foregroundNoti; |
| final ServiceRecord record = this; |
| if (DEBUG_FOREGROUND_SERVICE) { |
| Slog.d(TAG, "Posting notification " + _foregroundNoti |
| + " for foreground service " + this); |
| } |
| ams.mHandler.post(new Runnable() { |
| public void run() { |
| NotificationManagerInternal nm = LocalServices.getService( |
| NotificationManagerInternal.class); |
| if (nm == null) { |
| return; |
| } |
| // Record whether the package has permission to notify the user |
| mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage( |
| localPackageName, appUid); |
| Notification localForegroundNoti = _foregroundNoti; |
| try { |
| if (localForegroundNoti.getSmallIcon() == null) { |
| // It is not correct for the caller to not supply a notification |
| // icon, but this used to be able to slip through, so for |
| // those dirty apps we will create a notification clearly |
| // blaming the app. |
| Slog.v(TAG, "Attempted to start a foreground service (" |
| + shortInstanceName |
| + ") with a broken notification (no icon: " |
| + localForegroundNoti |
| + ")"); |
| |
| CharSequence appName = appInfo.loadLabel( |
| ams.mContext.getPackageManager()); |
| if (appName == null) { |
| appName = appInfo.packageName; |
| } |
| Context ctx = null; |
| try { |
| ctx = ams.mContext.createPackageContextAsUser( |
| appInfo.packageName, 0, new UserHandle(userId)); |
| |
| Notification.Builder notiBuilder = new Notification.Builder(ctx, |
| localForegroundNoti.getChannelId()); |
| |
| // it's ugly, but it clearly identifies the app |
| notiBuilder.setSmallIcon(appInfo.icon); |
| |
| // mark as foreground |
| notiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true); |
| |
| Intent runningIntent = new Intent( |
| Settings.ACTION_APPLICATION_DETAILS_SETTINGS); |
| runningIntent.setData(Uri.fromParts("package", |
| appInfo.packageName, null)); |
| PendingIntent pi = PendingIntent.getActivityAsUser(ams.mContext, 0, |
| runningIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, null, |
| UserHandle.of(userId)); |
| notiBuilder.setColor(ams.mContext.getColor( |
| com.android.internal |
| .R.color.system_notification_accent_color)); |
| notiBuilder.setContentTitle( |
| ams.mContext.getString( |
| com.android.internal.R.string |
| .app_running_notification_title, |
| appName)); |
| notiBuilder.setContentText( |
| ams.mContext.getString( |
| com.android.internal.R.string |
| .app_running_notification_text, |
| appName)); |
| notiBuilder.setContentIntent(pi); |
| |
| localForegroundNoti = notiBuilder.build(); |
| } catch (PackageManager.NameNotFoundException e) { |
| } |
| } |
| if (nm.getNotificationChannel(localPackageName, appUid, |
| localForegroundNoti.getChannelId()) == null) { |
| int targetSdkVersion = Build.VERSION_CODES.O_MR1; |
| try { |
| final ApplicationInfo applicationInfo = |
| ams.mContext.getPackageManager().getApplicationInfoAsUser( |
| appInfo.packageName, 0, userId); |
| targetSdkVersion = applicationInfo.targetSdkVersion; |
| } catch (PackageManager.NameNotFoundException e) { |
| } |
| if (targetSdkVersion >= Build.VERSION_CODES.O_MR1) { |
| throw new RuntimeException( |
| "invalid channel for service notification: " |
| + foregroundNoti); |
| } |
| } |
| if (localForegroundNoti.getSmallIcon() == null) { |
| // Notifications whose icon is 0 are defined to not show |
| // 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: " |
| + foregroundNoti); |
| } |
| nm.enqueueNotification(localPackageName, localPackageName, |
| appUid, appPid, null, localForegroundId, localForegroundNoti, |
| userId, byForegroundService /* byForegroundService */); |
| |
| foregroundNoti = localForegroundNoti; // save it for amending next time |
| |
| signalForegroundServiceNotification(packageName, appInfo.uid, |
| localForegroundId, false /* canceling */); |
| |
| } catch (RuntimeException e) { |
| Slog.w(TAG, "Error showing notification for service", e); |
| // If it gave us a garbage notification, it doesn't |
| // get to be foreground. |
| ams.mServices.killMisbehavingService(record, |
| appUid, appPid, localPackageName, |
| CannotPostForegroundServiceNotificationException.TYPE_ID); |
| } |
| } |
| }); |
| } |
| } |
| |
| public void cancelNotification() { |
| // Do asynchronous communication with notification manager to |
| // avoid deadlocks. |
| final String localPackageName = packageName; |
| final int localForegroundId = foregroundId; |
| final int appUid = appInfo.uid; |
| final int appPid = app != null ? app.getPid() : 0; |
| ams.mHandler.post(new Runnable() { |
| public void run() { |
| NotificationManagerInternal nm = LocalServices.getService( |
| NotificationManagerInternal.class); |
| if (nm == null) { |
| return; |
| } |
| try { |
| nm.cancelNotification(localPackageName, localPackageName, appUid, appPid, |
| null, localForegroundId, userId); |
| } catch (RuntimeException e) { |
| Slog.w(TAG, "Error canceling notification for service", e); |
| } |
| signalForegroundServiceNotification(packageName, appInfo.uid, localForegroundId, |
| true /* canceling */); |
| } |
| }); |
| } |
| |
| private void signalForegroundServiceNotification(String packageName, int uid, |
| int foregroundId, boolean canceling) { |
| synchronized (ams) { |
| for (int i = ams.mForegroundServiceStateListeners.size() - 1; i >= 0; i--) { |
| ams.mForegroundServiceStateListeners.get(i).onForegroundServiceNotificationUpdated( |
| packageName, appInfo.uid, foregroundId, canceling); |
| } |
| } |
| } |
| |
| public void stripForegroundServiceFlagFromNotification() { |
| final int localForegroundId = foregroundId; |
| final int localUserId = userId; |
| final String localPackageName = packageName; |
| |
| // Do asynchronous communication with notification manager to |
| // avoid deadlocks. |
| ams.mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| NotificationManagerInternal nmi = LocalServices.getService( |
| NotificationManagerInternal.class); |
| if (nmi == null) { |
| return; |
| } |
| nmi.removeForegroundServiceFlagFromNotification(localPackageName, localForegroundId, |
| localUserId); |
| } |
| }); |
| } |
| |
| public void clearDeliveredStartsLocked() { |
| for (int i=deliveredStarts.size()-1; i>=0; i--) { |
| deliveredStarts.get(i).removeUriPermissionsLocked(); |
| } |
| deliveredStarts.clear(); |
| } |
| |
| public String toString() { |
| if (stringName != null) { |
| return stringName; |
| } |
| StringBuilder sb = new StringBuilder(128); |
| sb.append("ServiceRecord{") |
| .append(Integer.toHexString(System.identityHashCode(this))) |
| .append(" u").append(userId) |
| .append(' ').append(shortInstanceName); |
| if (mRecentCallingPackage != null) { |
| sb.append(" c:").append(mRecentCallingPackage); |
| } |
| sb.append('}'); |
| return stringName = sb.toString(); |
| } |
| |
| public ComponentName getComponentName() { |
| return name; |
| } |
| |
| /** |
| * @return true if it's a foreground service of the "short service" type and don't have |
| * other fgs type bits set. |
| */ |
| public boolean isShortFgs() { |
| // Note if the type contains FOREGROUND_SERVICE_TYPE_SHORT_SERVICE but also other bits |
| // set, it's _not_ considered be a short service. (because we shouldn't apply |
| // the short-service restrictions) |
| // (But we should be preventing mixture of FOREGROUND_SERVICE_TYPE_SHORT_SERVICE |
| // and other types in Service.startForeground().) |
| return startRequested && isForeground |
| && (foregroundServiceType == ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE); |
| } |
| |
| public ShortFgsInfo getShortFgsInfo() { |
| return isShortFgs() ? mShortFgsInfo : null; |
| } |
| |
| /** |
| * Call it when a short FGS starts. |
| */ |
| public void setShortFgsInfo(long uptimeNow) { |
| this.mShortFgsInfo = new ShortFgsInfo(uptimeNow); |
| } |
| |
| /** @return whether {@link #mShortFgsInfo} is set or not. */ |
| public boolean hasShortFgsInfo() { |
| return mShortFgsInfo != null; |
| } |
| |
| /** |
| * Call it when a short FGS stops. |
| */ |
| public void clearShortFgsInfo() { |
| this.mShortFgsInfo = null; |
| } |
| |
| private boolean shouldTriggerShortFgsTimedEvent(long targetTime, long nowUptime) { |
| if (!isAppAlive()) { |
| return false; |
| } |
| if (!this.startRequested || !isShortFgs() || mShortFgsInfo == null |
| || !mShortFgsInfo.isCurrent()) { |
| return false; |
| } |
| return targetTime <= nowUptime; |
| } |
| |
| /** |
| * @return true if it's a short FGS that's still up and running, and should be timed out. |
| */ |
| public boolean shouldTriggerShortFgsTimeout(long nowUptime) { |
| return shouldTriggerShortFgsTimedEvent( |
| (mShortFgsInfo == null ? 0 : mShortFgsInfo.getTimeoutTime()), |
| nowUptime); |
| } |
| |
| /** |
| * @return true if it's a short FGS's procstate should be demoted. |
| */ |
| public boolean shouldDemoteShortFgsProcState(long nowUptime) { |
| return shouldTriggerShortFgsTimedEvent( |
| (mShortFgsInfo == null ? 0 : mShortFgsInfo.getProcStateDemoteTime()), |
| nowUptime); |
| } |
| |
| /** |
| * @return true if it's a short FGS that's still up and running, and should be declared |
| * an ANR. |
| */ |
| public boolean shouldTriggerShortFgsAnr(long nowUptime) { |
| return shouldTriggerShortFgsTimedEvent( |
| (mShortFgsInfo == null ? 0 : mShortFgsInfo.getAnrTime()), |
| nowUptime); |
| } |
| |
| /** |
| * Human readable description about short-FGS internal states. |
| */ |
| public String getShortFgsTimedEventDescription(long nowUptime) { |
| return "aa=" + isAppAlive() |
| + " sreq=" + this.startRequested |
| + " isfg=" + this.isForeground |
| + " type=" + Integer.toHexString(this.foregroundServiceType) |
| + " sfc=" + this.mStartForegroundCount |
| + " now=" + nowUptime |
| + " " + (mShortFgsInfo == null ? "" : mShortFgsInfo.getDescription()); |
| } |
| |
| private boolean isAppAlive() { |
| if (app == null) { |
| return false; |
| } |
| if (app.getThread() == null || app.isKilled() || app.isKilledByAm()) { |
| return false; |
| } |
| return true; |
| } |
| } |