AlarmManager详解

作者: 开开向前冲 | 来源:发表于2017-08-21 15:44 被阅读1451次

    版权说明:本文为 开开向前冲 原创文章,转载请注明出处;
    注:限于作者水平有限,文中有不对的地方还请指教

    项目需求:AP需要在开机24小时后自检重启;
    针对上述需求,我们首先想想有哪些实现方法呢?

    • 1:开机接收广播,启动一个常驻服务,在服务中轮训。轮训的方式可以通过Handler或者启动一个常驻服务,在服务中开启线程中做死循环;
    • 2:使用Timer 来定时操作;
    • 3:使用AlarmManager 来实现定时操作功能;

    上述三个方法看似都可行;实际上只有最后一种可行,前两种都是不可行的,为什么不可行呢?
    一:服务轮训:Handler发送消息,消息的发送依赖于Handler 线程,如果线程结束,GG!那开启一个常驻服务呢,在服务中开启一个线程做死循环,我们先不说省电问题,如果系统进入深度睡眠,即使这个while(1)的循环也不能得到执行;
    二:Timer:Timer的问题也是在于如果系统进入深度睡眠,将无法唤醒;

    所以只有AlarmManager。可以通过AlarmManager定时唤醒系统执行任务,即使系统处于深度睡眠也能唤醒——这就和我们的闹钟差不多;也能省电;那使用AlarmManager如果做呢?

    代码敬上:

    AlarmManager aManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
            Intent in = new Intent();
            in.setClass(context, RebootService.class);
            PendingIntent pi = PendingIntent.getService(context, 0, in, 0);
            aManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,24 * 60 * 60 * 1000, pi);
    

    没错,AlarmManager的使用就是这个简单,在开机后24小时系统将会启动RebootService,RebootService是我自己实现的一个服务,在这个服务中任意Lifecycle中实现关机操作就OK;

                PowerManager pm = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
                pm.reboot("self-inspection");
    

    AlarmManager不仅可以唤醒服务,也可以发送广播,也可以调起Activity;需要将上述PendingIntent.getService()修改成对应的PendingIntent.getActivity()或者PendingIntent.getBroadcast()方法;

    因为之前有维护过原生Clock APP,对AlarmManager有了解,正好借此机会完整机会阐述一下AlarmManager;

    AlarmManager 对象获取:

     AlarmManager aManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    

    AlarmManager 常用接口:

    AlarmManager.png
    • set(int type, long triggerAtMillis, PendingIntent operation)
      一次性任务;
    • setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
      重复任务;时间固定;
    • setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
      重复任务,时间不固定;
    • cancel(PendingIntent operation)
      取消上述设置的定时任务;此时PendingIntent务必和需要取消的任务的PendingIntent一模一样;

    这里主要介绍了几个常用于定时任务的接口;关于AlarmManager 的接口和用法,
    可以参考Google官方API文档AlarmManager 文档

    接口参数详解:

    • int type
    AlarmManager.RTC:硬件时间,不唤醒休眠设备;当休眠时不发起闹钟。
    AlarmManager.RTC_WAKEUP:硬件时间,当闹钟发射时唤醒休眠设备;
    AlarmManager.ELAPSED_REALTIME:真实时间流逝,不唤醒休眠设备;当设备休眠时不发起闹钟。
    AlarmManager.ELAPSED_REALTIME_WAKEUP:真实时间流逝,当闹钟发起时唤醒手机休眠;
    
        RTC闹钟和ELAPSED_REALTIME 最大的差别就是前者可以通过修改手机时间触发闹钟事件,
    后者要通过真实时间的流逝,即使在休眠状态,时间也会被计算。
    
    • long triggerAtMillis : 闹钟第一次执行时间,毫秒为单位,需与第一个type参数匹配,
      1. 如果是RTC类型,triggerAtMillis 则一般使用System.currentTimeMillis();
      2. 如果是ELAPSED类型,triggerAtMillis 则一般使用SystemClock.elapsedRealtime();
    • long intervalMillis : 两次闹钟执行间隔
    • PendingIntent operation : 任务的执行动作,发送广播,启动activity,启动service

    上述大概讲了AlarmManager接口如何使用,作为一名System Engineer,我们还需要研究研究真实的服务提供者AlarmManagerService:

    从上述名字中我们知道AlarmManager 只是AlarmManagerService的代理,实际实现都是在AlarmManagerService中实现的;

    AlarmManagerService启动

    frameworks\base\services\java\com\android\server\SystemServer.java
    private void startOtherServices() {
          ...
                mAlarmManagerService = mSystemServiceManager.startService(AlarmManagerService.class);
        //mSystemServiceManager 即是SystemServiceManger
                alarm = IAlarmManager.Stub.asInterface(
                        ServiceManager.getService(Context.ALARM_SERVICE));
          ...
    }
    
    frameworks\base\services\core\java\com\android\server\SystemServiceManager.java
    public <T extends SystemService> T startService(Class<T> serviceClass) {
    ...
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
    ...
                mServices.add(service);
                service.onStart(); //service 即是AlarmManagerService
    }
    
    frameworks\base\services\core\java\com\android\server\AlarmManagerService.java
    @Override
        public void onStart() {
            mNativeData = init();//init方法是native 方法,调用JNI
            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));
    
            PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");
    
            mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
                    new Intent(Intent.ACTION_TIME_TICK).addFlags(
                            Intent.FLAG_RECEIVER_REGISTERED_ONLY
                            | Intent.FLAG_RECEIVER_FOREGROUND), 0,
                            UserHandle.ALL);
            Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
            mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
                    Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
            
            // now that we have initied the driver schedule the alarm
            mClockReceiver = new ClockReceiver();
            mClockReceiver.scheduleTimeTickEvent();
            mClockReceiver.scheduleDateChangedEvent();
            mInteractiveStateReceiver = new InteractiveStateReceiver();
            mUninstallReceiver = new UninstallReceiver();
            /*
            上述代码注册了各种监听器,监听各种和时间变化相关的事务
            */
            if (mNativeData != 0) {
                AlarmThread waitThread = new AlarmThread(); //创建AlarmThread
                waitThread.start();
            } else {
                Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
            }
    
            publishBinderService(Context.ALARM_SERVICE, mService); //注册服务,调用ServiceManager.addService添加服务;
        }
    

    最后的publishBinderService即调用ServiceManager.addService注册服务,服务

    AlarmManagerService的onStart方法工作

      1. 由于重启内核没有时区信息,需要将时区信息保存到内核;
      2. 创建ClockReceiver用于监听TIME_TICK和DATE_CHANGED广播;
      3. 创建InteractiveStateReceiver,用于监听亮屏/灭屏广播;
      4. 创建UninstallReceiver,用于监听package移除/重启,sdcard不可用的广播;
      5. 创建线程”AlarmManager”;
      6. 注册服务。
    

    AlarmManagerService中的native JNI方法:

        private native long init();
        private native void close(long nativeData);
        private native void set(long nativeData, int type, long seconds, long nanoseconds);
        private native void clear(long nativeData, int type, long seconds, long nanoseconds);
        private native int waitForAlarm(long nativeData);
        private native int setKernelTime(long nativeData, long millis);
        private native int setKernelTimezone(long nativeData, int minuteswest);
    

    我们根据Android JNI 文件命名规范(报名中"."替换为"_")知道对应的JNI文件名为com_android_server_AlarmManagerService.cpp

    static JNINativeMethod sMethods[] = {
         /* name, signature, funcPtr */
        {"init", "()J", (void*)android_server_AlarmManagerService_init},
        {"close", "(J)V", (void*)android_server_AlarmManagerService_close},
        {"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set},
        {"clear", "(JIJJ)V", (void*)android_server_AlarmManagerService_clear},
        {"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
        {"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime},
        {"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
    };
    

    上述说道在AlarmManagerService.java中onStart方法中有调用native JNI init()方法;

    static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject)
    {
        jlong ret = init_alarm_driver(); //初始化alarm driver
        if (ret) {
            return ret;
        }
        return init_timerfd(); //初始化文件描述符
    }
    
    static jlong init_alarm_driver()
    {
        int fd = open("/dev/alarm", O_RDWR); //打开节点/dev/alarm,并创建Alarm驱动对象。
        if (fd < 0) {
            ALOGV("opening alarm driver failed: %s", strerror(errno));
            return 0;
        }
        AlarmImpl *ret = new AlarmImplAlarmDriver(fd);//创建AlarmImplAlarmDriver对象
        return reinterpret_cast<jlong>(ret);
    }
    
    static jlong init_timerfd()
    {
        int epollfd;
        int fds[N_ANDROID_TIMERFDS];
         ......
        epollfd = epoll_create(N_ANDROID_TIMERFDS);
        for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
            fds[i] = timerfd_create(android_alarm_to_clockid[i], 0);
           ......
        }
        AlarmImpl *ret = new AlarmImplTimerFd(fds, epollfd);//创建AlarmImplTimerFd对象
    
        for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
            epoll_event event;
            event.events = EPOLLIN | EPOLLWAKEUP;
            event.data.u32 = i;
    
            int err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], &event);
            if (err < 0) {
                ALOGV("epoll_ctl(EPOLL_CTL_ADD) failed: %s", strerror(errno));
                delete ret;
                return 0;
            }
        }
    
        struct itimerspec spec;
        memset(&spec, 0, sizeof(spec));
        /* 0 = disarmed; the timerfd doesn't need to be armed to get
           RTC change notifications, just set up as cancelable */
    
        int err = timerfd_settime(fds[ANDROID_ALARM_TYPE_COUNT],
                TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, NULL);
        if (err < 0) {
            ALOGV("timerfd_settime() failed: %s", strerror(errno));
            delete ret;
            return 0;
        }
    
        return reinterpret_cast<jlong>(ret);
    }
    //android_alarm_to_clockid 数组的定义
    android_alarm_to_clockid[N_ANDROID_TIMERFDS] = {
        CLOCK_REALTIME_ALARM,
        CLOCK_REALTIME,
        CLOCK_BOOTTIME_ALARM,
        CLOCK_BOOTTIME,
        CLOCK_MONOTONIC,
        CLOCK_REALTIME,
    };
    

    AlarmThread

    private class AlarmThread extends Thread
        {
            public AlarmThread()
            {
                super("AlarmManager");
            }
            
            public void run()
            {
                ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
    
                while (true) //无线循环
                {
                    int result = waitForAlarm(mNativeData); //JNI 方法,使用EPOLL监听FD,等待驱动返回执行下面的分发执行;
    
                    triggerList.clear();
    
                    if ((result & TIME_CHANGED_MASK) != 0) {
                        if (DEBUG_BATCH) {
                            Slog.v(TAG, "Time changed notification from kernel; rebatching");
                        }
                        removeImpl(mTimeTickSender);
                        rebatchAllAlarms();
                        mClockReceiver.scheduleTimeTickEvent();
                        synchronized (mLock) {
                            mNumTimeChanged++;
                        }
                        Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
                        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                                | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                                | Intent.FLAG_RECEIVER_FOREGROUND);
                        getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
                    }
                    
                    synchronized (mLock) {
                        final long nowRTC = System.currentTimeMillis();
                        final long nowELAPSED = SystemClock.elapsedRealtime();
                        if (localLOGV) Slog.v(
                            TAG, "Checking for alarms... rtc=" + nowRTC
                            + ", elapsed=" + nowELAPSED);
    
                        if (WAKEUP_STATS) {
                            if ((result & IS_WAKEUP_MASK) != 0) {
                                long newEarliest = nowRTC - RECENT_WAKEUP_PERIOD;
                                int n = 0;
                                for (WakeupEvent event : mRecentWakeups) {
                                    if (event.when > newEarliest) break;
                                    n++; // number of now-stale entries at the list head
                                }
                                for (int i = 0; i < n; i++) {
                                    mRecentWakeups.remove();
                                }
    
                                recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC);
                            }
                        }
    
                        boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
    
                        if (SystemProperties.getInt("sys.quickboot.enable", 0) == 1) {
                            filtQuickBootAlarms(triggerList);
                        }
    
                        if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
                            // if there are no wakeup alarms and the screen is off, we can
                            // delay what we have so far until the future.
                            if (mPendingNonWakeupAlarms.size() == 0) {
                                mStartCurrentDelayTime = nowELAPSED;
                                mNextNonWakeupDeliveryTime = nowELAPSED
                                        + ((currentNonWakeupFuzzLocked(nowELAPSED)*3)/2);
                            }
                            mPendingNonWakeupAlarms.addAll(triggerList);
                            mNumDelayedAlarms += triggerList.size();
                            rescheduleKernelAlarmsLocked();
                            updateNextAlarmClockLocked();
                        } else {
                            // now deliver the alarm intents; if there are pending non-wakeup
                            // alarms, we need to merge them in to the list.  note we don't
                            // just deliver them first because we generally want non-wakeup
                            // alarms delivered after wakeup alarms.
                            rescheduleKernelAlarmsLocked();
                            updateNextAlarmClockLocked();
                            if (mPendingNonWakeupAlarms.size() > 0) {
                                calculateDeliveryPriorities(mPendingNonWakeupAlarms);
                                triggerList.addAll(mPendingNonWakeupAlarms);
                                Collections.sort(triggerList, mAlarmDispatchComparator);
                                final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
                                mTotalDelayTime += thisDelayTime;
                                if (mMaxDelayTime < thisDelayTime) {
                                    mMaxDelayTime = thisDelayTime;
                                }
                                mPendingNonWakeupAlarms.clear();
                            }
                            deliverAlarmsLocked(triggerList, nowELAPSED); //alarm 事件分发
                        }
                    }
                }
            }
        }
    waitForAlarm是一个native方法具体的实现在驱动中。如果整个系统中没有alarm的时间回调,
    waitForAlarm则阻塞在这,直到有回调的时候才往后执行,这样会减少CPU的开销。
    

    com_android_server_AlarmManagerService.cpp waitForAlarm()

    int AlarmImplTimerFd::waitForAlarm()
    {
        epoll_event events[N_ANDROID_TIMERFDS];
    
        int nevents = epoll_wait(epollfd, events, N_ANDROID_TIMERFDS, -1);//监听事件
        if (nevents < 0) {
            return nevents;
        }
    
        int result = 0;
        for (int i = 0; i < nevents; i++) {
            uint32_t alarm_idx = events[i].data.u32;
            uint64_t unused;
            ssize_t err = read(fds[alarm_idx], &unused, sizeof(unused));
            if (err < 0) {
                if (alarm_idx == ANDROID_ALARM_TYPE_COUNT && errno == ECANCELED) {
                    result |= ANDROID_ALARM_TIME_CHANGE_MASK;
                } else {
                    return err;
                }
            } else {
                result |= (1 << alarm_idx);
            }
        }
    
        return result;
    }
    
    deliverAlarmsLocked() —— AlarmThread事件中分发Alarm事件
    void deliverAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED) {
            mLastAlarmDeliveryTime = nowELAPSED;
            for (int i=0; i<triggerList.size(); i++) {
                Alarm alarm = triggerList.get(i);
                try {
                    if (localLOGV) {
                        Slog.v(TAG, "sending alarm " + alarm);
                    }
                    alarm.operation.send(getContext(), 0, //调用PendingIntent的send方法
                            mBackgroundIntent.putExtra(
                                    Intent.EXTRA_ALARM_COUNT, alarm.count),
                            mResultReceiver, mHandler);
    
                    // we have an active broadcast so stay awake.
                    if (mBroadcastRefCount == 0 || !mWakeLock.isHeld()) {
                        setWakelockWorkSource(alarm.operation, alarm.workSource,
                                alarm.type, alarm.tag, true);
                        mWakeLock.acquire();
                    }
                    final InFlight inflight = new InFlight(AlarmManagerService.this,
                            alarm.operation, alarm.workSource, alarm.type, alarm.tag, alarm.uid);
                    mInFlight.add(inflight);
                    mBroadcastRefCount++;
                    mTriggeredUids.add(new Integer(alarm.uid));
    
                    final BroadcastStats bs = inflight.mBroadcastStats;
                    bs.count++;
                    if (bs.nesting == 0) {
                        bs.nesting = 1;
                        bs.startTime = nowELAPSED;
                    } else {
                        bs.nesting++;
                    }
                    final FilterStats fs = inflight.mFilterStats;
                    fs.count++;
                    if (fs.nesting == 0) {
                        fs.nesting = 1;
                        fs.startTime = nowELAPSED;
                    } else {
                        fs.nesting++;
                    }
                    if (alarm.type == ELAPSED_REALTIME_WAKEUP
                            || alarm.type == RTC_WAKEUP
                            || alarm.type == RTC_POWEROFF_WAKEUP) {
                        bs.numWakeup++;
                        fs.numWakeup++;
                        if (alarm.workSource != null && alarm.workSource.size() > 0) {
                            for (int wi=0; wi<alarm.workSource.size(); wi++) {
                                ActivityManagerNative.noteWakeupAlarm(
                                        alarm.operation, alarm.workSource.get(wi),
                                        alarm.workSource.getName(wi));
                            }
                        } else {
                            ActivityManagerNative.noteWakeupAlarm(
                                    alarm.operation, -1, null);
                        }
                    }
                } catch (PendingIntent.CanceledException e) {
                    if (alarm.repeatInterval > 0) {
                        // This IntentSender is no longer valid, but this
                        // is a repeating alarm, so toss the hoser.
                        removeImpl(alarm.operation);
                    }
                } catch (RuntimeException e) {
                    Slog.w(TAG, "Failure sending alarm.", e);
                }
            }
        }
    

    PendingIntent.java

    public void send(Context context, int code, @Nullable Intent intent,
            @Nullable OnFinished onFinished, @Nullable Handler handler,
            @Nullable String requiredPermission, @Nullable Bundle options)
            throws CanceledException {
        try {
            String resolvedType = intent != null ?
                    intent.resolveTypeIfNeeded(context.getContentResolver())
                    : null;
            int res = mTarget.send(code, intent, resolvedType, //////////mTarget 是PendingIntentRecord
                    onFinished != null
                            ? new FinishedDispatcher(this, onFinished, handler)
                            : null,
                    requiredPermission, options);
            ...
        } catch (RemoteException e) {
            throw new CanceledException(e);
        }
    }
    

    mTarget 是什么呢???
    我们前面讲述AlarmManger使用时讲述会通过PendingIntent.getActivity或者getService吧;
    PendingIntent 常用的几个静态方法:

    PendingIntent.getActivity
    PendingIntent.getService
    PendingIntent.getBroadcast
    PendingIntent.getBroadcastAsUser
    

    mTarget就是在上述方法中创建PendingIntent时创建的。上述几个方法最终都会调用ActivityManagerService的getIntentSender方法

    public static PendingIntent getService(Context context, int requestCode,
            Intent intent,  int flags) {
       String packageName = context.getPackageName();
       String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
               context.getContentResolver()) : null;
       try {
           intent.prepareToLeaveProcess();
           IIntentSender target =
               ActivityManagerNative.getDefault().getIntentSender( // ActivityManagerNative.getDefault()获取的是
    //AMS的代理ActivityMangerProxy,binder call 调用AMS的相关方法
                   ActivityManager.INTENT_SENDER_SERVICE, packageName,
                   null, null, requestCode, new Intent[] { intent },
                   resolvedType != null ? new String[] { resolvedType } : null,
                   flags, null, UserHandle.myUserId());
           return target != null ? new PendingIntent(target) : null;
       } catch (RemoteException e) {
       }
       return null;
    }
    

    ActivityMangerService.java的 getIntentSender()获取的是PendingIntentRecord对象, 而该对象继承于IIntentSender.Stub, 经过binder call回来, 所以此处target是指PendingIntentRecord对象的代理端, 即为PendingIntent.mTarget,所以上述最终会调用PendingIntentRecord的send方法;具体的唤起Activity,Service等具体业务就是在这个send 方法中完成;

    下面完整看一下AlarmManager 的set接口操作过程;

    AlarmManager.java

        public void set(int type, long triggerAtMillis, PendingIntent operation) {
            setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null, null);
        }
    
        private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
                PendingIntent operation, 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;
            }
    
            try {
                mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
                        workSource, alarmClock); //这里的mService就是前面的AlarmManagerService中的mService对象。
            } catch (RemoteException ex) {
            }
        }
        //AlarmManager 类的构造函数
        AlarmManager(IAlarmManager service, Context ctx) {
            mService = service;
    
            final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
            mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
        }
    
    

    ContextImpl.java
    AlarmManager对象的获取:(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

        @Override
        public Object getSystemService(String name) {
            ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
            return fetcher == null ? null : fetcher.getService(this);
        }   
    
     private static void registerService(String serviceName, ServiceFetcher fetcher) {
            if (!(fetcher instanceof StaticServiceFetcher)) {
                fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
            }
            SYSTEM_SERVICE_MAP.put(serviceName, fetcher); //添加service到SYSTEM_SERVICE_MAP
        }
    
     registerService(ALARM_SERVICE, new ServiceFetcher() { //注册
                      public Object createService(ContextImpl ctx) {
                          IBinder b = ServiceManager.getService(ALARM_SERVICE);
                          IAlarmManager service = IAlarmManager.Stub.asInterface(b);
                          return new AlarmManager(service, ctx);
                    }});   
    
    
        /*package*/ static class ServiceFetcher {
            int mContextCacheIndex = -1;
    
            /**
             * Main entrypoint; only override if you don't need caching.
             */
            public Object getService(ContextImpl ctx) {
                ArrayList<Object> cache = ctx.mServiceCache;
                Object service;
                synchronized (cache) {
                    if (cache.size() == 0) {
                        // Initialize the cache vector on first access.
                        // At this point sNextPerContextServiceCacheIndex
                        // is the number of potential services that are
                        // cached per-Context.
                        for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) {
                            cache.add(null);
                        }
                    } else {
                        service = cache.get(mContextCacheIndex);
                        if (service != null) {
                            return service;
                        }
                    }
                    service = createService(ctx);
                    cache.set(mContextCacheIndex, service);
                    return service;
                }
            }
    
            /**
             * Override this to create a new per-Context instance of the
             * service.  getService() will handle locking and caching.
             */
            public Object createService(ContextImpl ctx) {
                throw new RuntimeException("Not implemented");
            }
        }
            
    

    所以getSystemService中返回的就是AlarmManagerService 中mService 的代理;即AlarmManager中mService变量即是AlarmManagerService中mService的代理;AlarmManager中方法最终都是有AlarmManagerService的mService处理

    关于上述AlarmManager中set方法的处理,调用

        mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
                        workSource, alarmClock); //这里的mService就是前面的AlarmManagerService中的mService对象。
    

    Binder Call 到AlarmMangerService中
    本段代码是截取Android 5.1源码;

            @Override
            public void set(int type, long triggerAtTime, long windowLength, long interval,
                    PendingIntent operation, WorkSource workSource,
                    AlarmManager.AlarmClockInfo alarmClock) {
                if (workSource != null) {
                    getContext().enforceCallingPermission(
                            android.Manifest.permission.UPDATE_DEVICE_STATS,
                            "AlarmManager.set");
                }
    
                setImpl(type, triggerAtTime, windowLength, interval, operation,
                        windowLength == AlarmManager.WINDOW_EXACT, workSource, alarmClock);
            }
    
    
    void setImpl(int type, long triggerAtTime, long windowLength, long interval,
                PendingIntent operation, boolean isStandalone, WorkSource workSource,
                AlarmManager.AlarmClockInfo alarmClock) {
            if (operation == null) {
                Slog.w(TAG, "set/setRepeating ignored because there is no intent");
                return;
            }
    
            // Sanity check the window length.  This will catch people mistakenly
            // trying to pass an end-of-window timestamp rather than a duration.
            if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
                Slog.w(TAG, "Window length " + windowLength
                        + "ms suspiciously long; limiting to 1 hour");
                windowLength = AlarmManager.INTERVAL_HOUR;
            }
    
            // Sanity check the recurrence interval.  This will catch people who supply
            // seconds when the API expects milliseconds.
            if (interval > 0 && interval < MIN_INTERVAL) {
                Slog.w(TAG, "Suspiciously short interval " + interval
                        + " millis; expanding to " + (int)(MIN_INTERVAL/1000)
                        + " seconds");
                interval = MIN_INTERVAL;
            }
    
            if (type < RTC_WAKEUP || type > RTC_POWEROFF_WAKEUP) {
                throw new IllegalArgumentException("Invalid alarm type " + type);
            }
    
            if (triggerAtTime < 0) {
                final long who = Binder.getCallingUid();
                final long what = Binder.getCallingPid();
                Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + who
                        + " pid=" + what);
                triggerAtTime = 0;
            }
    
            final long nowElapsed = SystemClock.elapsedRealtime();
            final long nominalTrigger = convertToElapsed(triggerAtTime, type);
            // Try to prevent spamming by making sure we aren't firing alarms in the immediate future
            final long minTrigger = nowElapsed + MIN_FUTURITY;
            final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
    
            final long maxElapsed;
            if (windowLength == AlarmManager.WINDOW_EXACT) {
                maxElapsed = triggerElapsed;
            } else if (windowLength < 0) {
                maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
            } else {
                maxElapsed = triggerElapsed + windowLength;
            }
    
            final int userId = UserHandle.getCallingUserId();
    
            synchronized (mLock) {
                if (DEBUG_BATCH) {
                    Slog.v(TAG, "set(" + operation + ") : type=" + type
                            + " triggerAtTime=" + triggerAtTime + " win=" + windowLength
                            + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed
                            + " interval=" + interval + " standalone=" + isStandalone);
                }
                setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
                        interval, operation, isStandalone, true, workSource, alarmClock, userId);
            }
        }
    
    private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
                long maxWhen, long interval, PendingIntent operation, boolean isStandalone,
                boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
                int userId) {
            Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
                    operation, workSource, alarmClock, userId);
            removeLocked(operation);
    
            int whichBatch = (isStandalone) ? -1 : attemptCoalesceLocked(whenElapsed, maxWhen);
            if (whichBatch < 0) {
                Batch batch = new Batch(a);
                batch.standalone = isStandalone;
                addBatchLocked(mAlarmBatches, batch);
            } else {
                Batch batch = mAlarmBatches.get(whichBatch);
                if (batch.add(a)) {
                    // The start time of this batch advanced, so batch ordering may
                    // have just been broken.  Move it to where it now belongs.
                    mAlarmBatches.remove(whichBatch);
                    addBatchLocked(mAlarmBatches, batch);
                }
            }
    
            if (alarmClock != null) {
                mNextAlarmClockMayChange = true;
                updateNextAlarmClockLocked();
            }
    
            if (DEBUG_VALIDATE) {
                if (doValidate && !validateConsistencyLocked()) {
                    Slog.v(TAG, "Tipping-point operation: type=" + type + " when=" + when
                            + " when(hex)=" + Long.toHexString(when)
                            + " whenElapsed=" + whenElapsed + " maxWhen=" + maxWhen
                            + " interval=" + interval + " op=" + operation
                            + " standalone=" + isStandalone);
                    rebatchAllAlarmsLocked(false);
                }
            }
    
            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.
            long nextNonWakeup = 0;
            if (mAlarmBatches.size() > 0) {
                final Batch firstWakeup = findFirstWakeupBatchLocked();
                final Batch firstBatch = mAlarmBatches.get(0);
                final Batch firstRtcWakeup = findFirstRtcWakeupBatchLocked();
    
                // always update the kernel alarms, as a backstop against missed wakeups
                if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
                    mNextWakeup = firstWakeup.start;
                    setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);//调用方法
                }
                if (firstRtcWakeup != null && mNextRtcWakeup != firstRtcWakeup.start) {
                    mNextRtcWakeup = firstRtcWakeup.start;
                    long when = firstRtcWakeup.getWhenByElapsedTime(mNextRtcWakeup);
    
                    if (when != 0) {
                        setLocked(RTC_POWEROFF_WAKEUP, when);
                    }
                }
                if (firstBatch != firstWakeup) {
                    nextNonWakeup = firstBatch.start;
                }
            }
            if (mPendingNonWakeupAlarms.size() > 0) {
                if (nextNonWakeup == 0 || mNextNonWakeupDeliveryTime < nextNonWakeup) {
                    nextNonWakeup = mNextNonWakeupDeliveryTime;
                }
            }
            // always update the kernel alarm, as a backstop against missed wakeups
            if (nextNonWakeup != 0) {
                mNextNonWakeup = nextNonWakeup;
                setLocked(ELAPSED_REALTIME, nextNonWakeup); //
            }
        }
    
    private void setLocked(int type, long when) {
            if (mNativeData != 0) {
                // The kernel never triggers alarms with negative wakeup times
                // so we ensure they are positive.
                long alarmSeconds, alarmNanoseconds;
                if (when < 0) {
                    alarmSeconds = 0;
                    alarmNanoseconds = 0;
                } else {
                    alarmSeconds = when / 1000;
                    alarmNanoseconds = (when % 1000) * 1000 * 1000;
                }
                
                set(mNativeData, type, alarmSeconds, alarmNanoseconds); //native  JNI call,调用JN native 方法
            } else {
                Message msg = Message.obtain();
                msg.what = ALARM_EVENT;
                
                mHandler.removeMessages(ALARM_EVENT);
                mHandler.sendMessageAtTime(msg, when);
            }
        }
    

    com_android_server_AlarmManagerService.cpp

    static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type,
     jlong seconds, jlong nanoseconds)
    {
        AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData); //从java传递的nativeData,
        //AlarmMangerService中的nativeData 实际是该JNI init() 的返回值;此时返回的是AlarmImplAlarmDriver
        struct timespec ts;
        ts.tv_sec = seconds;
        ts.tv_nsec = nanoseconds;
    
        int result = impl->set(type, &ts);//AlarmImplAlarmDriver->set
        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));
        }
    }
    
    static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject)
    {
        jlong ret = init_alarm_driver();//即操作Alarm device——"/dev/alarm"
        if (ret) {
            return ret; 
        }
    
        return init_timerfd();//如果没有Alarm device,继续执行
    }
    
    int AlarmImplAlarmDriver::set(int type, struct timespec *ts)
    {
        return ioctl(fds[0], ANDROID_ALARM_SET(type), ts);// 最终ioctl操作硬件
    }
    

    最终通过ioctl的方式将时间设置给驱动,后续驱动不做详解,我也不清楚,
    目的就是把时间设置给驱动,等到硬件中断返回后,再回调到native层,native再回调到framework,
    如何回调到framework的呢??
    我们上述说的AlarmThread,在run()方法中开启一个无限循环,循环中会调用

    int result = waitForAlarm(mNativeData); //JNI 方法,使用EPOLL监听FD,阻塞等待驱动返回执行下面的分发执行;
    

    waitForAlarm是一个native方法,具体的实现在驱动中。如果整个系统中没有alarm的时间回调,waitForAlarm则阻塞在这,直到有回调的时候才往后执行,才会继续执行上述的deliverAlarmsLocked()方法,才会去执行相应的send方法唤醒activity等。

    一个完整的过程就是操作"/dev/alarm"设备的过程。自此一个完整的操作过程就结束了;

    相关文章

      网友评论

        本文标题:AlarmManager详解

        本文链接:https://www.haomeiwen.com/subject/etpelxtx.html