四种和时间相关的广播

1
2
3
4
5
6
1. ACTION_TIME_TICK = "android.intent.action.TIME_TICK"
2. ACTION_TIME_CHANGED = "android.intent.action.TIME_SET"
3. ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED"
4. ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED"

处理逻辑都集中在AlarmManagerService.java里面,下文代码默认此文件

ACTION_TIME_TICK和ACTION_DATE_CHANGED

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
ACTION_TIME_TICK      每分钟发送一次
ACTION_DATE_CHANGED   日期发生改变时发送

public void onStart() {

    ... ...
    # 构造TimeTick广播发送对象
    mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
            new Intent(Intent.ACTION_TIME_TICK).addFlags(
                    Intent.FLAG_RECEIVER_REGISTERED_ONLY
                    | Intent.FLAG_RECEIVER_FOREGROUND), 0,
                    UserHandle.ALL);

    # 构造DateChanged广播发送对象
    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);
    ... ...
}

class ClockReceiver extends BroadcastReceiver {
    public ClockReceiver() {
        # TimeTick, DateChanged广播是循环的,处理一次后需要继续监听
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_TIME_TICK);
        filter.addAction(Intent.ACTION_DATE_CHANGED);
        getContext().registerReceiver(this, filter);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
            if (DEBUG_BATCH) {
                Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
            }
            # 调度TimeTick广播
            scheduleTimeTickEvent();
        } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
            // Since the kernel does not keep track of DST, we need to
            // reset the TZ information at the beginning of each day
            // based off of the current Zone gmt offset + userspace tracked
            // daylight savings information.
            TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
            int gmtOffset = zone.getOffset(System.currentTimeMillis());
            setKernelTimezone(mNativeData, -(gmtOffset / 60000));
            #调度DateChanged广播
            scheduleDateChangedEvent();
        }
    }

    public void scheduleTimeTickEvent() {
        final long currentTime = System.currentTimeMillis();
        final long nextTime = 60000 * ((currentTime / 60000) + 1);

        // Schedule this event for the amount of time that it would take to get to
        // the top of the next minute.
        final long tickEventDelay = nextTime - currentTime;

        final WorkSource workSource = null; // Let system take blame for time tick events.
        # 发送TimeTick广播
        setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
                0, mTimeTickSender, true, workSource, null);
    }

    public void scheduleDateChangedEvent() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DAY_OF_MONTH, 1);

        final WorkSource workSource = null; // Let system take blame for date change events.
        # 发送DateChanged广播,注意这里需要calendar的支持,否则该广播无法发出
        setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, true, workSource,
                null);
    }
}

ACTION_TIME_CHANGED

1
设置时间的时候发送

public void run() { ArrayList triggerList = new ArrayList();

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
        while (true)
        {
            int result = waitForAlarm(mNativeData);

            triggerList.clear();

            #检测内核TIME_CHANGED_MASK标记,有的话说明系统时间发生过设置,可以是setting或者toolbox触发的
            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);
                getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
            }
            ... ...
        }
}

ACTION_TIMEZONE_CHANGED

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
时区改变时发送

public void onStart() {
    mNativeData = init();
    mNextWakeup = mNextNonWakeup = 0;

    // We have to set current TimeZone info to kernel
    // because kernel doesn't keep this after reboot
    # 根据时区property设置时区信息
    setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));

    ... ...
}

void setTimeZoneImpl(String tz) {
        if (TextUtils.isEmpty(tz)) {
            return;
        }

    TimeZone zone = TimeZone.getTimeZone(tz);
    // Prevent reentrant calls from stepping on each other when writing
    // the time zone property
    boolean timeZoneWasChanged = false;
    synchronized (this) {
        String current = SystemProperties.get(TIMEZONE_PROPERTY);
        if (current == null || !current.equals(zone.getID())) {
            if (localLOGV) {
                Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
            }
            timeZoneWasChanged = true;
            SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
        }

        // Update the kernel timezone information
        // Kernel tracks time offsets as 'minutes west of GMT'
        int gmtOffset = zone.getOffset(System.currentTimeMillis());
        setKernelTimezone(mNativeData, -(gmtOffset / 60000));
    }

    TimeZone.setDefault(null);

    if (timeZoneWasChanged) {
        # 发送TimezoneChanged广播
        Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        intent.putExtra("time-zone", zone.getID());
        getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
    }
}

# 对外接口设置时区
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);
        }
    }