Remove BG service from calendar provider.

The purpose of EmptyService was to keep the process running until
it sends a broadcast with a delay which is scheduled with a handler...

However "sending a broadcast with a delay" can just be done

Bug 35350114

Test: Manual tests with the verbose log enabled.
- adb shell setprop log.tag.CalendarProvider2 VERBOSE &&
    adb shell am force-stop com.android.providers.calendar
- Then create/delete events on the calendar app and monitor
adb logcat | grep 'sendUpdateNotification'
- Then check the alarm and make sure PROVIDER_CHANGED is scheduled
at the right time.
adb shell dumpsys alarm | grep -A 4 -P 'ELAPSED.*com.android.providers.calendar'

Change-Id: Id2c0e10604bb7fdd3e9f8650295da83b3a62ecff
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1d88eb9..255f39a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -54,13 +54,6 @@
                 android:readPermission="android.permission.READ_CALENDAR"
                 android:writePermission="android.permission.WRITE_CALENDAR" />
 
-        <!-- This is used to keep the provider alive long enough to send update
-             intent broadcasts. -->
-        <service android:name=".EmptyService" />
-        <!-- This is used to keep the provider alive long enough to clean up scheduled
-             alarms after boot. -->
-        <service android:name=".CalendarReceiver$RemoveScheduledAlarmsEmptyService" />
-
         <activity android:name="CalendarContentProviderTests" android:label="Calendar Content Provider"
                 android:exported="false">
             <intent-filter>
diff --git a/src/com/android/providers/calendar/CalendarAlarmManager.java b/src/com/android/providers/calendar/CalendarAlarmManager.java
index adb98c4..b107bf7 100644
--- a/src/com/android/providers/calendar/CalendarAlarmManager.java
+++ b/src/com/android/providers/calendar/CalendarAlarmManager.java
@@ -499,6 +499,10 @@
                 + CalendarAlerts.STATE_SCHEDULED, null /* whereArgs */);
     }
 
+    public void set(int type, long triggerAtTime, PendingIntent operation) {
+        mAlarmManager.set(type, triggerAtTime, operation);
+    }
+
     public void setExact(int type, long triggerAtTime, PendingIntent operation) {
         mAlarmManager.setExact(type, triggerAtTime, operation);
     }
diff --git a/src/com/android/providers/calendar/CalendarProvider2.java b/src/com/android/providers/calendar/CalendarProvider2.java
index a85c45b..08fe756 100644
--- a/src/com/android/providers/calendar/CalendarProvider2.java
+++ b/src/com/android/providers/calendar/CalendarProvider2.java
@@ -20,7 +20,9 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
+import android.app.AlarmManager;
 import android.app.AppOpsManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -40,6 +42,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.Process;
+import android.os.SystemClock;
 import android.provider.BaseColumns;
 import android.provider.CalendarContract;
 import android.provider.CalendarContract.Attendees;
@@ -446,23 +449,6 @@
     @VisibleForTesting
     protected CalendarAlarmManager mCalendarAlarm;
 
-    private final Handler mBroadcastHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            Context context = CalendarProvider2.this.mContext;
-            if (msg.what == UPDATE_BROADCAST_MSG) {
-                // Broadcast a provider changed intent
-                doSendUpdateNotification();
-                // Because the handler does not guarantee message delivery in
-                // the case that the provider is killed, we need to make sure
-                // that the provider stays alive long enough to deliver the
-                // notification. This empty service is sufficient to "wedge" the
-                // process until we stop it here.
-                context.stopService(new Intent(context, EmptyService.class));
-            }
-        }
-    };
-
     /**
      * Listens for timezone changes and disk-no-longer-full events
      */
@@ -4537,11 +4523,9 @@
     }
 
     /**
-     * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent.
+     * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent with a delay.
      * This also provides a timeout, so any calls to this method will be batched
-     * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class.  The
-     * actual sending of the intent is done in
-     * {@link #doSendUpdateNotification()}.
+     * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class.
      *
      * TODO add support for eventId
      *
@@ -4550,47 +4534,24 @@
      */
     private void sendUpdateNotification(long eventId,
             boolean callerIsSyncAdapter) {
-        // Are there any pending broadcast requests?
-        if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) {
-            // Delete any pending requests, before requeuing a fresh one
-            mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG);
-        } else {
-            // Because the handler does not guarantee message delivery in
-            // the case that the provider is killed, we need to make sure
-            // that the provider stays alive long enough to deliver the
-            // notification. This empty service is sufficient to "wedge" the
-            // process until we stop it here.
-            mContext.startService(new Intent(mContext, EmptyService.class));
-        }
         // We use a much longer delay for sync-related updates, to prevent any
         // receivers from slowing down the sync
-        long delay = callerIsSyncAdapter ?
+        final long delay = callerIsSyncAdapter ?
                 SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS :
                 UPDATE_BROADCAST_TIMEOUT_MILLIS;
-        // Despite the fact that we actually only ever use one message at a time
-        // for now, it is really important to call obtainMessage() to get a
-        // clean instance.  This avoids potentially infinite loops resulting
-        // adding the same instance to the message queue twice, since the
-        // message queue implements its linked list using a field from Message.
-        Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG);
-        mBroadcastHandler.sendMessageDelayed(msg, delay);
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "sendUpdateNotification: delay=" + delay);
+        }
+
+        mCalendarAlarm.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delay,
+                PendingIntent.getBroadcast(mContext, 0, createProviderChangedBroadcast(),
+                        PendingIntent.FLAG_UPDATE_CURRENT));
     }
 
-    /**
-     * This method should not ever be called directly, to prevent sending too
-     * many potentially expensive broadcasts.  Instead, call
-     * {@link #sendUpdateNotification(boolean)} instead.
-     *
-     * @see #sendUpdateNotification(boolean)
-     */
-    private void doSendUpdateNotification() {
-        Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED,
-                CalendarContract.CONTENT_URI);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        if (Log.isLoggable(TAG, Log.INFO)) {
-            Log.i(TAG, "Sending notification intent: " + intent);
-        }
-        mContext.sendBroadcast(intent, null);
+    private Intent createProviderChangedBroadcast() {
+        return new Intent(Intent.ACTION_PROVIDER_CHANGED, CalendarContract.CONTENT_URI)
+                .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
     }
 
     private static final int TRANSACTION_QUERY = 0;
diff --git a/src/com/android/providers/calendar/EmptyService.java b/src/com/android/providers/calendar/EmptyService.java
deleted file mode 100644
index 70cf3d4..0000000
--- a/src/com/android/providers/calendar/EmptyService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.providers.calendar;
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-/**
- * Background {@link Service} that is used to keep our process alive long enough
- * for background threads to finish. Started and stopped directly by specific
- * background tasks when needed.
- */
-public class EmptyService extends Service {
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-}
diff --git a/tests/src/com/android/providers/calendar/CalendarProvider2ForTesting.java b/tests/src/com/android/providers/calendar/CalendarProvider2ForTesting.java
index e54f5bd..f73168e 100644
--- a/tests/src/com/android/providers/calendar/CalendarProvider2ForTesting.java
+++ b/tests/src/com/android/providers/calendar/CalendarProvider2ForTesting.java
@@ -58,6 +58,10 @@
         }
 
         @Override
+        public void set(int type, long triggerAtTime, PendingIntent operation) {
+        }
+
+        @Override
         public void setExact(int type, long triggerAtTime, PendingIntent operation) {
         }