gt:                 if (service.mPushRegistrationId == null) {
context: /*
   * Kontalk Android client
   * Copyright (C) 2020 Kontalk Devteam <devteam@kontalk.org>
  
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
  
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
  
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  package org.kontalk.service.msgcenter;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;
  import java.lang.ref.WeakReference;
  import java.security.GeneralSecurityException;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Date;
  import java.util.List;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  import java.util.zip.ZipInputStream;
  
  import org.greenrobot.eventbus.EventBus;
  import org.greenrobot.eventbus.Subscribe;
  import org.greenrobot.eventbus.ThreadMode;
  import org.jivesoftware.smack.ExceptionCallback;
  import org.jivesoftware.smack.SmackConfiguration;
  import org.jivesoftware.smack.SmackException;
  import org.jivesoftware.smack.SmackException.NotConnectedException;
  import org.jivesoftware.smack.StanzaListener;
  import org.jivesoftware.smack.XMPPConnection;
  import org.jivesoftware.smack.XMPPException;
  import org.jivesoftware.smack.debugger.AbstractDebugger;
  import org.jivesoftware.smack.debugger.SmackDebugger;
  import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
  import org.jivesoftware.smack.filter.StanzaFilter;
  import org.jivesoftware.smack.filter.StanzaIdFilter;
  import org.jivesoftware.smack.filter.StanzaTypeFilter;
  import org.jivesoftware.smack.packet.IQ;
  import org.jivesoftware.smack.packet.Presence;
  import org.jivesoftware.smack.packet.Stanza;
  import org.jivesoftware.smack.packet.StanzaError;
  import org.jivesoftware.smack.roster.Roster;
  import org.jivesoftware.smack.roster.RosterEntry;
  import org.jivesoftware.smack.roster.packet.RosterPacket;
  import org.jivesoftware.smack.sm.StreamManagementException;
  import org.jivesoftware.smack.util.Async;
  import org.jivesoftware.smack.util.StringUtils;
  import org.jivesoftware.smack.util.SuccessCallback;
  import org.jivesoftware.smackx.caps.packet.CapsExtension;
  import org.jivesoftware.smackx.chatstates.ChatState;
  import org.jivesoftware.smackx.chatstates.packet.ChatStateExtension;
  import org.jivesoftware.smackx.csi.ClientStateIndicationManager;
  import org.jivesoftware.smackx.delay.packet.DelayInformation;
  import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
  import org.jivesoftware.smackx.disco.packet.DiscoverItems;
  import org.jivesoftware.smackx.forward.packet.Forwarded;
  import org.jivesoftware.smackx.iqlast.packet.LastActivity;
  import org.jivesoftware.smackx.iqversion.VersionManager;
  import org.jivesoftware.smackx.iqversion.packet.Version;
  import org.jivesoftware.smackx.ping.PingFailedListener;
  import org.jivesoftware.smackx.ping.PingManager;
  import org.jivesoftware.smackx.receipts.DeliveryReceipt;
  import org.jivesoftware.smackx.receipts.DeliveryReceiptRequest;
  import org.jxmpp.jid.BareJid;
  import org.jxmpp.jid.Jid;
  import org.jxmpp.jid.impl.JidCreate;
  import org.jxmpp.jid.parts.Domainpart;
  import org.jxmpp.jid.parts.Localpart;
  import org.jxmpp.stringprep.XmppStringprepException;
  import org.bouncycastle.openpgp.PGPException;
  
  import android.annotation.SuppressLint;
  import android.app.AlarmManager;
  import android.app.PendingIntent;
  import android.app.Service;
  import android.content.BroadcastReceiver;
  import android.content.ContentValues;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.net.Uri;
  import android.os.Handler;
  import android.os.HandlerThread;
  import android.os.IBinder;
  import android.os.Looper;
  import android.os.Message;
  import android.os.MessageQueue.IdleHandler;
  import android.os.PowerManager;
  import android.os.PowerManager.WakeLock;
  import android.os.Process;
  import android.os.SystemClock;
  
  import androidx.annotation.CheckResult;
  import androidx.annotation.Nullable;
  import androidx.core.app.ServiceCompat;
  import androidx.core.content.ContextCompat;
  import androidx.localbroadcastmanager.content.LocalBroadcastManager;
  import android.text.TextUtils;
  import android.widget.Toast;
  
  import org.kontalk.BuildConfig;
  import org.kontalk.Kontalk;
  import org.kontalk.Log;
  import org.kontalk.R;
  import org.kontalk.authenticator.Authenticator;
  import org.kontalk.authenticator.MyAccount;
  import org.kontalk.client.BitsOfBinary;
  import org.kontalk.client.BlockingCommand;
  import org.kontalk.client.E2EEncryption;
  import org.kontalk.client.EndpointServer;
  import org.kontalk.client.KontalkConnection;
  import org.kontalk.client.OutOfBandData;
  import org.kontalk.client.PublicKeyPublish;
  import org.kontalk.client.PushRegistration;
  import org.kontalk.client.RosterMatch;
  import org.kontalk.client.ServerlistCommand;
  import org.kontalk.client.SmackInitializer;
  import org.kontalk.client.UserLocation;
  import org.kontalk.crypto.Coder;
  import org.kontalk.crypto.PersonalKey;
  import org.kontalk.data.Contact;
  import org.kontalk.data.Conversation;
  import org.kontalk.data.GroupInfo;
  import org.kontalk.message.AttachmentComponent;
  import org.kontalk.message.CompositeMessage;
  import org.kontalk.message.GroupCommandComponent;
  import org.kontalk.message.GroupComponent;
  import org.kontalk.message.HiddenTextComponent;
  import org.kontalk.message.InReplyToComponent;
  import org.kontalk.message.LocationComponent;
  import org.kontalk.message.ReferencedMessage;
  import org.kontalk.message.TextComponent;
  import org.kontalk.provider.Keyring;
  import org.kontalk.provider.MessagesProviderClient;
  import org.kontalk.provider.MessagesProviderClient.MessageUpdater;
  import org.kontalk.provider.MyMessages.Messages;
  import org.kontalk.provider.MyMessages.Threads;
  import org.kontalk.provider.MyMessages.Threads.Requests;
  import org.kontalk.provider.MyUsers;
  import org.kontalk.provider.UsersProvider;
  import org.kontalk.reporting.ReportingManager;
  import org.kontalk.service.KeyPairGeneratorService;
  import org.kontalk.service.UploadService;
  import org.kontalk.service.XMPPConnectionHelper;
  import org.kontalk.service.XMPPConnectionHelper.ConnectionHelperListener;
  import org.kontalk.service.msgcenter.event.BlocklistEvent;
  import org.kontalk.service.msgcenter.event.BlocklistRequest;
  import org.kontalk.service.msgcenter.event.ConnectedEvent;
  import org.kontalk.service.msgcenter.event.DisconnectedEvent;
  import org.kontalk.service.msgcenter.event.LastActivityRequest;
  import org.kontalk.service.msgcenter.event.NoPresenceEvent;
  import org.kontalk.service.msgcenter.event.PresenceEvent;
  import org.kontalk.service.msgcenter.event.PresenceRequest;
  import org.kontalk.service.msgcenter.event.PublicKeyRequest;
  import org.kontalk.service.msgcenter.event.RosterMatchRequest;
  import org.kontalk.service.msgcenter.event.RosterStatusEvent;
  import org.kontalk.service.msgcenter.event.RosterStatusRequest;
  import org.kontalk.service.msgcenter.event.SendChatStateRequest;
  import org.kontalk.service.msgcenter.event.SendDeliveryReceiptRequest;
  import org.kontalk.service.msgcenter.event.SendMessageRequest;
  import org.kontalk.service.msgcenter.event.ServerListEvent;
  import org.kontalk.service.msgcenter.event.ServerListRequest;
  import org.kontalk.service.msgcenter.event.SetUserPrivacyRequest;
  import org.kontalk.service.msgcenter.event.SubscribeRequest;
  import org.kontalk.service.msgcenter.event.UnsubscribeRequest;
  import org.kontalk.service.msgcenter.event.UpdateStatusRequest;
  import org.kontalk.service.msgcenter.event.UploadAttachmentRequest;
  import org.kontalk.service.msgcenter.event.UploadPrivateKeyRequest;
  import org.kontalk.service.msgcenter.event.UploadServiceFoundEvent;
  import org.kontalk.service.msgcenter.event.UserBlockedEvent;
  import org.kontalk.service.msgcenter.event.UserOnlineEvent;
  import org.kontalk.service.msgcenter.event.UserUnblockedEvent;
  import org.kontalk.service.msgcenter.event.VersionRequest;
  import org.kontalk.service.msgcenter.group.AddRemoveMembersCommand;
  import org.kontalk.service.msgcenter.group.CreateGroupCommand;
  import org.kontalk.service.msgcenter.group.GroupCommand;
  import org.kontalk.service.msgcenter.group.GroupController;
  import org.kontalk.service.msgcenter.group.GroupControllerFactory;
  import org.kontalk.service.msgcenter.group.PartCommand;
  import org.kontalk.service.msgcenter.group.SetSubjectCommand;
  import org.kontalk.ui.MessagingNotification;
  import org.kontalk.util.DataUtils;
  import org.kontalk.util.EventBusIndex;
  import org.kontalk.util.MediaStorage;
  import org.kontalk.util.MessageUtils;
  import org.kontalk.util.Preferences;
  import org.kontalk.util.SystemUtils;
  import org.kontalk.util.WakefulHashSet;
  import org.kontalk.util.XMPPUtils;
  
  import static org.kontalk.ui.MessagingNotification.NOTIFICATION_ID_FOREGROUND;
  
  
  /**
   * The Message Center Service.
   * Use {@link Intent}s to deliver commands (via {@link Context#startService}).
   * Service will broadcast intents when certain events occur.
   *
   * @author Daniele Ricci
   */
  public class MessageCenterService extends Service implements ConnectionHelperListener {
      public static final String TAG = MessageCenterService.class.getSimpleName();
  
      private static final EventBus BUS;
  
      static {
          BUS = EventBus.builder()
              // TODO .logger(...)
              .addIndex(new EventBusIndex())
              .throwSubscriberException(BuildConfig.DEBUG)
              .logNoSubscriberMessages(BuildConfig.DEBUG)
              .build();
          SmackConfiguration.DEBUG = Log.isDebug();
          // we need our own debugger factory because of our internal logging system
          SmackConfiguration.setDefaultSmackDebuggerFactory(new SmackDebuggerFactory() {
              @Override
              public SmackDebugger create(XMPPConnection connection) throws IllegalArgumentException {
                  return new AbstractDebugger(connection) {
                      @Override
                      protected void log(String logMessage) {
                          Log.d("SMACK", logMessage);
                      }
  
                      @Override
                      protected void log(String logMessage, Throwable throwable) {
                          Log.d("SMACK", logMessage, throwable);
                      }
                  };
              }
          });
      }
  
      public static final String ACTION_HOLD = "org.kontalk.action.HOLD";
      public static final String ACTION_RELEASE = "org.kontalk.action.RELEASE";
      public static final String ACTION_RESTART = "org.kontalk.action.RESTART";
      public static final String ACTION_TEST = "org.kontalk.action.TEST";
      public static final String ACTION_PUSH_START = "org.kontalk.push.START";
      public static final String ACTION_PUSH_STOP = "org.kontalk.push.STOP";
      public static final String ACTION_PUSH_REGISTERED = "org.kontalk.push.REGISTERED";
      public static final String ACTION_IDLE = "org.kontalk.action.IDLE";
      public static final String ACTION_PING = "org.kontalk.action.PING";
  
      /**
       * Commence key pair regeneration.
       * {@link KeyPairGeneratorService} service will be started to generate the
       * key pair. After that, we will send the public key to the server for
       * verification and signature. Once the server returns the signed public
       * key, it will be installed in the default account.
       * Broadcasted when key pair regeneration has completed.
       */
      @Deprecated
      public static final String ACTION_REGENERATE_KEYPAIR = "org.kontalk.action.REGEN_KEYPAIR";
  
      /**
       * Commence key pair import.
       */
      @Deprecated
      public static final String ACTION_IMPORT_KEYPAIR = "org.kontalk.action.IMPORT_KEYPAIR";
  
      // common parameters
      public static final String EXTRA_FOREGROUND = "org.kontalk.foreground";
  
      // used with org.kontalk.action.IMPORT_KEYPAIR
      public static final String EXTRA_KEYPACK = "org.kontalk.keypack";
      public static final String EXTRA_PASSPHRASE = "org.kontalk.passphrase";
  
      // used with org.kontalk.action.TEST
      public static final String EXTRA_TEST_CHECK_NETWORK = "org.kontalk.test.check_network";
  
      // other
      public static final String PUSH_REGISTRATION_ID = "org.kontalk.PUSH_REGISTRATION_ID";
      private static final String DEFAULT_PUSH_PROVIDER = "gcm";
  
      /**
       * Minimal wakeup time.
       */
      public final static int MIN_WAKEUP_TIME = 300000;
  
      /**
       * Normal ping tester timeout.
       */
      private static final int SLOW_PING_TIMEOUT = 10000;
  
      /**
       * Fast ping tester timeout.
       */
      private static final int FAST_PING_TIMEOUT = 5000;
  
      /**
       * Minimal interval between connection tests (2 mins).
       */
      private static final int MIN_TEST_INTERVAL = 2 * 60 * 1000;
  
      /** How long to retain the wakelock to wait for incoming messages. */
      private static final int WAIT_FOR_MESSAGES_DELAY = 5000;
  
      static final IPushListener sPushListener = PushServiceManager.getDefaultListener();
  
      /**
       * Push service instance.
       */
      private IPushService mPushService;
      /**
       * Push notifications enabled flag.
       */
      boolean mPushNotifications;
      /**
       * Server push sender id. This is static so the {@link IPushListener} can see it.
       */
      static String sPushSenderId;
      /**
       * Push registration id.
       */
      String mPushRegistrationId;
      /**
       * Flag marking a currently ongoing push registration cycle (unregister/register)
       */
      boolean mPushRegistrationCycle;
  
      // created in onCreate
      private WakeLock mWakeLock;
      private WakeLock mPingLock;
      LocalBroadcastManager mLocalBroadcastManager;
      private AlarmManager mAlarmManager;
  
      private LastActivityListener mLastActivityListener;
      private PingFailedListener mPingFailedListener;
  
      /**
       * Cached last used server.
       */
      EndpointServer mServer;
      /**
       * The connection helper instance.
       */
      XMPPConnectionHelper mHelper;
      /**
       * The connection instance.
       */
      KontalkConnection mConnection;
      /**
       * My username (account name).
       */
      String mMyUsername;
  
      /** The current network identifier. */
      private String mCurrentNetwork;
  
      /**
       * Supported upload services.
       */
      List<IUploadService> mUploadServices;
  
      /**
       * Roster store.
       */
      private SQLiteRosterStore mRosterStore;
  
      /**
       * Service handler.
       */
      Handler mHandler;
      /**
       * Task execution pool. Generally used by packet listeners.
       */
      private ExecutorService mThreadPool;
  
      /**
       * Idle handler.
       */
      IdleConnectionHandler mIdleHandler;
      /**
       * Inactive state flag (for CSI).
       */
      private boolean mInactive;
      /**
       * Timestamp of last use of {@link #ACTION_TEST}.
       */
      private long mLastTest;
      /**
       * Pending intent for idle signaling.
       */
      private PendingIntent mIdleIntent;
  
      private boolean mFirstStart = true;
  
      /**
       * Messages waiting for server ack (type: internalStorageId).
       */
      WakefulHashSet<Long> mWaitingReceipt;
  
      private RegenerateKeyPairListener mKeyPairRegenerator;
      private ImportKeyPairListener mKeyPairImporter;
  
      static final class IdleConnectionHandler extends Handler implements IdleHandler {
          /**
           * Idle signal.
           */
          private static final int MSG_IDLE = 1;
          /**
           * Inactive signal (for CSI).
           */
          private static final int MSG_INACTIVE = 2;
          /**
           * Test signal.
           */
          private static final int MSG_TEST = 3;
  
          /**
           * How much time to wait to enter inactive state.
           */
          private final static int INACTIVE_TIME = 30000;
  
          /**
           * A reference to the message center.
           */
          WeakReference<MessageCenterService> s;
          /**
           * Reference counter.
           */
          int mRefCount;
  
          public IdleConnectionHandler(MessageCenterService service, int refCount, Looper looper) {
              super(looper);
              s = new WeakReference<>(service);
              mRefCount = refCount;
  
              // set idle handler for the first idle message
              Looper.myQueue().addIdleHandler(this);
          }
  
          /**
           * Queue idle callback. This gets called just one time to issue the
           * first idle message.
           */
          @Override
          public boolean queueIdle() {
              reset();
              return false;
          }
  
          @Override
          public void handleMessage(Message msg) {
              MessageCenterService service = s.get();
              boolean consumed = false;
              if (service != null)
                  consumed = handleMessage(service, msg);
  
              if (!consumed)
                  super.handleMessage(msg);
          }
  
          private boolean handleMessage(MessageCenterService service, Message msg) {
              if (msg.what == MSG_IDLE) {
                  if (service.isConnecting()) {
                      // try again next time
                      queueInactive();
                  }
  
                  // push notifications unavailable: set up an alarm for next time
  