Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

DO NOT MERGE - Move scheduled alarm scrub off main thread

We currently do a database update when the "boot completed" intent
is received.  Doing it on the main thread appears to be causing ANRs,
so this moves it to a background thread.  An "empty" service is
used to discourage the system from killing the provider while this
is in progress.

Also, removed an unused field from CalendarAlarmManager.

Bug 5262151

(cherry-pick from master)

Change-Id: Ic3a3da986b5c8f271a6310e56f61839897ba8336
  • Loading branch information...
commit 5503ef10b15a441730f02e5a2cbd41328a424984 1 parent 07a7d5c
Andy McFadden fadden authored
3  AndroidManifest.xml
@@ -50,6 +50,9 @@
50 50 <!-- This is used to keep the provider alive long enough to send update
51 51 intent broadcasts. -->
52 52 <service android:name=".EmptyService" />
  53 + <!-- This is used to keep the provider alive long enough to clean up scheduled
  54 + alarms after boot. -->
  55 + <service android:name=".CalendarReceiver$RemoveScheduledAlarmsEmptyService" />
53 56
54 57 <activity android:name="CalendarContentProviderTests" android:label="Calendar Content Provider"
55 58 android:exported="false">
10 src/com/android/providers/calendar/CalendarAlarmManager.java
@@ -112,12 +112,6 @@
112 112 @VisibleForTesting
113 113 protected AtomicBoolean mNextAlarmCheckScheduled;
114 114 /**
115   - * Used for tracking if current alarms should be removed when recalculating
116   - * new ones.
117   - */
118   - @VisibleForTesting
119   - protected AtomicBoolean mNeedRemoveAlarms;
120   - /**
121 115 * Used for synchronization
122 116 */
123 117 @VisibleForTesting
@@ -139,14 +133,10 @@ protected void initializeWithContext(Context context) {
139 133 mContext = context;
140 134 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
141 135 mNextAlarmCheckScheduled = new AtomicBoolean(false);
142   - mNeedRemoveAlarms = new AtomicBoolean(false);
143 136 mAlarmLock = new Object();
144 137 }
145 138
146 139 void scheduleNextAlarm(boolean removeAlarms) {
147   - // We aggregate first the "remove alarm flag". Whenever it is to true,
148   - // it will be sticky
149   - mNeedRemoveAlarms.set(mNeedRemoveAlarms.get() || removeAlarms);
150 140 if (!mNextAlarmCheckScheduled.getAndSet(true)) {
151 141 if (Log.isLoggable(CalendarProvider2.TAG, Log.DEBUG)) {
152 142 Log.d(CalendarProvider2.TAG, "Scheduling check of next Alarm");
66 src/com/android/providers/calendar/CalendarReceiver.java
@@ -16,10 +16,13 @@
16 16
17 17 package com.android.providers.calendar;
18 18
  19 +import android.app.Service;
19 20 import android.content.BroadcastReceiver;
20 21 import android.content.ContentResolver;
21 22 import android.content.Context;
22 23 import android.content.Intent;
  24 +import android.os.IBinder;
  25 +import android.util.Log;
23 26
24 27 /**
25 28 * This IntentReceiver executes when the boot completes and ensures that
@@ -29,6 +32,7 @@
29 32 * yet.
30 33 */
31 34 public class CalendarReceiver extends BroadcastReceiver {
  35 + private static final String TAG = "CalendarReceiver";
32 36
33 37 static final String SCHEDULE = "com.android.providers.calendar.SCHEDULE_ALARM";
34 38
@@ -40,14 +44,62 @@ public void onReceive(Context context, Intent intent) {
40 44 cr.update(CalendarAlarmManager.SCHEDULE_ALARM_URI, null /* values */, null /* where */,
41 45 null /* selectionArgs */);
42 46 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
43   - // Remove alarms from the CalendarAlerts table that have been marked
44   - // as "scheduled" but not fired yet. We do this because the
45   - // AlarmManagerService loses all information about alarms when the
46   - // power turns off but we store the information in a database table
47   - // that persists across reboots. See the documentation for
48   - // scheduleNextAlarmLocked() for more information.
49   - cr.update(CalendarAlarmManager.SCHEDULE_ALARM_REMOVE_URI, null /* values */,
  47 + removeScheduledAlarms(context, cr);
  48 + }
  49 + }
  50 +
  51 + /*
  52 + * Remove alarms from the CalendarAlerts table that have been marked
  53 + * as "scheduled" but not fired yet. We do this because the
  54 + * AlarmManagerService loses all information about alarms when the
  55 + * power turns off but we store the information in a database table
  56 + * that persists across reboots. See the documentation for
  57 + * scheduleNextAlarmLocked() for more information.
  58 + *
  59 + * Running this on the main thread has caused ANRs, so we run it on a background
  60 + * thread and start an "empty service" to encourage the system to keep us alive.
  61 + *
  62 + * We don't expect this to be called more than once. If it were, we would have to
  63 + * worry about serializing the use of the service.
  64 + */
  65 + private void removeScheduledAlarms(Context context, ContentResolver resolver) {
  66 + context.startService(new Intent(context, RemoveScheduledAlarmsEmptyService.class));
  67 +
  68 + RemoveScheduledAlarmsThread thread = new RemoveScheduledAlarmsThread(context, resolver);
  69 + thread.start();
  70 + }
  71 +
  72 + /**
  73 + * Background thread that handles cleanup of scheduled alarms.
  74 + */
  75 + private static class RemoveScheduledAlarmsThread extends Thread {
  76 + private Context mContext;
  77 + private ContentResolver mResolver;
  78 +
  79 + RemoveScheduledAlarmsThread(Context context, ContentResolver resolver) {
  80 + mContext = context;
  81 + mResolver = resolver;
  82 + }
  83 +
  84 + @Override
  85 + public void run() {
  86 + if (Log.isLoggable(TAG, Log.DEBUG)) {
  87 + Log.d(TAG, "Removing scheduled alarms");
  88 + }
  89 + mResolver.update(CalendarAlarmManager.SCHEDULE_ALARM_REMOVE_URI, null /* values */,
50 90 null /* where */, null /* selectionArgs */);
  91 + mContext.stopService(new Intent(mContext, RemoveScheduledAlarmsEmptyService.class));
  92 + }
  93 + }
  94 +
  95 + /**
  96 + * Background {@link Service} that is used to keep our process alive long enough
  97 + * for background threads to finish. Used for cleanup of scheduled alarms.
  98 + */
  99 + public static class RemoveScheduledAlarmsEmptyService extends Service {
  100 + @Override
  101 + public IBinder onBind(Intent intent) {
  102 + return null;
51 103 }
52 104 }
53 105 }
1  tests/src/com/android/providers/calendar/CalendarProvider2ForTesting.java
@@ -49,7 +49,6 @@ public MockCalendarAlarmManager(Context context) {
49 49 protected void initializeWithContext(Context context) {
50 50 mContext = context;
51 51 mNextAlarmCheckScheduled = new AtomicBoolean(false);
52   - mNeedRemoveAlarms = new AtomicBoolean(false);
53 52 mAlarmLock = new Object();
54 53 }
55 54

0 comments on commit 5503ef1

Please sign in to comment.
Something went wrong with that request. Please try again.