AlarmManager详解

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

项目需求: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"设备的过程。自此一个完整的操作过程就结束了;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容