gecko/mobile/android/base/background/announcements/AnnouncementsBroadcastService.java
Nick Alexander 80b57618b7 Bug 844347 - Factor logging code that is not Sync-specific out of org.mozilla.gecko.sync. r=rnewman
--HG--
rename : mobile/android/base/sync/GlobalConstants.java.in => mobile/android/base/background/common/GlobalConstants.java.in
rename : mobile/android/base/sync/Logger.java => mobile/android/base/background/common/log/Logger.java
rename : mobile/android/base/sync/log/writers/AndroidLevelCachingLogWriter.java => mobile/android/base/background/common/log/writers/AndroidLevelCachingLogWriter.java
rename : mobile/android/base/sync/log/writers/AndroidLogWriter.java => mobile/android/base/background/common/log/writers/AndroidLogWriter.java
rename : mobile/android/base/sync/log/writers/LevelFilteringLogWriter.java => mobile/android/base/background/common/log/writers/LevelFilteringLogWriter.java
rename : mobile/android/base/sync/log/writers/LogWriter.java => mobile/android/base/background/common/log/writers/LogWriter.java
rename : mobile/android/base/sync/log/writers/PrintLogWriter.java => mobile/android/base/background/common/log/writers/PrintLogWriter.java
rename : mobile/android/base/sync/log/writers/SimpleTagLogWriter.java => mobile/android/base/background/common/log/writers/SimpleTagLogWriter.java
rename : mobile/android/base/sync/log/writers/StringLogWriter.java => mobile/android/base/background/common/log/writers/StringLogWriter.java
rename : mobile/android/base/sync/log/writers/TagLogWriter.java => mobile/android/base/background/common/log/writers/TagLogWriter.java
rename : mobile/android/base/sync/log/writers/ThreadLocalTagLogWriter.java => mobile/android/base/background/common/log/writers/ThreadLocalTagLogWriter.java
2013-02-27 15:44:21 -08:00

193 lines
8.1 KiB
Java

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.background.announcements;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.mozilla.gecko.background.BackgroundService;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
/**
* A service which listens to broadcast intents from the system and from the
* browser, registering or unregistering the main
* {@link AnnouncementsStartReceiver} with the {@link AlarmManager}.
*/
public class AnnouncementsBroadcastService extends BackgroundService {
private static final String WORKER_THREAD_NAME = "AnnouncementsBroadcastServiceWorker";
private static final String LOG_TAG = "AnnounceBrSvc";
public AnnouncementsBroadcastService() {
super(WORKER_THREAD_NAME);
}
private void toggleAlarm(final Context context, boolean enabled) {
Logger.info(LOG_TAG, (enabled ? "R" : "Unr") + "egistering announcements broadcast receiver...");
final PendingIntent pending = createPendingIntent(context, AnnouncementsStartReceiver.class);
if (!enabled) {
cancelAlarm(pending);
return;
}
final long pollInterval = getPollInterval(context);
scheduleAlarm(pollInterval, pending);
}
/**
* Record the last launch time of our version of Fennec.
*
* @param context
* the <code>Context</code> to use to gain access to
* <code>SharedPreferences</code>.
*/
public static void recordLastLaunch(final Context context) {
final long now = System.currentTimeMillis();
final SharedPreferences preferences = context.getSharedPreferences(AnnouncementsConstants.PREFS_BRANCH, GlobalConstants.SHARED_PREFERENCES_MODE);
// One of several things might be true, according to our logs:
//
// * The new current time is older than the last
// * … or way in the future
// * … or way in the distant past
// * … or it's reasonable.
//
// Furthermore, when we come to calculate idle we might find that the clock
// is dramatically different — that the current time is thirteen years older
// than our saved timestamp (system clock resets to 2000 on battery change),
// or it's thirty years in the future (previous timestamp was saved as 0).
//
// We should try to do something vaguely sane in these situations.
long previous = preferences.getLong(AnnouncementsConstants.PREF_LAST_LAUNCH, -1);
if (previous == -1) {
Logger.debug(LOG_TAG, "No previous launch recorded.");
}
if (now < GlobalConstants.BUILD_TIMESTAMP) {
Logger.warn(LOG_TAG, "Current time " + now + " is older than build date " +
GlobalConstants.BUILD_TIMESTAMP + ". Ignoring until clock is corrected.");
return;
}
if (now > AnnouncementsConstants.LATEST_ACCEPTED_LAUNCH_TIMESTAMP) {
Logger.warn(LOG_TAG, "Launch time " + now + " is later than max sane launch timestamp " +
AnnouncementsConstants.LATEST_ACCEPTED_LAUNCH_TIMESTAMP +
". Ignoring until clock is corrected.");
return;
}
if (previous > now) {
Logger.debug(LOG_TAG, "Previous launch " + previous + " later than current time " +
now + ", but new time is sane. Accepting new time.");
}
preferences.edit().putLong(AnnouncementsConstants.PREF_LAST_LAUNCH, now).commit();
}
public static long getPollInterval(final Context context) {
SharedPreferences preferences = context.getSharedPreferences(AnnouncementsConstants.PREFS_BRANCH, GlobalConstants.SHARED_PREFERENCES_MODE);
return preferences.getLong(AnnouncementsConstants.PREF_ANNOUNCE_FETCH_INTERVAL_MSEC, AnnouncementsConstants.DEFAULT_ANNOUNCE_FETCH_INTERVAL_MSEC);
}
public static void setPollInterval(final Context context, long interval) {
SharedPreferences preferences = context.getSharedPreferences(AnnouncementsConstants.PREFS_BRANCH, GlobalConstants.SHARED_PREFERENCES_MODE);
preferences.edit().putLong(AnnouncementsConstants.PREF_ANNOUNCE_FETCH_INTERVAL_MSEC, interval).commit();
}
@Override
protected void onHandleIntent(Intent intent) {
Logger.setThreadLogTag(AnnouncementsConstants.GLOBAL_LOG_TAG);
final String action = intent.getAction();
Logger.debug(LOG_TAG, "Broadcast onReceive. Intent is " + action);
if (AnnouncementsConstants.ACTION_ANNOUNCEMENTS_PREF.equals(action)) {
handlePrefIntent(intent);
return;
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action) ||
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
handleSystemLifetimeIntent();
return;
}
// Failure case.
Logger.warn(LOG_TAG, "Unknown intent " + action);
}
/**
* Handle one of the system intents to which we listen to launch our service
* without the browser being opened.
*
* To avoid tight coupling to Fennec, we use reflection to find
* <code>GeckoPreferences</code>, invoking the same code path that
* <code>GeckoApp</code> uses on startup to send the <i>other</i>
* notification to which we listen.
*
* All of this is neatly wrapped in <code>try…catch</code>, so this code
* will run safely without a Firefox build installed.
*/
protected void handleSystemLifetimeIntent() {
// Ask the browser to tell us the current state of the preference.
try {
Class<?> geckoPreferences = Class.forName(GlobalConstants.GECKO_PREFERENCES_CLASS);
Method broadcastSnippetsPref = geckoPreferences.getMethod(GlobalConstants.GECKO_BROADCAST_METHOD, Context.class);
broadcastSnippetsPref.invoke(null, this);
return;
} catch (ClassNotFoundException e) {
Logger.error(LOG_TAG, "Class " + GlobalConstants.GECKO_PREFERENCES_CLASS + " not found!");
return;
} catch (NoSuchMethodException e) {
Logger.error(LOG_TAG, "Method " + GlobalConstants.GECKO_PREFERENCES_CLASS + "/" + GlobalConstants.GECKO_BROADCAST_METHOD + " not found!");
return;
} catch (IllegalArgumentException e) {
Logger.error(LOG_TAG, "Got exception invoking " + GlobalConstants.GECKO_BROADCAST_METHOD + ".");
} catch (IllegalAccessException e) {
Logger.error(LOG_TAG, "Got exception invoking " + GlobalConstants.GECKO_BROADCAST_METHOD + ".");
} catch (InvocationTargetException e) {
Logger.error(LOG_TAG, "Got exception invoking " + GlobalConstants.GECKO_BROADCAST_METHOD + ".");
}
}
/**
* Handle the intent sent by the browser when it wishes to notify us
* of the value of the user preference. Look at the value and toggle the
* alarm service accordingly.
*/
protected void handlePrefIntent(Intent intent) {
if (!intent.hasExtra("enabled")) {
Logger.warn(LOG_TAG, "Got ANNOUNCEMENTS_PREF intent without enabled. Ignoring.");
return;
}
final boolean enabled = intent.getBooleanExtra("enabled", true);
Logger.debug(LOG_TAG, intent.getStringExtra("branch") + "/" +
intent.getStringExtra("pref") + " = " +
(intent.hasExtra("enabled") ? enabled : ""));
toggleAlarm(this, enabled);
// Primarily intended for debugging and testing, but this doesn't do any harm.
if (!enabled) {
Logger.info(LOG_TAG, "!enabled: clearing last fetch.");
final SharedPreferences sharedPreferences = this.getSharedPreferences(AnnouncementsConstants.PREFS_BRANCH,
GlobalConstants.SHARED_PREFERENCES_MODE);
final Editor editor = sharedPreferences.edit();
editor.remove(AnnouncementsConstants.PREF_LAST_FETCH_LOCAL_TIME);
editor.remove(AnnouncementsConstants.PREF_EARLIEST_NEXT_ANNOUNCE_FETCH);
editor.commit();
}
}
}