1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/background/announcements/AnnouncementsBroadcastService.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,179 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.background.announcements; 1.9 + 1.10 +import org.mozilla.gecko.background.BackgroundService; 1.11 +import org.mozilla.gecko.background.common.GlobalConstants; 1.12 +import org.mozilla.gecko.background.common.log.Logger; 1.13 + 1.14 +import android.app.AlarmManager; 1.15 +import android.app.PendingIntent; 1.16 +import android.content.Context; 1.17 +import android.content.Intent; 1.18 +import android.content.SharedPreferences; 1.19 +import android.content.SharedPreferences.Editor; 1.20 + 1.21 +/** 1.22 + * A service which listens to broadcast intents from the system and from the 1.23 + * browser, registering or unregistering the main 1.24 + * {@link AnnouncementsService} with the {@link AlarmManager}. 1.25 + */ 1.26 +public class AnnouncementsBroadcastService extends BackgroundService { 1.27 + private static final String WORKER_THREAD_NAME = "AnnouncementsBroadcastServiceWorker"; 1.28 + private static final String LOG_TAG = "AnnounceBrSvc"; 1.29 + 1.30 + public AnnouncementsBroadcastService() { 1.31 + super(WORKER_THREAD_NAME); 1.32 + } 1.33 + 1.34 + protected static SharedPreferences getSharedPreferences(Context context) { 1.35 + return context.getSharedPreferences(AnnouncementsConstants.PREFS_BRANCH, 1.36 + GlobalConstants.SHARED_PREFERENCES_MODE); 1.37 + } 1.38 + 1.39 + protected SharedPreferences getSharedPreferences() { 1.40 + return this.getSharedPreferences(AnnouncementsConstants.PREFS_BRANCH, 1.41 + GlobalConstants.SHARED_PREFERENCES_MODE); 1.42 + } 1.43 + 1.44 + private void toggleAlarm(final Context context, boolean enabled) { 1.45 + final Class<?> serviceClass = AnnouncementsService.class; 1.46 + Logger.info(LOG_TAG, (enabled ? "R" : "Unr") + "egistering " + serviceClass.getSimpleName() + 1.47 + "."); 1.48 + 1.49 + final Intent service = new Intent(context, serviceClass); 1.50 + final PendingIntent pending = PendingIntent.getService(context, 0, service, 1.51 + PendingIntent.FLAG_CANCEL_CURRENT); 1.52 + 1.53 + if (!enabled) { 1.54 + cancelAlarm(pending); 1.55 + return; 1.56 + } 1.57 + 1.58 + final long pollInterval = getPollInterval(context); 1.59 + scheduleAlarm(pollInterval, pending); 1.60 + } 1.61 + 1.62 + /** 1.63 + * Record the last launch time of our version of Fennec. 1.64 + * 1.65 + * @param context 1.66 + * the <code>Context</code> to use to gain access to 1.67 + * <code>SharedPreferences</code>. 1.68 + */ 1.69 + public static void recordLastLaunch(final Context context) { 1.70 + final long now = System.currentTimeMillis(); 1.71 + final SharedPreferences preferences = getSharedPreferences(context); 1.72 + 1.73 + // One of several things might be true, according to our logs: 1.74 + // 1.75 + // * The new current time is older than the last 1.76 + // * … or way in the future 1.77 + // * … or way in the distant past 1.78 + // * … or it's reasonable. 1.79 + // 1.80 + // Furthermore, when we come to calculate idle we might find that the clock 1.81 + // is dramatically different — that the current time is thirteen years older 1.82 + // than our saved timestamp (system clock resets to 2000 on battery change), 1.83 + // or it's thirty years in the future (previous timestamp was saved as 0). 1.84 + // 1.85 + // We should try to do something vaguely sane in these situations. 1.86 + long previous = preferences.getLong(AnnouncementsConstants.PREF_LAST_LAUNCH, -1); 1.87 + if (previous == -1) { 1.88 + Logger.debug(LOG_TAG, "No previous launch recorded."); 1.89 + } 1.90 + 1.91 + if (now < GlobalConstants.BUILD_TIMESTAMP_MSEC) { 1.92 + Logger.warn(LOG_TAG, "Current time " + now + " is older than build date " + 1.93 + GlobalConstants.BUILD_TIMESTAMP_MSEC + ". Ignoring until clock is corrected."); 1.94 + return; 1.95 + } 1.96 + 1.97 + if (now > AnnouncementsConstants.LATEST_ACCEPTED_LAUNCH_TIMESTAMP_MSEC) { 1.98 + Logger.warn(LOG_TAG, "Launch time " + now + " is later than max sane launch timestamp " + 1.99 + AnnouncementsConstants.LATEST_ACCEPTED_LAUNCH_TIMESTAMP_MSEC + 1.100 + ". Ignoring until clock is corrected."); 1.101 + return; 1.102 + } 1.103 + 1.104 + if (previous > now) { 1.105 + Logger.debug(LOG_TAG, "Previous launch " + previous + " later than current time " + 1.106 + now + ", but new time is sane. Accepting new time."); 1.107 + } 1.108 + 1.109 + preferences.edit().putLong(AnnouncementsConstants.PREF_LAST_LAUNCH, now).commit(); 1.110 + } 1.111 + 1.112 + public static long getPollInterval(final Context context) { 1.113 + final SharedPreferences preferences = getSharedPreferences(context); 1.114 + return preferences.getLong(AnnouncementsConstants.PREF_ANNOUNCE_FETCH_INTERVAL_MSEC, AnnouncementsConstants.DEFAULT_ANNOUNCE_FETCH_INTERVAL_MSEC); 1.115 + } 1.116 + 1.117 + public static void setPollInterval(final Context context, long interval) { 1.118 + final SharedPreferences preferences = getSharedPreferences(context); 1.119 + preferences.edit().putLong(AnnouncementsConstants.PREF_ANNOUNCE_FETCH_INTERVAL_MSEC, interval).commit(); 1.120 + } 1.121 + 1.122 + @Override 1.123 + protected void onHandleIntent(Intent intent) { 1.124 + Logger.setThreadLogTag(AnnouncementsConstants.GLOBAL_LOG_TAG); 1.125 + 1.126 + // Intent can be null. Bug 1025937. 1.127 + if (intent == null) { 1.128 + Logger.debug(LOG_TAG, "Short-circuiting on null intent."); 1.129 + return; 1.130 + } 1.131 + 1.132 + final String action = intent.getAction(); 1.133 + Logger.debug(LOG_TAG, "Broadcast onReceive. Intent is " + action); 1.134 + 1.135 + if (AnnouncementsConstants.ACTION_ANNOUNCEMENTS_PREF.equals(action)) { 1.136 + handlePrefIntent(intent); 1.137 + return; 1.138 + } 1.139 + 1.140 + if (Intent.ACTION_BOOT_COMPLETED.equals(action) || 1.141 + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { 1.142 + BackgroundService.reflectContextToFennec(this, 1.143 + GlobalConstants.GECKO_PREFERENCES_CLASS, 1.144 + GlobalConstants.GECKO_BROADCAST_ANNOUNCEMENTS_PREF_METHOD); 1.145 + return; 1.146 + } 1.147 + 1.148 + // Failure case. 1.149 + Logger.warn(LOG_TAG, "Unknown intent " + action); 1.150 + } 1.151 + 1.152 + /** 1.153 + * Handle the intent sent by the browser when it wishes to notify us 1.154 + * of the value of the user preference. Look at the value and toggle the 1.155 + * alarm service accordingly. 1.156 + * 1.157 + * @param intent must be non-null. 1.158 + */ 1.159 + private void handlePrefIntent(Intent intent) { 1.160 + if (!intent.hasExtra("enabled")) { 1.161 + Logger.warn(LOG_TAG, "Got ANNOUNCEMENTS_PREF intent without enabled. Ignoring."); 1.162 + return; 1.163 + } 1.164 + 1.165 + final boolean enabled = intent.getBooleanExtra("enabled", true); 1.166 + Logger.debug(LOG_TAG, intent.getStringExtra("branch") + "/" + 1.167 + intent.getStringExtra("pref") + " = " + 1.168 + (intent.hasExtra("enabled") ? enabled : "")); 1.169 + 1.170 + toggleAlarm(this, enabled); 1.171 + 1.172 + // Primarily intended for debugging and testing, but this doesn't do any harm. 1.173 + if (!enabled) { 1.174 + Logger.info(LOG_TAG, "!enabled: clearing last fetch."); 1.175 + final SharedPreferences sharedPreferences = getSharedPreferences(); 1.176 + final Editor editor = sharedPreferences.edit(); 1.177 + editor.remove(AnnouncementsConstants.PREF_LAST_FETCH_LOCAL_TIME); 1.178 + editor.remove(AnnouncementsConstants.PREF_EARLIEST_NEXT_ANNOUNCE_FETCH); 1.179 + editor.commit(); 1.180 + } 1.181 + } 1.182 +}