diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 937986b06b..6ae85a3460 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -87,10 +87,6 @@ - - - + + + + + - + - - + - + gcmToken; + Optional fcmToken; if (gcmSupported) { - gcmToken = Optional.of(GoogleCloudMessaging.getInstance(RegistrationActivity.this).register(GcmRefreshJob.REGISTRATION_ID)); + fcmToken = FcmUtil.getToken(); } else { - gcmToken = Optional.absent(); + fcmToken = Optional.absent(); } accountManager = AccountManagerFactory.createManager(RegistrationActivity.this, e164number, password); accountManager.requestSmsVerificationCode(smsRetrieverSupported); - return new Pair<>(password, gcmToken); + return new Pair<>(password, fcmToken); } catch (IOException e) { Log.w(TAG, "Error during account registration", e); return null; @@ -726,8 +725,8 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif accountManager.setGcmId(registrationState.gcmToken); } - TextSecurePreferences.setGcmRegistrationId(RegistrationActivity.this, registrationState.gcmToken.orNull()); - TextSecurePreferences.setGcmDisabled(RegistrationActivity.this, !registrationState.gcmToken.isPresent()); + TextSecurePreferences.setFcmToken(RegistrationActivity.this, registrationState.gcmToken.orNull()); + TextSecurePreferences.setFcmDisabled(RegistrationActivity.this, !registrationState.gcmToken.isPresent()); TextSecurePreferences.setWebsocketRegistered(RegistrationActivity.this, true); DatabaseFactory.getIdentityDatabase(RegistrationActivity.this) diff --git a/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java b/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java index 25b25279c8..901896ebdc 100644 --- a/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java +++ b/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java @@ -39,7 +39,7 @@ public class DozeReminder extends Reminder { } public static boolean isEligible(Context context) { - return TextSecurePreferences.isGcmDisabled(context) && + return TextSecurePreferences.isFcmDisabled(context) && !TextSecurePreferences.hasPromptedOptimizeDoze(context) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isIgnoringBatteryOptimizations(context.getPackageName()); diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java index 37d10f5524..fe2e3e0318 100644 --- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java +++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java @@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.dependencies; import android.content.Context; -import org.thoughtcrime.securesms.gcm.GcmBroadcastReceiver; +import org.thoughtcrime.securesms.gcm.FcmService; import org.thoughtcrime.securesms.jobs.AttachmentUploadJob; import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob; import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob; @@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; -import org.thoughtcrime.securesms.jobs.GcmRefreshJob; +import org.thoughtcrime.securesms.jobs.FcmRefreshJob; import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob; @@ -76,7 +76,7 @@ import dagger.Provides; MultiDeviceBlockedUpdateJob.class, DeviceListFragment.class, RefreshAttributesJob.class, - GcmRefreshJob.class, + FcmRefreshJob.class, RequestGroupInfoJob.class, PushGroupUpdateJob.class, AvatarDownloadJob.class, @@ -90,7 +90,7 @@ import dagger.Provides; SendReadReceiptJob.class, MultiDeviceReadReceiptUpdateJob.class, AppProtectionPreferenceFragment.class, - GcmBroadcastReceiver.class, + FcmService.class, RotateCertificateJob.class, SendDeliveryReceiptJob.class, RotateProfileKeyJob.class, @@ -147,7 +147,7 @@ public class SignalCommunicationModule { @Provides synchronized SignalServiceMessageReceiver provideSignalMessageReceiver() { if (this.messageReceiver == null) { - SleepTimer sleepTimer = TextSecurePreferences.isGcmDisabled(context) ? new RealtimeSleepTimer(context) : new UptimeSleepTimer(); + SleepTimer sleepTimer = TextSecurePreferences.isFcmDisabled(context) ? new RealtimeSleepTimer(context) : new UptimeSleepTimer(); this.messageReceiver = new SignalServiceMessageReceiver(networkAccess.getConfiguration(context), new DynamicCredentialsProvider(context), diff --git a/src/org/thoughtcrime/securesms/gcm/GcmBroadcastReceiver.java b/src/org/thoughtcrime/securesms/gcm/FcmService.java similarity index 64% rename from src/org/thoughtcrime/securesms/gcm/GcmBroadcastReceiver.java rename to src/org/thoughtcrime/securesms/gcm/FcmService.java index e20d87ded1..ff87c158fd 100644 --- a/src/org/thoughtcrime/securesms/gcm/GcmBroadcastReceiver.java +++ b/src/org/thoughtcrime/securesms/gcm/FcmService.java @@ -1,92 +1,88 @@ package org.thoughtcrime.securesms.gcm; import android.content.Context; -import android.content.Intent; import android.os.PowerManager; import android.support.annotation.NonNull; -import android.support.v4.content.WakefulBroadcastReceiver; -import android.text.TextUtils; +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement; -import org.thoughtcrime.securesms.logging.Log; - -import com.google.android.gms.gcm.GoogleCloudMessaging; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.jobs.PushContentReceiveJob; +import org.thoughtcrime.securesms.jobs.FcmRefreshJob; import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; +import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.service.GenericForegroundService; import org.thoughtcrime.securesms.util.PowerManagerCompat; +import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; +import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.internal.util.Util; import java.io.IOException; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; -public class GcmBroadcastReceiver extends WakefulBroadcastReceiver implements InjectableType { +public class FcmService extends FirebaseMessagingService implements InjectableType { - private static final String TAG = GcmBroadcastReceiver.class.getSimpleName(); + private static final String TAG = FcmService.class.getSimpleName(); - private static final Executor MESSAGE_EXECUTOR = SignalExecutors.newCachedSingleThreadExecutor("GcmMessageProcessing"); - - private static int activeCount = 0; + private static final Executor MESSAGE_EXECUTOR = SignalExecutors.newCachedSingleThreadExecutor("FcmMessageProcessing"); @Inject SignalServiceMessageReceiver messageReceiver; + private static int activeCount; + @Override - public void onReceive(Context context, Intent intent) { - ApplicationContext.getInstance(context).injectDependencies(this); + public void onMessageReceived(RemoteMessage remoteMessage) { + Log.i(TAG, "FCM message... Original Priority: " + remoteMessage.getOriginalPriority() + ", Actual Priority: " + remoteMessage.getPriority()); + ApplicationContext.getInstance(getApplicationContext()).injectDependencies(this); + handleReceivedNotification(getApplicationContext()); + } - GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context); - String messageType = gcm.getMessageType(intent); - - if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) { - Log.i(TAG, "GCM message..."); - - if (!TextSecurePreferences.isPushRegistered(context)) { - Log.w(TAG, "Not push registered!"); - return; - } - - if (intent.hasExtra("notification")) { - handleReceivedNotification(context); - } else { - Log.w(TAG, "Received an unexpected intent."); - } + @Override + public void onNewToken(String token) { + if (!TextSecurePreferences.isPushRegistered(getApplicationContext())) { + Log.i(TAG, "Got a new FCM token, but the user isn't registered."); + return; } + + ApplicationContext.getInstance(getApplicationContext()) + .getJobManager() + .add(new FcmRefreshJob(getApplicationContext())); } private void handleReceivedNotification(Context context) { if (!incrementActiveGcmCount()) { - Log.i(TAG, "Skipping GCM processing -- there's already one enqueued."); + Log.i(TAG, "Skipping FCM processing -- there's already one enqueued."); return; } TextSecurePreferences.setNeedsMessagePull(context, true); - long startTime = System.currentTimeMillis(); - PendingResult callback = goAsync(); - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - boolean doze = PowerManagerCompat.isDeviceIdleMode(powerManager); - boolean network = new NetworkRequirement(context).isPresent(); + long startTime = System.currentTimeMillis(); + PowerManager powerManager = ServiceUtil.getPowerManager(getApplicationContext()); + boolean doze = PowerManagerCompat.isDeviceIdleMode(powerManager); + boolean network = new NetworkRequirement(context).isPresent(); final Object foregroundLock = new Object(); final AtomicBoolean foregroundRunning = new AtomicBoolean(false); final AtomicBoolean taskCompleted = new AtomicBoolean(false); + final CountDownLatch latch = new CountDownLatch(1); if (doze || !network) { Log.i(TAG, "Starting a foreground task because we may be operating in a constrained environment. Doze: " + doze + " Network: " + network); showForegroundNotification(context); foregroundRunning.set(true); - callback.finish(); + latch.countDown(); } MESSAGE_EXECUTOR.execute(() -> { @@ -102,7 +98,7 @@ public class GcmBroadcastReceiver extends WakefulBroadcastReceiver implements In if (foregroundRunning.getAndSet(false)) { GenericForegroundService.stopForegroundTask(context); } else { - callback.finish(); + latch.countDown(); } taskCompleted.set(true); } @@ -113,20 +109,26 @@ public class GcmBroadcastReceiver extends WakefulBroadcastReceiver implements In }); if (!foregroundRunning.get()) { - new Thread("GcmForegroundServiceTimer") { + new Thread("FcmForegroundServiceTimer") { @Override public void run() { - Util.sleep(4500); + Util.sleep(7000); synchronized (foregroundLock) { if (!taskCompleted.get() && !foregroundRunning.getAndSet(true)) { Log.i(TAG, "Starting a foreground task because the job is running long."); showForegroundNotification(context); - callback.finish(); + latch.countDown(); } } } }.start(); } + + try { + latch.await(); + } catch (InterruptedException e) { + Log.w(TAG, "Latch was interrupted.", e); + } } private void showForegroundNotification(@NonNull Context context) { @@ -147,4 +149,4 @@ public class GcmBroadcastReceiver extends WakefulBroadcastReceiver implements In private static synchronized void decrementActiveGcmCount() { activeCount--; } -} \ No newline at end of file +} diff --git a/src/org/thoughtcrime/securesms/gcm/FcmUtil.java b/src/org/thoughtcrime/securesms/gcm/FcmUtil.java new file mode 100644 index 0000000000..b7c7d67867 --- /dev/null +++ b/src/org/thoughtcrime/securesms/gcm/FcmUtil.java @@ -0,0 +1,43 @@ +package org.thoughtcrime.securesms.gcm; + +import android.support.annotation.WorkerThread; + +import com.google.firebase.iid.FirebaseInstanceId; + +import org.thoughtcrime.securesms.logging.Log; +import org.whispersystems.libsignal.util.guava.Optional; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +public final class FcmUtil { + + private static final String TAG = FcmUtil.class.getSimpleName(); + + /** + * Retrieves the current FCM token. If one isn't available, it'll be generated. + */ + @WorkerThread + public static Optional getToken() { + CountDownLatch latch = new CountDownLatch(1); + AtomicReference token = new AtomicReference<>(null); + + FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> { + if (task.isSuccessful() && task.getResult() != null) { + token.set(task.getResult().getToken()); + } else { + Log.w(TAG, "Failed to get the token.", task.getException()); + } + + latch.countDown(); + }); + + try { + latch.await(); + } catch (InterruptedException e) { + Log.w(TAG, "Was interrupted while waiting for the token."); + } + + return Optional.fromNullable(token.get()); + } +} diff --git a/src/org/thoughtcrime/securesms/jobs/GcmRefreshJob.java b/src/org/thoughtcrime/securesms/jobs/FcmRefreshJob.java similarity index 76% rename from src/org/thoughtcrime/securesms/jobs/GcmRefreshJob.java rename to src/org/thoughtcrime/securesms/jobs/FcmRefreshJob.java index 81394efc5a..f514b17ec2 100644 --- a/src/org/thoughtcrime/securesms/jobs/GcmRefreshJob.java +++ b/src/org/thoughtcrime/securesms/jobs/FcmRefreshJob.java @@ -24,43 +24,44 @@ import android.graphics.BitmapFactory; import android.support.annotation.NonNull; import android.support.v4.app.NotificationCompat; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; + +import org.thoughtcrime.securesms.gcm.FcmUtil; import org.thoughtcrime.securesms.jobmanager.SafeData; import org.thoughtcrime.securesms.logging.Log; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GooglePlayServicesUtil; -import com.google.android.gms.gcm.GoogleCloudMessaging; - import org.thoughtcrime.securesms.PlayServicesProblemActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobmanager.JobParameters; import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; +import java.io.IOException; + import javax.inject.Inject; import androidx.work.Data; import androidx.work.WorkerParameters; -public class GcmRefreshJob extends ContextJob implements InjectableType { +public class FcmRefreshJob extends ContextJob implements InjectableType { - private static final String TAG = GcmRefreshJob.class.getSimpleName(); - - public static final String REGISTRATION_ID = "312334754206"; + private static final String TAG = FcmRefreshJob.class.getSimpleName(); @Inject transient SignalServiceAccountManager textSecureAccountManager; - public GcmRefreshJob(@NonNull Context context, @NonNull WorkerParameters workerParameters) { + public FcmRefreshJob(@NonNull Context context, @NonNull WorkerParameters workerParameters) { super(context, workerParameters); } - public GcmRefreshJob(Context context) { + public FcmRefreshJob(Context context) { super(context, JobParameters.newBuilder() - .withGroupId(GcmRefreshJob.class.getSimpleName()) + .withGroupId(FcmRefreshJob.class.getSimpleName()) .withDuplicatesIgnored(true) .withNetworkRequirement() .withRetryCount(1) @@ -78,20 +79,25 @@ public class GcmRefreshJob extends ContextJob implements InjectableType { @Override public void onRun() throws Exception { - if (TextSecurePreferences.isGcmDisabled(context)) return; + if (TextSecurePreferences.isFcmDisabled(context)) return; - Log.i(TAG, "Reregistering GCM..."); - int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context); + Log.i(TAG, "Reregistering FCM..."); + + int result = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context); if (result != ConnectionResult.SUCCESS) { - notifyGcmFailure(); + notifyFcmFailure(); } else { - String gcmId = GoogleCloudMessaging.getInstance(context).register(REGISTRATION_ID); - textSecureAccountManager.setGcmId(Optional.of(gcmId)); + Optional token = FcmUtil.getToken(); - TextSecurePreferences.setGcmRegistrationId(context, gcmId); - TextSecurePreferences.setGcmRegistrationIdLastSetTime(context, System.currentTimeMillis()); - TextSecurePreferences.setWebsocketRegistered(context, true); + if (token.isPresent()) { + textSecureAccountManager.setGcmId(token); + TextSecurePreferences.setFcmToken(context, token.get()); + TextSecurePreferences.setFcmTokenLastSetTime(context, System.currentTimeMillis()); + TextSecurePreferences.setWebsocketRegistered(context, true); + } else { + throw new RetryLaterException(new IOException("Failed to retrieve a token.")); + } } } @@ -106,7 +112,7 @@ public class GcmRefreshJob extends ContextJob implements InjectableType { return true; } - private void notifyGcmFailure() { + private void notifyFcmFailure() { Intent intent = new Intent(context, PlayServicesProblemActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 1122, intent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationChannels.FAILURES); diff --git a/src/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java b/src/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java index 0c2c9d16f3..5b5b71805b 100644 --- a/src/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java +++ b/src/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java @@ -53,7 +53,7 @@ public class RefreshAttributesJob extends ContextJob implements InjectableType { @Override public void onRun() throws IOException { int registrationId = TextSecurePreferences.getLocalRegistrationId(context); - boolean fetchesMessages = TextSecurePreferences.isGcmDisabled(context); + boolean fetchesMessages = TextSecurePreferences.isFcmDisabled(context); String pin = TextSecurePreferences.getRegistrationLockPin(context); byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context); boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context); diff --git a/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java index d2d1ec08e1..b70addfe42 100644 --- a/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java +++ b/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java @@ -17,7 +17,7 @@ import android.support.v7.preference.Preference; import org.thoughtcrime.securesms.logging.Log; import android.widget.Toast; -import com.google.android.gms.gcm.GoogleCloudMessaging; +import com.google.firebase.iid.FirebaseInstanceId; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.LogSubmitActivity; @@ -192,8 +192,8 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment { Log.w(TAG, e); } - if (!TextSecurePreferences.isGcmDisabled(context)) { - GoogleCloudMessaging.getInstance(context).unregister(); + if (!TextSecurePreferences.isFcmDisabled(context)) { + FirebaseInstanceId.getInstance().deleteInstanceId(); } return SUCCESS; diff --git a/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java b/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java index de098d2521..dacbc49e6b 100644 --- a/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java +++ b/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java @@ -60,7 +60,7 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe new NetworkRequirementProvider(context).setListener(this); new MessageRetrievalThread().start(); - if (TextSecurePreferences.isGcmDisabled(context)) { + if (TextSecurePreferences.isFcmDisabled(context)) { ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class)); } @@ -95,7 +95,7 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe } private synchronized boolean isConnectionNecessary() { - boolean isGcmDisabled = TextSecurePreferences.isGcmDisabled(context); + boolean isGcmDisabled = TextSecurePreferences.isFcmDisabled(context); Log.d(TAG, String.format("Network requirement: %s, app visible: %s, gcm disabled: %b", networkRequirement.isPresent(), appVisible, isGcmDisabled)); diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 457f4355dd..98ba27158d 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -388,11 +388,11 @@ public class TextSecurePreferences { return getBooleanPreference(context, ALWAYS_RELAY_CALLS_PREF, false); } - public static boolean isGcmDisabled(Context context) { + public static boolean isFcmDisabled(Context context) { return getBooleanPreference(context, GCM_DISABLED_PREF, false); } - public static void setGcmDisabled(Context context, boolean disabled) { + public static void setFcmDisabled(Context context, boolean disabled) { setBooleanPreference(context, GCM_DISABLED_PREF, disabled); } @@ -486,12 +486,12 @@ public class TextSecurePreferences { setBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, value); } - public static void setGcmRegistrationId(Context context, String registrationId) { + public static void setFcmToken(Context context, String registrationId) { setStringPreference(context, GCM_REGISTRATION_ID_PREF, registrationId); setIntegerPrefrence(context, GCM_REGISTRATION_ID_VERSION_PREF, Util.getCurrentApkReleaseVersion(context)); } - public static String getGcmRegistrationId(Context context) { + public static String getFcmToken(Context context) { int storedRegistrationIdVersion = getIntegerPreference(context, GCM_REGISTRATION_ID_VERSION_PREF, 0); if (storedRegistrationIdVersion != Util.getCurrentApkReleaseVersion(context)) { @@ -501,11 +501,11 @@ public class TextSecurePreferences { } } - public static long getGcmRegistrationIdLastSetTime(Context context) { + public static long getFcmTokenLastSetTime(Context context) { return getLongPreference(context, GCM_REGISTRATION_ID_TIME_PREF, 0); } - public static void setGcmRegistrationIdLastSetTime(Context context, long timestamp) { + public static void setFcmTokenLastSetTime(Context context, long timestamp) { setLongPreference(context, GCM_REGISTRATION_ID_TIME_PREF, timestamp); }