mobile/android/base/background/healthreport/HealthReportBroadcastService.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.background.healthreport;
     7 import org.mozilla.gecko.background.BackgroundService;
     8 import org.mozilla.gecko.background.common.GlobalConstants;
     9 import org.mozilla.gecko.background.common.log.Logger;
    10 import org.mozilla.gecko.background.healthreport.prune.HealthReportPruneService;
    11 import org.mozilla.gecko.background.healthreport.upload.HealthReportUploadService;
    12 import org.mozilla.gecko.background.healthreport.upload.ObsoleteDocumentTracker;
    14 import android.app.AlarmManager;
    15 import android.app.PendingIntent;
    16 import android.content.Context;
    17 import android.content.Intent;
    18 import android.content.SharedPreferences;
    19 import android.content.SharedPreferences.Editor;
    21 /**
    22  * A service which listens to broadcast intents from the system and from the
    23  * browser, registering or unregistering the background health report services with the
    24  * {@link AlarmManager}.
    25  */
    26 public class HealthReportBroadcastService extends BackgroundService {
    27   public static final String LOG_TAG = HealthReportBroadcastService.class.getSimpleName();
    28   public static final String WORKER_THREAD_NAME = LOG_TAG + "Worker";
    30   public HealthReportBroadcastService() {
    31     super(WORKER_THREAD_NAME);
    32   }
    34   protected SharedPreferences getSharedPreferences() {
    35     return this.getSharedPreferences(HealthReportConstants.PREFS_BRANCH, GlobalConstants.SHARED_PREFERENCES_MODE);
    36   }
    38   public long getSubmissionPollInterval() {
    39     return getSharedPreferences().getLong(HealthReportConstants.PREF_SUBMISSION_INTENT_INTERVAL_MSEC, HealthReportConstants.DEFAULT_SUBMISSION_INTENT_INTERVAL_MSEC);
    40   }
    42   public void setSubmissionPollInterval(final long interval) {
    43     getSharedPreferences().edit().putLong(HealthReportConstants.PREF_SUBMISSION_INTENT_INTERVAL_MSEC, interval).commit();
    44   }
    46   public long getPrunePollInterval() {
    47     return getSharedPreferences().getLong(HealthReportConstants.PREF_PRUNE_INTENT_INTERVAL_MSEC,
    48         HealthReportConstants.DEFAULT_PRUNE_INTENT_INTERVAL_MSEC);
    49   }
    51   public void setPrunePollInterval(final long interval) {
    52     getSharedPreferences().edit().putLong(HealthReportConstants.PREF_PRUNE_INTENT_INTERVAL_MSEC,
    53         interval).commit();
    54   }
    56   /**
    57    * Set or cancel an alarm to submit data for a profile.
    58    *
    59    * @param context
    60    *          Android context.
    61    * @param profileName
    62    *          to submit data for.
    63    * @param profilePath
    64    *          to submit data for.
    65    * @param enabled
    66    *          whether the user has enabled submitting health report data for
    67    *          this profile.
    68    * @param serviceEnabled
    69    *          whether submitting should be scheduled. If the user turns off
    70    *          submitting, <code>enabled</code> could be false but we could need
    71    *          to delete so <code>serviceEnabled</code> could be true.
    72    */
    73   protected void toggleSubmissionAlarm(final Context context, String profileName, String profilePath,
    74       boolean enabled, boolean serviceEnabled) {
    75     final Class<?> serviceClass = HealthReportUploadService.class;
    76     Logger.info(LOG_TAG, (serviceEnabled ? "R" : "Unr") + "egistering " +
    77         serviceClass.getSimpleName() + ".");
    79     // PendingIntents are compared without reference to their extras. Therefore
    80     // even though we pass the profile details to the action, different
    81     // profiles will share the *same* pending intent. In a multi-profile future,
    82     // this will need to be addressed.  See Bug 882182.
    83     final Intent service = new Intent(context, serviceClass);
    84     service.setAction("upload"); // PendingIntents "lose" their extras if no action is set.
    85     service.putExtra("uploadEnabled", enabled);
    86     service.putExtra("profileName", profileName);
    87     service.putExtra("profilePath", profilePath);
    88     final PendingIntent pending = PendingIntent.getService(context, 0, service, PendingIntent.FLAG_CANCEL_CURRENT);
    90     if (!serviceEnabled) {
    91       cancelAlarm(pending);
    92       return;
    93     }
    95     final long pollInterval = getSubmissionPollInterval();
    96     scheduleAlarm(pollInterval, pending);
    97   }
    99   @Override
   100   protected void onHandleIntent(Intent intent) {
   101     Logger.setThreadLogTag(HealthReportConstants.GLOBAL_LOG_TAG);
   103     // Intent can be null. Bug 1025937.
   104     if (intent == null) {
   105       Logger.debug(LOG_TAG, "Short-circuiting on null intent.");
   106       return;
   107     }
   109     // The same intent can be handled by multiple methods so do not short-circuit evaluate.
   110     boolean handled = attemptHandleIntentForUpload(intent);
   111     handled = attemptHandleIntentForPrune(intent) ? true : handled;
   113     if (!handled) {
   114       Logger.warn(LOG_TAG, "Unhandled intent with action " + intent.getAction() + ".");
   115     }
   116   }
   118   /**
   119    * Attempts to handle the given intent for FHR document upload. If it cannot, false is returned.
   120    *
   121    * @param intent must be non-null.
   122    */
   123   private boolean attemptHandleIntentForUpload(final Intent intent) {
   124     if (HealthReportConstants.UPLOAD_FEATURE_DISABLED) {
   125       Logger.debug(LOG_TAG, "Health report upload feature is compile-time disabled; not handling intent.");
   126       return false;
   127     }
   129     final String action = intent.getAction();
   130     Logger.debug(LOG_TAG, "Health report upload feature is compile-time enabled; attempting to " +
   131         "handle intent with action " + action + ".");
   133     if (HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF.equals(action)) {
   134       handleUploadPrefIntent(intent);
   135       return true;
   136     }
   138     if (Intent.ACTION_BOOT_COMPLETED.equals(action) ||
   139         Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
   140       BackgroundService.reflectContextToFennec(this,
   141           GlobalConstants.GECKO_PREFERENCES_CLASS,
   142           GlobalConstants.GECKO_BROADCAST_HEALTHREPORT_UPLOAD_PREF_METHOD);
   143       return true;
   144     }
   146     return false;
   147   }
   149   /**
   150    * Handle the intent sent by the browser when it wishes to notify us
   151    * of the value of the user preference. Look at the value and toggle the
   152    * alarm service accordingly.
   153    *
   154    * @param intent must be non-null.
   155    */
   156   private void handleUploadPrefIntent(Intent intent) {
   157     if (!intent.hasExtra("enabled")) {
   158       Logger.warn(LOG_TAG, "Got " + HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF + " intent without enabled. Ignoring.");
   159       return;
   160     }
   162     final boolean enabled = intent.getBooleanExtra("enabled", true);
   163     Logger.debug(LOG_TAG, intent.getStringExtra("branch") + "/" +
   164                           intent.getStringExtra("pref")   + " = " +
   165                           (intent.hasExtra("enabled") ? enabled : ""));
   167     String profileName = intent.getStringExtra("profileName");
   168     String profilePath = intent.getStringExtra("profilePath");
   170     if (profileName == null || profilePath == null) {
   171       Logger.warn(LOG_TAG, "Got " + HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF + " intent without profilePath or profileName. Ignoring.");
   172       return;
   173     }
   175     Logger.pii(LOG_TAG, "Updating health report upload alarm for profile " + profileName + " at " +
   176         profilePath + ".");
   178     final SharedPreferences sharedPrefs = getSharedPreferences();
   179     final ObsoleteDocumentTracker tracker = new ObsoleteDocumentTracker(sharedPrefs);
   180     final boolean hasObsoleteIds = tracker.hasObsoleteIds();
   182     if (!enabled) {
   183       final Editor editor = sharedPrefs.edit();
   184       editor.remove(HealthReportConstants.PREF_LAST_UPLOAD_DOCUMENT_ID);
   186       if (hasObsoleteIds) {
   187         Logger.debug(LOG_TAG, "Health report upload disabled; scheduling deletion of " + tracker.numberOfObsoleteIds() + " documents.");
   188         tracker.limitObsoleteIds();
   189       } else {
   190         // Primarily intended for debugging and testing.
   191         Logger.debug(LOG_TAG, "Health report upload disabled and no deletes to schedule: clearing prefs.");
   192         editor.remove(HealthReportConstants.PREF_FIRST_RUN);
   193         editor.remove(HealthReportConstants.PREF_NEXT_SUBMISSION);
   194       }
   196       editor.commit();
   197     }
   199     // The user can toggle us off or on, or we can have obsolete documents to
   200     // remove.
   201     final boolean serviceEnabled = hasObsoleteIds || enabled;
   202     toggleSubmissionAlarm(this, profileName, profilePath, enabled, serviceEnabled);
   203   }
   205   /**
   206    * Attempts to handle the given intent for FHR data pruning. If it cannot, false is returned.
   207    *
   208    * @param intent must be non-null.
   209    */
   210   private boolean attemptHandleIntentForPrune(final Intent intent) {
   211     final String action = intent.getAction();
   212     Logger.debug(LOG_TAG, "Prune: Attempting to handle intent with action, " + action + ".");
   214     if (HealthReportConstants.ACTION_HEALTHREPORT_PRUNE.equals(action)) {
   215       handlePruneIntent(intent);
   216       return true;
   217     }
   219     if (Intent.ACTION_BOOT_COMPLETED.equals(action) ||
   220         Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
   221       BackgroundService.reflectContextToFennec(this,
   222           GlobalConstants.GECKO_PREFERENCES_CLASS,
   223           GlobalConstants.GECKO_BROADCAST_HEALTHREPORT_PRUNE_METHOD);
   224       return true;
   225     }
   227     return false;
   228   }
   230   /**
   231    * @param intent must be non-null.
   232    */
   233   private void handlePruneIntent(final Intent intent) {
   234     final String profileName = intent.getStringExtra("profileName");
   235     final String profilePath = intent.getStringExtra("profilePath");
   237     if (profileName == null || profilePath == null) {
   238       Logger.warn(LOG_TAG, "Got " + HealthReportConstants.ACTION_HEALTHREPORT_PRUNE + " intent " +
   239           "without profilePath or profileName. Ignoring.");
   240       return;
   241     }
   243     final Class<?> serviceClass = HealthReportPruneService.class;
   244     final Intent service = new Intent(this, serviceClass);
   245     service.setAction("prune"); // Intents without actions have their extras removed.
   246     service.putExtra("profileName", profileName);
   247     service.putExtra("profilePath", profilePath);
   248     final PendingIntent pending = PendingIntent.getService(this, 0, service,
   249         PendingIntent.FLAG_CANCEL_CURRENT);
   251     // Set a regular alarm to start PruneService. Since the various actions that PruneService can
   252     // take occur on irregular intervals, we can be more efficient by only starting the Service
   253     // when one of these time limits runs out.  However, subsequent Service invocations must then
   254     // be registered by the PruneService itself, which would fail if the PruneService crashes.
   255     // Thus, we set this regular (and slightly inefficient) alarm.
   256     Logger.info(LOG_TAG, "Registering " + serviceClass.getSimpleName() + ".");
   257     final long pollInterval = getPrunePollInterval();
   258     scheduleAlarm(pollInterval, pending);
   259   }
   260 }

mercurial