Add a PRE_BOOT_COMPLETED receiver.

It runs the first time after an OTA and either creates
the database or runs the upgrade path on it. This may
take a significatn amount of time so it's done before
the boot animation completes.

Bug: 2713849
Change-Id: Ie7ac396bd82577cd421daa102ad05c0abf55b6f0
diff --git a/Android.mk b/Android.mk
index 3dea6da..af7aa95 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,6 +21,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := guava
 
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_SRC_FILES += \
+        src/com/android/providers/calendar/EventLogTags.logtags
 
 LOCAL_PACKAGE_NAME := CalendarProvider
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 361ae6d..d9926c2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -85,6 +85,15 @@
             </intent-filter>
        </receiver>
 
+       <!-- Handles database upgrades after OTAs, then disables itself -->
+       <receiver android:name="CalendarUpgradeReceiver">
+           <!-- This broadcast is sent after the core system has finished
+                booting, before the home app is launched or BOOT_COMPLETED
+                is sent. -->
+           <intent-filter>
+               <action android:name="android.intent.action.PRE_BOOT_COMPLETED"/>
+           </intent-filter>
+       </receiver>
 
     </application>
 </manifest>
diff --git a/src/com/android/providers/calendar/CalendarDatabaseHelper.java b/src/com/android/providers/calendar/CalendarDatabaseHelper.java
index 9ba72f7..b38831b 100644
--- a/src/com/android/providers/calendar/CalendarDatabaseHelper.java
+++ b/src/com/android/providers/calendar/CalendarDatabaseHelper.java
@@ -58,7 +58,7 @@
 
     // Note: if you update the version number, you must also update the code
     // in upgradeDatabase() to modify the database (gracefully, if possible).
-    private static final int DATABASE_VERSION = 69;
+    static final int DATABASE_VERSION = 69;
 
     private static final int PRE_FROYO_SYNC_STATE_VERSION = 3;
 
diff --git a/src/com/android/providers/calendar/CalendarUpgradeReceiver.java b/src/com/android/providers/calendar/CalendarUpgradeReceiver.java
new file mode 100644
index 0000000..30ac58c
--- /dev/null
+++ b/src/com/android/providers/calendar/CalendarUpgradeReceiver.java
@@ -0,0 +1,83 @@
+/*
+ * 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
+ */
+
+package com.android.providers.calendar;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.util.EventLog;
+import android.util.Log;
+
+/**
+ * This will be launched during system boot, after the core system has
+ * been brought up but before any non-persistent processes have been
+ * started.  It is launched in a special state, with no content provider
+ * or custom application class associated with the process running.
+ *
+ * It's job is to prime the calendar database. Either create it
+ * if it doesn't exist, or open it and force any necessary upgrades.
+ * All of this heavy lifting happens before the boot animation ends.
+ */
+public class CalendarUpgradeReceiver extends BroadcastReceiver {
+    static final String TAG = "CalendarUpgradeReceiver";
+    static final String PREF_DB_VERSION = "db_version";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        // We are now running with the system up, but no apps started,
+        // so can do whatever cleanup after an upgrade that we want.
+
+        try {
+            long startTime = System.currentTimeMillis();
+
+            // Lookup the last known database version
+            SharedPreferences prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE);
+            int prefVersion = prefs.getInt(PREF_DB_VERSION, 0);
+
+            // If the version is old go ahead and attempt to create or upgrade the database.
+            if (prefVersion != CalendarDatabaseHelper.DATABASE_VERSION) {
+                // Store the current version so this receiver isn't run again until the database
+                // version number changes. This is intentionally done even before the upgrade path
+                // is attempted to be conservative. If the upgrade fails for some reason and we
+                // crash and burn we don't want to get into a loop doing so.
+                prefs.edit().putInt(PREF_DB_VERSION, CalendarDatabaseHelper.DATABASE_VERSION).commit();
+
+                // Ask for a reference to the database to force the helper to either
+                // create the database or open it up, performing any necessary upgrades
+                // in the process.
+                Log.i(TAG, "Creating or opening calendar database");
+                CalendarDatabaseHelper helper = CalendarDatabaseHelper.getInstance(context);
+                helper.getWritableDatabase();
+                helper.close();
+
+                // Log the total time taken for the receiver to perform the operation
+                EventLogTags.writeCalendarUpgradeReceiver(System.currentTimeMillis() - startTime);
+            }
+        } catch (Throwable t) {
+            // Something has gone terribly wrong. Disable this receiver for good so we can't
+            // possibly end up in a reboot loop.
+            Log.wtf(TAG, "Error during upgrade attempt. Disabling receiver.", t);
+            context.getPackageManager().setComponentEnabledSetting(
+                    new ComponentName(context, getClass()),
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP);
+        }
+    }
+}
diff --git a/src/com/android/providers/calendar/EventLogTags.logtags b/src/com/android/providers/calendar/EventLogTags.logtags
new file mode 100644
index 0000000..621b3c7
--- /dev/null
+++ b/src/com/android/providers/calendar/EventLogTags.logtags
@@ -0,0 +1,5 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.providers.calendar;
+
+4000 calendar_upgrade_receiver (time|2|3)