[FileEncap18] Clean up uploading scheduling

- Move scheduling logic from ResearchLogger.java to
  UploaderService.java
- Switch to a one-shot timer.  Previously the uploader was scheduled
  on an inexact repeating schedule.  It's better to reschedule the
  next upload after the current one is finished to reduce the chances
  of multiple uploads happening at the same time.
- Avoid double-execution
    - Previously a scheduled upload might run right after an explicit
      one if they occured at the same time.  This change reduces the
      chances of this.
- Some method extraction and naming

Change-Id: I9efda11be77d334c7f61bd40a36d65f0421ebde4
main
Kurt Partridge 2013-02-27 17:27:12 -08:00
parent fb658d6c53
commit 6d71d238e2
3 changed files with 51 additions and 31 deletions

View File

@ -25,9 +25,10 @@ import android.content.Intent;
*/
public final class BootBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
ResearchLogger.scheduleUploadingService(context);
UploaderService.cancelAndRescheduleUploadingService(context,
true /* needsRescheduling */);
}
}
}

View File

@ -20,16 +20,13 @@ import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOAR
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
@ -74,22 +71,16 @@ import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.research.MotionEventReader.ReplayData;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.UUID;
/**
* Logs the use of the LatinIME keyboard.
@ -254,7 +245,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mUploadNowIntent = new Intent(mLatinIME, UploaderService.class);
mUploadNowIntent.putExtra(UploaderService.EXTRA_UPLOAD_UNCONDITIONALLY, true);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
scheduleUploadingService(mLatinIME);
UploaderService.cancelAndRescheduleUploadingService(mLatinIME,
true /* needsRescheduling */);
}
mReplayer.setKeyboardSwitcher(keyboardSwitcher);
}
@ -268,25 +260,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
ResearchSettings.writeResearchLastDirCleanupTime(mPrefs, now);
}
/**
* Arrange for the UploaderService to be run on a regular basis.
*
* Any existing scheduled invocation of UploaderService is removed and rescheduled. This may
* cause problems if this method is called often and frequent updates are required, but since
* the user will likely be sleeping at some point, if the interval is less that the expected
* sleep duration and this method is not called during that time, the service should be invoked
* at some point.
*/
public static void scheduleUploadingService(Context context) {
final Intent intent = new Intent(context, UploaderService.class);
final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
final AlarmManager manager =
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
manager.cancel(pendingIntent);
manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
UploaderService.RUN_INTERVAL, UploaderService.RUN_INTERVAL, pendingIntent);
}
public void mainKeyboardView_onAttachedToWindow(final MainKeyboardView mainKeyboardView) {
mMainKeyboardView = mainKeyboardView;
maybeShowSplashScreen();

View File

@ -18,6 +18,8 @@ package com.android.inputmethod.research;
import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@ -43,11 +45,17 @@ public final class UploaderService extends IntentService {
@Override
protected void onHandleIntent(final Intent intent) {
// We may reach this point either because the alarm fired, or because the system explicitly
// requested that an Upload occur. In the latter case, we want to cancel the alarm in case
// it's about to fire.
cancelAndRescheduleUploadingService(this, false /* needsRescheduling */);
final Uploader uploader = new Uploader(this);
if (!uploader.isPossibleToUpload()) return;
if (isUploadingUnconditionally(intent.getExtras()) || uploader.isConvenientToUpload()) {
uploader.doUpload();
}
cancelAndRescheduleUploadingService(this, true /* needsRescheduling */);
}
private boolean isUploadingUnconditionally(final Bundle bundle) {
@ -57,4 +65,42 @@ public final class UploaderService extends IntentService {
}
return false;
}
/**
* Arrange for the UploaderService to be run on a regular basis.
*
* Any existing scheduled invocation of UploaderService is removed and optionally rescheduled.
* This may cause problems if this method is called so often that no scheduled invocation is
* ever run. But if the delay is short enough that it will go off when the user is sleeping,
* then there should be no starvation.
*
* @param context {@link Context} object
* @param needsRescheduling whether to schedule a future intent to be delivered to this service
*/
public static void cancelAndRescheduleUploadingService(final Context context,
final boolean needsRescheduling) {
final PendingIntent pendingIntent = getPendingIntentForService(context);
final AlarmManager alarmManager = (AlarmManager) context.getSystemService(
Context.ALARM_SERVICE);
cancelAnyScheduledServiceAlarm(alarmManager, pendingIntent);
if (needsRescheduling) {
scheduleServiceAlarm(alarmManager, pendingIntent);
}
}
private static PendingIntent getPendingIntentForService(final Context context) {
final Intent intent = new Intent(context, UploaderService.class);
return PendingIntent.getService(context, 0, intent, 0);
}
private static void cancelAnyScheduledServiceAlarm(final AlarmManager alarmManager,
final PendingIntent pendingIntent) {
alarmManager.cancel(pendingIntent);
}
private static void scheduleServiceAlarm(final AlarmManager alarmManager,
final PendingIntent pendingIntent) {
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, UploaderService.RUN_INTERVAL,
pendingIntent);
}
}