深入理解AlarmManagerService

Quibbler 2020-8-2 2864

深入理解AlarmManagerService


        通过AlarmManager方便的设置定时任务,详见《AlarmManager定时任务》。现在来看看其中更深层次的原理,主AlarmManager要通过AIDL远程调用AlarmManagerService接口方法来在系统Service中设置定时任务。以set方法为例跟随设置流程来看看一个定时任务是如何被添加到系统中的。




1、setImpl(...)

        所有调用AlarmManager设置定时任务的方法最终都是调用setImpl(int type, long triggerAtMillis, long windowMillis,long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,String listenerTag, Handler targetHandler, WorkSource workSource,AlarmClockInfo alarmClock)私有方法,


1.1、参数含义

       setImpl(...)方法一共有11个参数,可见Android提供的AlarmManager类为开发者做了很多封装。setImpl(...)方法中每个参数的含义如下:

        ①type:定时任务类型,详见《AlarmManager定时任务》第二节

        ②triggerAtMillis:触发时间

        ③windowMillis:窗口时间

        ④intervalMillis:重复时间

        ⑤flags:一般为0,内部使用;此外还有FLAG_STANDALONE、FLAG_WAKE_FROM_IDLE、FLAG_ALLOW_WHILE_IDLE等

        ⑥operation:时间到时执行的PendingIntent

        ⑦listener:回调OnAlarmListener 

        ⑧listenerTag:tag标记

        ⑨targetHandler:在该Handler上执行OnAlarmListener 回调,如为null则默认在应用程序Main Handler上执行

        ⑩workSource:一般为null

        ⑪alarmClock:一般为null


1.2、完整代码

        setImpl(...)方法的源码如下,一共

    private void setImpl(@AlarmType int type, long triggerAtMillis, long windowMillis,
            long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
            String listenerTag, Handler targetHandler, WorkSource workSource,
            AlarmClockInfo alarmClock) {
        if (triggerAtMillis < 0) {
            /* NOTYET
            if (mAlwaysExact) {
                // Fatal error for KLP+ apps to use negative trigger times
                throw new IllegalArgumentException("Invalid alarm trigger time "
                        + triggerAtMillis);
            }
            */
            triggerAtMillis = 0;
        }
        ListenerWrapper recipientWrapper = null;
        if (listener != null) {
            synchronized (AlarmManager.class) {
                if (sWrappers == null) {
                    sWrappers = new ArrayMap<OnAlarmListener, ListenerWrapper>();
                }
                recipientWrapper = sWrappers.get(listener);
                // no existing wrapper => build a new one
                if (recipientWrapper == null) {
                    recipientWrapper = new ListenerWrapper(listener);
                    sWrappers.put(listener, recipientWrapper);
                }
            }
            final Handler handler = (targetHandler != null) ? targetHandler : mMainThreadHandler;
            recipientWrapper.setHandler(handler);
        }
        try {
            mService.set(mPackageName, type, triggerAtMillis, windowMillis, intervalMillis, flags,
                    operation, recipientWrapper, listenerTag, workSource, alarmClock);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }


1.3、代码片段详解

        传入设置定时任务的时间triggerAtMillis为负就设置为0,并不会抛出异常,一般如果传入的时间为负没有意义理应直接抛出异常才对。

        if (triggerAtMillis < 0) {
            /* NOTYET
            if (mAlwaysExact) {
                // Fatal error for KLP+ apps to use negative trigger times
                throw new IllegalArgumentException("Invalid alarm trigger time "
                        + triggerAtMillis);
            }
            */
            triggerAtMillis = 0;
        }

        这一段代码的含义就是设置将OnAlarmListener封装为ListenerWrapper,并且保存在AlarmManager静态成员集合sWrappers中。

        ListenerWrapper recipientWrapper = null;
        if (listener != null) {
            synchronized (AlarmManager.class) {
                if (sWrappers == null) {
                    sWrappers = new ArrayMap<OnAlarmListener, ListenerWrapper>();
                }
                recipientWrapper = sWrappers.get(listener);
                // no existing wrapper => build a new one
                if (recipientWrapper == null) {
                    recipientWrapper = new ListenerWrapper(listener);
                    sWrappers.put(listener, recipientWrapper);
                }
            }
            final Handler handler = (targetHandler != null) ? targetHandler : mMainThreadHandler;
            recipientWrapper.setHandler(handler);
        }

        并且设置执行OnAlarmListener的Handler,可以看到传入的targetHandler为null,就使用主线程的Handler来执行。AlarmManager中的mMainThreadHandler成员变量在类构造方法中使用主线程的Main Looper构造。

    /**
     * package private on purpose
     */
    AlarmManager(IAlarmManager service, Context ctx) {
        mService = service;
        mContext = ctx;
        mPackageName = ctx.getPackageName();
        mTargetSdkVersion = ctx.getApplicationInfo().targetSdkVersion;
        mAlwaysExact = (mTargetSdkVersion < Build.VERSION_CODES.KITKAT);
        mMainThreadHandler = new Handler(ctx.getMainLooper());
    }

        最后,也是最关键的一步,借助mService设置定时任务。最终还是由Service来接管定时任务。

        try {
            mService.set(mPackageName, type, triggerAtMillis, windowMillis, intervalMillis, flags,
                    operation, recipientWrapper, listenerTag, workSource, alarmClock);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }

        mServiceIAlarmManager,是AIDL接口类,定义在android.app包中。

    @UnsupportedAppUsage
    private final IAlarmManager mService;



2、AlarmManagerService

        IAlarmManager AIDL接口在AlarmManagerService中实现。AlarmManagerService属于系统其它服务。

        

2.1、AlarmManagerService的创建和启动

        SystemServer启动的过程中,在startOtherServices()方法中启动该服务,详见《AMS服务的启动》

    /**
     * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.
     */
    private void startOtherServices() {
        ...
            traceBeginAndSlog("StartAlarmManagerService");
            mSystemServiceManager.startService(new AlarmManagerService(context));
        ...
    }

        调用AlarmManagerService的构造函数

    AlarmManagerService(Context context, Injector injector) {
        super(context);
        mInjector = injector;
    }
    
    AlarmManagerService(Context context) {
        this(context, new Injector(context));
    }

        在《AMS服务的启动》2.1节中了解到startService()开启服务调用的是各个系统服务的onStart()方法,AlarmManagerService的onStart()方法完整代码如下: 

    public void onStart() {
        mInjector.init();
        synchronized (mLock) {
            mHandler = new AlarmHandler();
            mOperationCancelListener = (intent) -> removeImpl(intent, null);
            mConstants = new Constants(mHandler);
            mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
            mNextWakeup = mNextNonWakeup = 0;
            // We have to set current TimeZone info to kernel
            // because kernel doesn't keep this after reboot
            setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
            // Ensure that we're booting with a halfway sensible current time.  Use the
            // most recent of Build.TIME, the root file system's timestamp, and the
            // value of the ro.build.date.utc system property (which is in seconds).
            final long systemBuildTime =  Long.max(
                    1000L * SystemProperties.getLong("ro.build.date.utc", -1L),
                    Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
            if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
                Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis()
                        + ", advancing to build time " + systemBuildTime);
                mInjector.setKernelTime(systemBuildTime);
            }
            // Determine SysUI's uid
            mSystemUiUid = mInjector.getSystemUiUid();
            if (mSystemUiUid <= 0) {
                Slog.wtf(TAG, "SysUI package not found!");
            }
            mWakeLock = mInjector.getAlarmWakeLock();
            mTimeTickIntent = new Intent(Intent.ACTION_TIME_TICK).addFlags(
                    Intent.FLAG_RECEIVER_REGISTERED_ONLY
                    | Intent.FLAG_RECEIVER_FOREGROUND
                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
            mTimeTickTrigger = new IAlarmListener.Stub() {
                @Override
                public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException {
                    if (DEBUG_BATCH) {
                        Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
                    }
                    // Via handler because dispatch invokes this within its lock.  OnAlarmListener
                    // takes care of this automatically, but we're using the direct internal
                    // interface here rather than that client-side wrapper infrastructure.
                    mHandler.post(() -> {
                        getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL);
                        try {
                            callback.alarmComplete(this);
                        } catch (RemoteException e) { /* local method call */ }
                    });
                    synchronized (mLock) {
                        mLastTickReceived = mInjector.getCurrentTimeMillis();
                    }
                    mClockReceiver.scheduleTimeTickEvent();
                }
            };
            Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
            mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
                    Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
            mClockReceiver = mInjector.getClockReceiver(this);
            new InteractiveStateReceiver();
            new UninstallReceiver();
            if (mInjector.isAlarmDriverPresent()) {
                AlarmThread waitThread = new AlarmThread();
                waitThread.start();
            } else {
                Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
            }
            try {
                ActivityManager.getService().registerUidObserver(new UidObserver(),
                        ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
                                | ActivityManager.UID_OBSERVER_ACTIVE,
                        ActivityManager.PROCESS_STATE_UNKNOWN, null);
            } catch (RemoteException e) {
                // ignored; both services live in system_server
            }
        }
        publishLocalService(AlarmManagerInternal.class, new LocalService());
        publishBinderService(Context.ALARM_SERVICE, mService);
    }

        主要完成创建AlarmHandler注册广播接收器监听应用的卸载安装LocalService注册服务mService服务发布等关键操作。


2.2、IAlarmManager AIDL接口

        IAlarmManagerAIDL接口定义在android.app包中,定义了如下7个接口方法,都是AlarmManager中用到的:

    /**
     * System private API for talking with the alarm manager service.
     */
    interface IAlarmManager {
        /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
        
        void set(String callingPackage, int type, long triggerAtTime, long windowLength,
                long interval, int flags, in PendingIntent operation, in IAlarmListener listener,
                String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock);
        boolean setTime(long millis);
        void setTimeZone(String zone);
        void remove(in PendingIntent operation, in IAlarmListener listener);
        long getNextWakeFromIdleTime();
        AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
        long currentNetworkTimeMillis();
    }


2.3、IAlarmManager接口实现

        mService是IAlarmManager接口匿名实现类的实例,实现了IAlarmManager中的接口方法。

    private final IBinder mService = new IAlarmManager.Stub() {
        @Override
        public void set(String callingPackage,
                int type, long triggerAtTime, long windowLength, long interval, int flags,
                PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
                WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) {
            final int callingUid = Binder.getCallingUid();
            // make sure the caller is not lying about which package should be blamed for
            // wakelock time spent in alarm delivery
            mAppOps.checkPackage(callingUid, callingPackage);
            // Repeating alarms must use PendingIntent, not direct listener
            if (interval != 0) {
                if (directReceiver != null) {
                    throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers");
                }
            }
            if (workSource != null) {
                getContext().enforcePermission(
                        android.Manifest.permission.UPDATE_DEVICE_STATS,
                        Binder.getCallingPid(), callingUid, "AlarmManager.set");
            }
            // No incoming callers can request either WAKE_FROM_IDLE or
            // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
            flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE
                    | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
            // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
            // manager when to come out of idle mode, which is only for DeviceIdleController.
            if (callingUid != Process.SYSTEM_UID) {
                flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
            }
            // If this is an exact time alarm, then it can't be batched with other alarms.
            if (windowLength == AlarmManager.WINDOW_EXACT) {
                flags |= AlarmManager.FLAG_STANDALONE;
            }
            // If this alarm is for an alarm clock, then it must be standalone and we will
            // use it to wake early from idle if needed.
            if (alarmClock != null) {
                flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
            // If the caller is a core system component or on the user's whitelist, and not calling
            // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
            // This means we will allow these alarms to go off as normal even while idle, with no
            // timing restrictions.
            } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
                    || UserHandle.isSameApp(callingUid, mSystemUiUid)
                    || ((mAppStateTracker != null)
                        && mAppStateTracker.isUidPowerSaveUserWhitelisted(callingUid)))) {
                flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
            }
            setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
                    listenerTag, flags, workSource, alarmClock, callingUid, callingPackage);
        }
        
        @Override
        public boolean setTime(long millis) {
            getContext().enforceCallingOrSelfPermission(
                    "android.permission.SET_TIME",
                    "setTime");
            return setTimeImpl(millis);
        }
        
        @Override
        public void setTimeZone(String tz) {
            getContext().enforceCallingOrSelfPermission(
                    "android.permission.SET_TIME_ZONE",
                    "setTimeZone");
            final long oldId = Binder.clearCallingIdentity();
            try {
                setTimeZoneImpl(tz);
            } finally {
                Binder.restoreCallingIdentity(oldId);
            }
        }
        
        @Override
        public void remove(PendingIntent operation, IAlarmListener listener) {
            if (operation == null && listener == null) {
                Slog.w(TAG, "remove() with no intent or listener");
                return;
            }
            synchronized (mLock) {
                removeLocked(operation, listener);
            }
            mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
                    operation).sendToTarget();
        }
        
        @Override
        public long getNextWakeFromIdleTime() {
            return getNextWakeFromIdleTimeImpl();
        }
        
        @Override
        public AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) {
            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, false /* allowAll */, false /* requireFull */,
                    "getNextAlarmClock", null);
            return getNextAlarmClockImpl(userId);
        }
        
        @Override
        public long currentNetworkTimeMillis() {
            final NtpTrustedTime time = NtpTrustedTime.getInstance(getContext());
            if (time.hasCache()) {
                return time.currentTimeMillis();
            } else {
                throw new ParcelableException(new DateTimeException("Missing NTP fix"));
            }
        }
        
        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
            if (args.length > 0 && "--proto".equals(args[0])) {
                dumpProto(fd);
            } else {
                dumpImpl(pw);
            }
        }
        
        @Override
        public void onShellCommand(FileDescriptor in, FileDescriptor out,
                FileDescriptor err, String[] args, ShellCallback callback,
                ResultReceiver resultReceiver) {
            (new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver);
        }
    };


2.4、Alarm内部类

        AlarmManagerService内部封装了一个Alarm类,包含传入的PendingIntent还有一些时间参数。

    static class Alarm {
        public final int type;
        public final long origWhen;
        public final boolean wakeup;
        public final PendingIntent operation;
        public final IAlarmListener listener;
        public final String listenerTag;
        public final String statsTag;
        public final WorkSource workSource;
        public final int flags;
        public final AlarmManager.AlarmClockInfo alarmClock;
        ...
    }



3、set(...)

        重点来看看IAlarmManager接口的set(...)实现方法的执行流程。


3.1、set(...)实现

        IAlarmManager中的set(...)方法在Stub匿名类中实现,完整实现方法如下:

        public void set(String callingPackage,
                int type, long triggerAtTime, long windowLength, long interval, int flags,
                PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
                WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) {
            final int callingUid = Binder.getCallingUid();
            // make sure the caller is not lying about which package should be blamed for
            // wakelock time spent in alarm delivery
            mAppOps.checkPackage(callingUid, callingPackage);
            // Repeating alarms must use PendingIntent, not direct listener
            if (interval != 0) {
                if (directReceiver != null) {
                    throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers");
                }
            }
            if (workSource != null) {
                getContext().enforcePermission(
                        android.Manifest.permission.UPDATE_DEVICE_STATS,
                        Binder.getCallingPid(), callingUid, "AlarmManager.set");
            }
            // No incoming callers can request either WAKE_FROM_IDLE or
            // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
            flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE
                    | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
            // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
            // manager when to come out of idle mode, which is only for DeviceIdleController.
            if (callingUid != Process.SYSTEM_UID) {
                flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
            }
            // If this is an exact time alarm, then it can't be batched with other alarms.
            if (windowLength == AlarmManager.WINDOW_EXACT) {
                flags |= AlarmManager.FLAG_STANDALONE;
            }
            // If this alarm is for an alarm clock, then it must be standalone and we will
            // use it to wake early from idle if needed.
            if (alarmClock != null) {
                flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
            // If the caller is a core system component or on the user's whitelist, and not calling
            // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
            // This means we will allow these alarms to go off as normal even while idle, with no
            // timing restrictions.
            } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
                    || UserHandle.isSameApp(callingUid, mSystemUiUid)
                    || ((mAppStateTracker != null)
                        && mAppStateTracker.isUidPowerSaveUserWhitelisted(callingUid)))) {
                flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
            }
            setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
                    listenerTag, flags, workSource, alarmClock, callingUid, callingPackage);
        }

        注意其中小细节,判断如果设置了重复的定时任务,即interval不为零(为什么不是大于零呢?难道重复还能负?)。那么会判断IAlarmListener是否为空,不空就抛出异常,注意说明已经很清楚了“Repeating alarms must use PendingIntent, not direct listener”,设置重复定时任务仅允许使用PendIngIntent,不准使用OnAlarmListener。不然出现问题还不知道哪里错了,原来Android系统在这里进行了条件限制!!!


3.2、setImpl(...)

        调用了void setImpl(int type, long triggerAtTime, long windowLength, long interval,PendingIntent operation, IAlarmListener directReceiver, String listenerTag,int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,int callingUid, String callingPackage)方法,和AlarmManager类似的同名方法。

    void setImpl(int type, long triggerAtTime, long windowLength, long interval,
            PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
            int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
            int callingUid, String callingPackage) {
        // must be *either* PendingIntent or AlarmReceiver, but not both
        if ((operation == null && directReceiver == null)
                || (operation != null && directReceiver != null)) {
            Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver");
            // NB: previous releases failed silently here, so we are continuing to do the same
            // rather than throw an IllegalArgumentException.
            return;
        }
        ...
            setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
                    interval, operation, directReceiver, listenerTag, flags, true, workSource,
                    alarmClock, callingUid, callingPackage);
        }
    }

        继续调用setImplLocked(...)方法

    private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
            long maxWhen, long interval, PendingIntent operation, IAlarmListener directReceiver,
            String listenerTag, int flags, boolean doValidate, WorkSource workSource,
            AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
        Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
                operation, directReceiver, listenerTag, workSource, flags, alarmClock,
                callingUid, callingPackage);
        try {
            if (ActivityManager.getService().isAppStartModeDisabled(callingUid, callingPackage)) {
                Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
                        + " -- package not allowed to start");
                mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
                        operation).sendToTarget();
                return;
            }
        } catch (RemoteException e) {
        }
        removeLocked(operation, directReceiver);
        incrementAlarmCount(a.uid);
        setImplLocked(a, false, doValidate);
    }

        接着调用另一个同名的重载函数setImplLocked(Alarm a, boolean rebatching, boolean doValidate) 

    private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
        ...
        if (!rebatching) {
            if (DEBUG_VALIDATE) {
                if (doValidate && !validateConsistencyLocked()) {
                    Slog.v(TAG, "Tipping-point operation: type=" + a.type + " when=" + a.when
                            + " when(hex)=" + Long.toHexString(a.when)
                            + " whenElapsed=" + a.whenElapsed
                            + " maxWhenElapsed=" + a.maxWhenElapsed
                            + " interval=" + a.repeatInterval + " op=" + a.operation
                            + " flags=0x" + Integer.toHexString(a.flags));
                    rebatchAllAlarmsLocked(false);
                    needRebatch = false;
                }
            }
        ...
    }

        调用rebatchAllAlarmsLocked(boolean)方法

    void rebatchAllAlarmsLocked(boolean doValidate) {
        ...
        rescheduleKernelAlarmsLocked();
        updateNextAlarmClockLocked();
        mStatLogger.logDurationStat(Stats.REBATCH_ALL_ALARMS, start);
    }

        调用rescheduleKernelAlarmsLocked()方法

    void rescheduleKernelAlarmsLocked() {
        // Schedule the next upcoming wakeup alarm.  If there is a deliverable batch
        // prior to that which contains no wakeups, we schedule that as well.
        ...
            if (firstWakeup != null) {
                mNextWakeup = firstWakeup.start;
                mNextWakeUpSetAt = nowElapsed;
                setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
            }
        ...
    }

        调用setLocked(...)方法

    private void setLocked(int type, long when) {
        if (mInjector.isAlarmDriverPresent()) {
            mInjector.setAlarm(type, when);
        } else {
            Message msg = Message.obtain();
            msg.what = AlarmHandler.ALARM_EVENT;
            mHandler.removeMessages(msg.what);
            mHandler.sendMessageAtTime(msg, when);
        }
    }


3.3、Injector.setAlarm

        最后调用Injector的setAlarm,Injector类实例在AlarmManagerService构造函数中创建

    void setAlarm(int type, long millis) {
        // The kernel never triggers alarms with negative wakeup times
        // so we ensure they are positive.
        final long alarmSeconds, alarmNanoseconds;
        if (millis < 0) {
            alarmSeconds = 0;
            alarmNanoseconds = 0;
        } else {
            alarmSeconds = millis / 1000;
            alarmNanoseconds = (millis % 1000) * 1000 * 1000;
        }
        final int result = AlarmManagerService.set(mNativeData, type, alarmSeconds,
                alarmNanoseconds);
        if (result != 0) {
            final long nowElapsed = SystemClock.elapsedRealtime();
            Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed
                    + " type=" + type + " @ (" + alarmSeconds + "," + alarmNanoseconds
                    + "), ret = " + result + " = " + Os.strerror(result));
        }
    }


3.4、native set()方法

        最后到了native方法set设置定时任务,底层使用C++实现的。

    private static native int set(long nativeData, int type, long seconds, long nanoseconds);

        service系统服务相关的native类和方法在/frameworks/base/services/core/jni目录下,和AlarmManagerService相关的native代码在com_android_server_AlarmManagerService.cpp中。定义的set底层方法为:

    static jint android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, 
                                                       jint type, jlong seconds, jlong nanoseconds)
    {
        AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
        struct timespec ts;
        ts.tv_sec = seconds;
        ts.tv_nsec = nanoseconds;
        const int result = impl->set(type, &ts);
        if (result < 0)
        {
            ALOGE("Unable to set alarm to %lld.%09lld: %s\n",
                  static_cast<long long>(seconds),
                  static_cast<long long>(nanoseconds), strerror(errno));
        }
        return result >= 0 ? 0 : errno;
    }

        调用AlarmImpl类的set方法

    int AlarmImpl::set(int type, struct timespec *ts)
    {
        if (static_cast<size_t>(type) > ANDROID_ALARM_TYPE_COUNT) {
            errno = EINVAL;
            return -1;
        }
        if (!ts->tv_nsec && !ts->tv_sec) {
            ts->tv_nsec = 1;
        }
        /* timerfd interprets 0 = disarm, so replace with a practically
           equivalent deadline of 1 ns */
        struct itimerspec spec;
        memset(&spec, 0, sizeof(spec));
        memcpy(&spec.it_value, ts, sizeof(spec.it_value));
        return timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL);
    }

        最后通过Linux的timerfd_settime函数设置定时对象,并返回文件描述符。

        了解一下timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行定时通知,通常配合select/poll/epoll使用。

        到这里就算结束了,从应用层到frameworks层再到linux底层都理清了一遍,了解了通过AlarmManager设置定时任务的大概流程和原理。


推荐博客:

        AlarmManagerService深入分析

        AlarmManagerService

        timerfd的使用方法

        linux timerfd系列函数总结


上传的附件:
不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com