Android组件管理框架—前台服务Service之startForegroundService方式启动流程(Android P)

一 前言

这节的内容也是由于项目中的一个Bug引起:

12-13 10:41:07.520 16661 16661 E AndroidRuntime: FATAL EXCEPTION: main
12-13 10:41:07.520 16661 16661 E AndroidRuntime: Process: cn.xxx.xxxxx:remote, PID: 16661
12-13 10:41:07.520 16661 16661 E AndroidRuntime: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{ed71832 u0 cn.xxx.xxxxx/com.amap.api.location.APSService}
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1879)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:106)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at android.os.Looper.loop(Looper.java:217)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:7356)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:499)
12-13 10:41:07.520 16661 16661 E AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:865)

       Android O 之前,创建前台服务的方式通常是先创建一个后台服务,然后将该服务推到前台。Android O及Android P,系统不允许后台应用创建后台服务,Android O 引入了一种全新的方法,即ContextCompat.startForegroundService() ,以在前台启动新服务。

      启动一个前台服务要做的事情有:

     (1) 调用 ContextCompat.startForegroundService() 可以创建一个前台服务,相当于创建一个后台服务并将它推到前台;

     (2)  创建一个用户可见的 Notification ;

     (3)  必须在5秒内调用该服务的 startForeground(int id, Notification notification) 方法,否则将停止服务并抛出 android.app.RemoteServiceException:Context.startForegroundService() did not then call Service.startForeground()异常。

什么是前台应用
满足以下任意条件的应用被视为处于前台:

具有可见的 Activity;
具有前台服务;
另一个前台应用关联到该应用,如输入法、壁纸服务、语音服务等。
如果以上条件均不满足,应用将被视为处于后台。

前台服务后台服务的区别
        前台服务会在通知一栏显示ONGOING的Notification,当服务被终止的时候,通知一栏的Notification也会消失,这样对于用户有一定的通知作用,常见的如音乐播放服务。

        默认的服务即为后台服务,即不会在通知一栏显示ONGOING的Notification。当服务被终止的时候,用户是看不到效果的,某些不需要运行或终止提示的服务,如天气更新、日期同步、邮件同步等。

       后台服务我们可以自己创建 ONGOING 的 Notification 这样就成为前台服务吗?答案是否定的,前台服务在做了上述工作之后需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为前台服务。这样的话,当服务被外部强制终止掉的时候,ONGOING 的 Notification 也会移除掉。

 

二 图示调用流程

      主要调用流程和StartService的流程基本相似。

 

三 具体代码流程

第一部分

         启动前台服务调用者调用startForegroundService,第一部分主要随着调用流程的分析我们看看为什么会出现Context.startForegroundService() did not then call Service.startForeground()异常。

1 frameworks/base/core/java/android/content/ContextWrapper.java

@Override
public ComponentName startForegroundService(Intent service) {
    return mBase.startForegroundService(service);
}

这里mBase变量是ContextImpl类型,是在创建activity的时候,new 一个ContextImpl对象,赋值给activity的。

2 frameworks/base/core/java/android/app/ContextImpl.java

@Override
public ComponentName startForegroundService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, true, mUser);
}

startServiceCommon

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
    try {
        validateServiceIntent(service);
        service.prepareToLeaveProcess(this);
        ComponentName cn = ActivityManager.getService().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), requireForeground,
                        getOpPackageName(), user.getIdentifier());
        if (cn != null) {
            if (cn.getPackageName().equals("!")) {
                throw new SecurityException(
                        "Not allowed to start service " + service
                        + " without permission " + cn.getClassName());
            } else if (cn.getPackageName().equals("!!")) {
                throw new SecurityException(
                        "Unable to start service " + service
                        + ": " + cn.getClassName());
            } else if (cn.getPackageName().equals("?")) {
                throw new IllegalStateException(
                        "Not allowed to start service " + service + ": " + cn.getClassName());
            }
        }
        return cn;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

       和startService的流程相似,还是走到AMS的startService,requireForeground为true。可以先回看startService的流程Android组件管理框架—后台服务Service之startService方式启动流程(Android P)。

3 frameworks/base/core/java/android/app/ActivityManager.java

4 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

调用到系统进程(System_server)来了。

@Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, boolean requireForeground, String callingPackage, int userId)
        throws TransactionTooLargeException {
    enforceNotIsolatedCaller("startService");
    // Refuse possible leaked file descriptors
    if (service != null && service.hasFileDescriptors() == true) {
        throw new IllegalArgumentException("File descriptors passed in Intent");
    }
 
    if (callingPackage == null) {
        throw new IllegalArgumentException("callingPackage cannot be null");
    }
 
    if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
            "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
    synchronized(this) {
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        ComponentName res;
        try {
            res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid,
                    requireForeground, callingPackage, userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        return res;
    }
}

接着看mServices.startServiceLocked,mServices是ActiveService的实例。

5 frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
        throws TransactionTooLargeException {
    if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
            + " type=" + resolvedType + " args=" + service.getExtras());
 
    final boolean callerFg;
    if (caller != null) {
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        if (callerApp == null) {
            throw new SecurityException(
                    "Unable to find app for caller " + caller
                    + " (pid=" + callingPid
                    + ") when starting service " + service);
        }
        callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
    } else {
        callerFg = true;
    }
 
    //位置1
    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false, false);
    if (res == null) {
        return null;
    }
    if (res.record == null) {
        return new ComponentName("!", res.permission != null
                ? res.permission : "private to package");
    }
 
    ServiceRecord r = res.record;
    ...
    final ServiceMap smap = getServiceMapLocked(r.userId);
    boolean addToStarting = false;
    if (!callerFg && !fgRequired && r.app == null
            && mAm.mUserController.hasStartedUserState(r.userId)) {
        ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
        if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
            // If this is not coming from a foreground caller, then we may want
            // to delay the start if there are already other background services
            // that are starting.  This is to avoid process start spam when lots
            // of applications are all handling things like connectivity broadcasts.
            // We only do this for cached processes, because otherwise an application
            // can have assumptions about calling startService() for a service to run
            // in its own process, and for that process to not be killed before the
            // service is started.  This is especially the case for receivers, which
            // may start a service in onReceive() to do some additional work and have
            // initialized some global state as part of that.
            if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of "
                    + r + " in " + proc);
            if (r.delayed) {
                // This service is already scheduled for a delayed start; just leave
                // it still waiting.
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
                return r.name;
            }
            if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
                // Something else is starting, delay!
                Slog.i(TAG_SERVICE, "Delaying start of: " + r);
                smap.mDelayedStartList.add(r);
                r.delayed = true;
                return r.name;
            }
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
            addToStarting = true;
        } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
            // We slightly loosen when we will enqueue this new service as a background
            // starting service we are waiting for, to also include processes that are
            // currently running other services or receivers.
            addToStarting = true;
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                    "Not delaying, but counting as bg: " + r);
        ...
        //位置2
    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;
}

       通过位置1的retrieveServiceLocked来解析service这个Intent,也就是解析在AndroidManifest.xml定义的Service标签的intent-filter相关内容,然后将解析结果放在res.record中。看位置2的startServiceInnerLocked方法。

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    ServiceState stracker = r.getTracker();
    if (stracker != null) {
        stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
    }
    r.callStart = false;
    synchronized (r.stats.getBatteryStats()) {
        r.stats.startRunningLocked();
    }
    //位置1
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);    
    if (error != null) {
        return new ComponentName("!!", error);
    }
 
    if (r.startRequested && addToStarting) {
        boolean first = smap.mStartingBackground.size() == 0;
        smap.mStartingBackground.add(r);
        r.startingBgTimeout = SystemClock.uptimeMillis() + mAm.mConstants.BG_START_TIMEOUT;
        if (DEBUG_DELAYED_SERVICE) {
            RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
            Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r, here);
        } else if (DEBUG_DELAYED_STARTS) {
            Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r);
        }
        if (first) {
            smap.rescheduleDelayedStartsLocked();
        }
    } else if (callerFg || r.fgRequired) {
        smap.ensureNotStartingBackgroundLocked(r);
    }
 
    return r.name;
}

看位置1的bringUpServiceLocked方法。

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting, boolean permissionsReviewRequired)
        throws TransactionTooLargeException {
    //Slog.i(TAG, "Bring up service:");
    //r.dump("  ");
        
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }
        
    ...
    ProcessRecord app;
 
    if (!isolated) {
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                    + " app=" + app);
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                //位置1
                realStartServiceLocked(r, app, execInFg);
                return null;
            } catch (TransactionTooLargeException e) {
                throw e;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
            }
 
            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }
    } else {
        // If this service runs in an isolated process, then each time
        // we call startProcessLocked() we will get a new isolated
        // process, starting another process if we are currently waiting
        // for a previous process to come up.  To deal with this, we store
        // in the service any current isolated process it is running in or
        // waiting to have come up.
        app = r.isolatedProc;
        if (WebViewZygote.isMultiprocessEnabled()
                && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
            hostingType = "webview_service";
        }
    }
 
    ...
    return null;
}

看位置1处的方法realStartServiceLocked。

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    if (app.thread == null) {
        throw new RemoteException();
    }
    if (DEBUG_MU)
        Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
                + ", ProcessRecord.uid = " + app.uid);
    r.app = app;
    r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

    final boolean newService = app.services.add(r);
    bumpServiceExecutingLocked(r, execInFg, "create");
    mAm.updateLruProcessLocked(app, false, null);
    updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
    mAm.updateOomAdjLocked();

    boolean created = false;
    try {
        if (LOG_SERVICE_START_STOP) {
            String nameTerm;
            int lastPeriod = r.shortName.lastIndexOf('.');
            nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
            EventLogTags.writeAmCreateService(
                    r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
        }
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startLaunchedLocked();
        }
        mAm.notifyPackageUse(r.serviceInfo.packageName,
                            PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        //位置1 发送创建service的msg
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        //发送服务正在后台运行的通知
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        Slog.w(TAG, "Application dead when creating service " + r);
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        if (!created) {
            // Keep the executeNesting count accurate.
            final boolean inDestroying = mDestroyingServices.contains(r);
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);

            // Cleanup.
            if (newService) {
                app.services.remove(r);
                r.app = null;
            }

            // Retry.
            if (!inDestroying) {
                scheduleServiceRestartLocked(r, false);
            }
        }
    }

    if (r.whitelistManager) {
        app.whitelistManager = true;
    }

    requestServiceBindingsLocked(r, execInFg);

    updateServiceClientActivitiesLocked(app, null, true);

    // If the service is in the started state, and there are no
    // pending arguments, then fake up one so its onStartCommand() will
    // be called.
    if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                null, null, 0));
    }
    //位置2 后面会发送timeout 5s msg
    sendServiceArgsLocked(r, execInFg, true);

    if (r.delayed) {
        if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
        getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
        r.delayed = false;
    }

    if (r.delayedStop) {
        // Oh and hey we've already been asked to stop!
        r.delayedStop = false;
        if (r.startRequested) {
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                    "Applying delayed stop (from start): " + r);
            stopServiceLocked(r);
        }
    }
}

       位置1的app.thread.scheduleCreateService发送创建service的msg,最终和startSerive的一样会调用到Service.java的onCreate方法;r.postNotification() 发送服务正在后台运行的通知;位置2的sendServiceArgsLocked方法中会发送timeout 5s msg 。

private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
        boolean oomAdjusted) throws TransactionTooLargeException {
    final int N = r.pendingStarts.size();
    if (N == 0) {
        return;
    }

    ArrayList args = new ArrayList<>();

    while (r.pendingStarts.size() > 0) {
        ServiceRecord.StartItem si = r.pendingStarts.remove(0);
        if (DEBUG_SERVICE) {
            Slog.v(TAG_SERVICE, "Sending arguments to: "
                    + r + " " + r.intent + " args=" + si.intent);
        }
        if (si.intent == null && N > 1) {
            // If somehow we got a dummy null intent in the middle,
            // then skip it.  DO NOT skip a null intent when it is
            // the only one in the list -- this is to support the
            // onStartCommand(null) case.
            continue;
        }
        si.deliveredTime = SystemClock.uptimeMillis();
        r.deliveredStarts.add(si);
        si.deliveryCount++;
        if (si.neededGrants != null) {
            mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
                    si.getUriPermissionsLocked());
        }
        mAm.grantEphemeralAccessLocked(r.userId, si.intent,
                r.appInfo.uid, UserHandle.getAppId(si.callingId));
        bumpServiceExecutingLocked(r, execInFg, "start");
        if (!oomAdjusted) {
            oomAdjusted = true;
            mAm.updateOomAdjLocked(r.app, true);
        }
        // fgRequired = true 表示前台服务,fgWaiting默认值为false,当发送timeout msg 
        //时修改为true。
        if (r.fgRequired && !r.fgWaiting) {
            //r.isForeground为false,表示不在前台
            if (!r.isForeground) {
                if (DEBUG_BACKGROUND_CHECK) {
                    Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
                }
                //位置1 发送 timeout msg
                scheduleServiceForegroundTransitionTimeoutLocked(r);
            } else {
                if (DEBUG_BACKGROUND_CHECK) {
                    Slog.i(TAG, "Service already foreground; no new timeout: " + r);
                }
                r.fgRequired = false;
            }
        }
        int flags = 0;
        if (si.deliveryCount > 1) {
            flags |= Service.START_FLAG_RETRY;
        }
        if (si.doneExecutingCount > 0) {
            flags |= Service.START_FLAG_REDELIVERY;
        }
        args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
    }

    ParceledListSlice slice = new ParceledListSlice<>(args);
    slice.setInlineCountLimit(4);
    Exception caughtException = null;
    try {
        r.app.thread.scheduleServiceArgs(r, slice);
    } catch (TransactionTooLargeException e) {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
                + " args, first: " + args.get(0).args);
        Slog.w(TAG, "Failed delivering service starts", e);
        caughtException = e;
    } catch (RemoteException e) {
        // Remote process gone...  we'll let the normal cleanup take care of this.
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
        Slog.w(TAG, "Failed delivering service starts", e);
        caughtException = e;
    } catch (Exception e) {
        Slog.w(TAG, "Unexpected exception", e);
        caughtException = e;
    }

    if (caughtException != null) {
        // Keep nesting count correct
        final boolean inDestroying = mDestroyingServices.contains(r);
        for (int i = 0; i < args.size(); i++) {
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
        }
        if (caughtException instanceof TransactionTooLargeException) {
            throw (TransactionTooLargeException)caughtException;
        }
    }
}

位置1的scheduleServiceForegroundTransitionTimeoutLocked的方法将发送5s Timeout超时的Message,如果超时则报crash。

void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
    if (r.app.executingServices.size() == 0 || r.app.thread == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
    msg.obj = r;
    r.fgWaiting = true;
    mAm.mHandler.sendMessageDelayed(msg, SERVICE_START_FOREGROUND_TIMEOUT);
}

6 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

mAms是ActivityManagerService的对象,AMS中接收并处理msg。

case SERVICE_FOREGROUND_TIMEOUT_MSG: {
    mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
} break;

7 frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

       mServices是ActiveServices的对象。ActivityServices中停止服务并抛出异常:Context.startForegroundService() did not then call Service.startForeground()。

void serviceForegroundTimeout(ServiceRecord r) {
    ProcessRecord app;
    synchronized (mAm) {
        if (!r.fgRequired || r.destroying) {
            return;
         }

        app = r.app;
        if (app != null && app.debugging) {
            // The app's being debugged; let it ride
            return;
        }

        if (DEBUG_BACKGROUND_CHECK) {
            Slog.i(TAG, "Service foreground-required timeout for " + r);
        }
        r.fgWaiting = false;
        stopServiceLocked(r);
    }

    if (app != null) {
        mAm.mAppErrors.appNotResponding(app, null, null, false,
                "Context.startForegroundService() did not then call Service.startForeground(): "
                    + r);
    }
}

mAms是ActivityManagerService的对象,最终在AMS中打印出该异常信息。

 

第二部分

第二部分,我们看看为什么调用了Service.startForeground()则不会抛出Context.startForegroundService() did not then call Service.startForeground()异常。

1 frameworks/base/core/java/android/app/Service.java

/**
* If your service is started (running through {@link Context#startService(Intent)}), then
* also make this service run in the foreground, supplying the ongoing
* notification to be shown to the user while in this state.
* By default started services are background, meaning that their process won't be given
* foreground CPU scheduling (unless something else in that process is foreground) and,
* if the system needs to kill them to reclaim more memory (such as to display a large page in a
* web browser), they can be killed without too much harm.  You use
* {@link #startForeground} if killing your service would be disruptive to the user, such as
* if your service is performing background music playback, so the user
* would notice if their music stopped playing.
*
* 

Note that calling this method does not put the service in the started state * itself, even though the name sounds like it. You must always call * {@link #startService(Intent)} first to tell the system it should keep the service running, * and then use this method to tell it to keep it running harder.

* *

Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use * this API.

* * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) * NotificationManager.notify(int, Notification)}; must not be 0. * @param notification The Notification to be displayed. * * @see #stopForeground(boolean) */ public final void startForeground(int id, Notification notification) { try { mActivityManager.setServiceForeground( new ComponentName(this, mClassName), mToken, id, notification, 0); } catch (RemoteException ex) { } }

看setServiceForeground方法 。

2 frameworks/base/core/java/android/app/ActivityManager.java

3 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

@Override
public void setServiceForeground(ComponentName className, IBinder token,
        int id, Notification notification, int flags) {
    synchronized(this) {
        mServices.setServiceForegroundLocked(className, token, id, notification, flags);
    }
}

mServices是ActiveServices的对象,setServiceForegroundLocked。

public void setServiceForegroundLocked(ComponentName className, IBinder token,
        int id, Notification notification, int flags) {
    final int userId = UserHandle.getCallingUserId();
    final long origId = Binder.clearCallingIdentity();
    try {
        ServiceRecord r = findServiceLocked(className, token, userId);
        if (r != null) {
            setServiceForegroundInnerLocked(r, id, notification, flags);
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

看setServiceForegroundInnerLocked方法。

/**
* @param id Notification ID.  Zero === exit foreground state for the given service.
*/
private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
        Notification notification, int flags) {
    if (id != 0) {
        if (notification == null) {
            throw new IllegalArgumentException("null notification");
        }
        // Instant apps need permission to create foreground services.
        if (r.appInfo.isInstantApp()) {
            final int mode = mAm.mAppOpsService.checkOperation(
                    AppOpsManager.OP_INSTANT_APP_START_FOREGROUND,
                    r.appInfo.uid,
                    r.appInfo.packageName);
            switch (mode) {
                case AppOpsManager.MODE_ALLOWED:
                    break;
                case AppOpsManager.MODE_IGNORED:
                    Slog.w(TAG, "Instant app " + r.appInfo.packageName
                            + " does not have permission to create foreground services"
                            + ", ignoring.");
                    return;
                case AppOpsManager.MODE_ERRORED:
                    throw new SecurityException("Instant app " + r.appInfo.packageName
                            + " does not have permission to create foreground services");
                default:
                    mAm.enforcePermission(
                            android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
                            r.app.pid, r.appInfo.uid, "startForeground");
            }
        } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
            mAm.enforcePermission(
                    android.Manifest.permission.FOREGROUND_SERVICE,
                    r.app.pid, r.appInfo.uid, "startForeground");
        }
        boolean alreadyStartedOp = false;
        if (r.fgRequired) {
            if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "Service called startForeground() as required: " + r);
            }
            r.fgRequired = false;
            r.fgWaiting = false;
            alreadyStartedOp = true;
            //位置1 移除Timeout超时的Message
            mAm.mHandler.removeMessages(
                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        }

        try {
            boolean ignoreForeground = false;
            final int mode = mAm.mAppOpsService.checkOperation(
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
            switch (mode) {
                case AppOpsManager.MODE_ALLOWED:
                case AppOpsManager.MODE_DEFAULT:
                    // All okay.
                    break;
                case AppOpsManager.MODE_IGNORED:
                    // Whoops, silently ignore this.
                    Slog.w(TAG, "Service.startForeground() not allowed due to app op: service "
                            + r.shortName);
                    ignoreForeground = true;
                    break;
                default:
                    throw new SecurityException("Foreground not allowed as per app op");
            }

            if (!ignoreForeground &&
                    appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
                Slog.w(TAG,
                        "Service.startForeground() not allowed due to bg restriction: service "
                        + r.shortName);
                // Back off of any foreground expectations around this service, since we've
                // just turned down its fg request.
                updateServiceForegroundLocked(r.app, false);
                ignoreForeground = true;
            }

            // Apps under strict background restrictions simply don't get to have foreground
            // services, so now that we've enforced the startForegroundService() contract
            // we only do the machinery of making the service foreground when the app
            // is not restricted.
            if (!ignoreForeground) {
                if (r.foregroundId != id) {
                    cancelForegroundNotificationLocked(r);
                    r.foregroundId = id;
                }
                notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
                r.foregroundNoti = notification;
                if (!r.isForeground) {
                    final ServiceMap smap = getServiceMapLocked(r.userId);
                    if (smap != null) {
                        ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);
                        if (active == null) {
                            active = new ActiveForegroundApp();
                            active.mPackageName = r.packageName;
                            active.mUid = r.appInfo.uid;
                            active.mShownWhileScreenOn = mScreenOn;
                            if (r.app != null) {
                                active.mAppOnTop = active.mShownWhileTop =
                                        r.app.uidRecord.curProcState
                                                <= ActivityManager.PROCESS_STATE_TOP;
                            }
                            active.mStartTime = active.mStartVisibleTime
                                    = SystemClock.elapsedRealtime();
                            smap.mActiveForegroundApps.put(r.packageName, active);
                            requestUpdateActiveForegroundAppsLocked(smap, 0);
                        }
                        active.mNumActive++;
                    }
                    r.isForeground = true;
                    mAm.mAppOpsService.startOperation(
                            AppOpsManager.getToken(mAm.mAppOpsService),
                            AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
                            true);
                    StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                            r.appInfo.uid, r.shortName,
                            StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
                }
                //发送服务正在后台运行的通知
                r.postNotification();
                if (r.app != null) {
                    updateServiceForegroundLocked(r.app, true);
                }
                getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
                mAm.notifyPackageUse(r.serviceInfo.packageName,
                        PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
            } else {
                if (DEBUG_FOREGROUND_SERVICE) {
                    Slog.d(TAG, "Suppressing startForeground() for FAS " + r);
                }
            }
        } finally {
            if (alreadyStartedOp) {
                // If we had previously done a start op for direct foreground start,
                // we have cleared the flag so can now drop it.
                mAm.mAppOpsService.finishOperation(
                        AppOpsManager.getToken(mAm.mAppOpsService),
                        AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
            }
        }
    } else {
        if (r.isForeground) {
            final ServiceMap smap = getServiceMapLocked(r.userId);
            if (smap != null) {
                decActiveForegroundAppLocked(smap, r);
            }
            r.isForeground = false;
            mAm.mAppOpsService.finishOperation(
                    AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
            StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                    r.appInfo.uid, r.shortName,
                    StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
            if (r.app != null) {
                mAm.updateLruProcessLocked(r.app, false, null);
                updateServiceForegroundLocked(r.app, true);
            }
        }
        if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
            cancelForegroundNotificationLocked(r);
            r.foregroundId = 0;
            r.foregroundNoti = null;
        } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
            r.stripForegroundServiceFlagFromNotification();
            if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
                r.foregroundId = 0;
                r.foregroundNoti = null;
            }
        }
    }
}

       可以看到位置1的mAm.mHandler.removeMessages方法会移除Timeout超时的Message 。所以,5秒内调用该服务的 startForeground(int id, Notification notification) 方法,将停止服务并抛出 android.app.RemoteServiceException:Context.startForegroundService() did not then call Service.startForeground()异常。
                      

四  总结

        startForegroundService中主要是通知创建service 并发送一个延迟5s的msg,5s后就会抛出异常。

       在service创建之后会调用onCreate中手动添加的startForeground,它的作用是添加notification避免 “null notification”(startForeground方法参数会传入一个notification);同时移除了startForegroundService中发出的超时msg 避免了Context.startForegroundService() did not then call Service.startForeground()异常。
 

 

 


 

你可能感兴趣的:(Android)