private void checkAndLaunchUpdate() {
    Log.i(LOG_FILE_NAME, "Checking for an update");

    int statusCode = 8; // UNEXPECTED_ERROR
    File baseUpdateDir = null;
    if (Build.VERSION.SDK_INT >= 8)
      baseUpdateDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
    else baseUpdateDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");

    File updateDir = new File(new File(baseUpdateDir, "updates"), "0");

    File updateFile = new File(updateDir, "update.apk");
    File statusFile = new File(updateDir, "update.status");

    if (!statusFile.exists() || !readUpdateStatus(statusFile).equals("pending")) return;

    if (!updateFile.exists()) return;

    Log.i(LOG_FILE_NAME, "Update is available!");

    // Launch APK
    File updateFileToRun = new File(updateDir, getPackageName() + "-update.apk");
    try {
      if (updateFile.renameTo(updateFileToRun)) {
        String amCmd =
            "/system/bin/am start -a android.intent.action.VIEW "
                + "-n com.android.packageinstaller/.PackageInstallerActivity -d file://"
                + updateFileToRun.getPath();
        Log.i(LOG_FILE_NAME, amCmd);
        Runtime.getRuntime().exec(amCmd);
        statusCode = 0; // OK
      } else {
        Log.i(LOG_FILE_NAME, "Cannot rename the update file!");
        statusCode = 7; // WRITE_ERROR
      }
    } catch (Exception e) {
      Log.i(LOG_FILE_NAME, "error launching installer to update", e);
    }

    // Update the status file
    String status = statusCode == 0 ? "succeeded\n" : "failed: " + statusCode + "\n";

    OutputStream outStream;
    try {
      byte[] buf = status.getBytes("UTF-8");
      outStream = new FileOutputStream(statusFile);
      outStream.write(buf, 0, buf.length);
      outStream.close();
    } catch (Exception e) {
      Log.i(LOG_FILE_NAME, "error writing status file", e);
    }

    if (statusCode == 0) System.exit(0);
  }
  @SuppressWarnings("unused")
  public void handleConnectionClose(Command closeCommand) {
    ShutdownSignalException sse =
        shutdown(closeCommand.getMethod(), false, null, _inConnectionNegotiation);
    try {
      _channel0.quiescingTransmit(new AMQP.Connection.CloseOk.Builder().build());
    } catch (IOException ignored) {
    } // ignore
    _brokerInitiatedShutdown = true;
    SocketCloseWait scw = new SocketCloseWait(sse);

    // if shutdown executor is configured, use it. Otherwise
    // execut socket close monitor the old fashioned way.
    // see rabbitmq/rabbitmq-java-client#91
    if (shutdownExecutor != null) {
      shutdownExecutor.execute(scw);
    } else {
      final String name =
          "RabbitMQ connection shutdown monitor " + getHostAddress() + ":" + getPort();
      Thread waiter = Environment.newThread(threadFactory, scw, name);
      waiter.start();
    }
  }
  /**
   * Start up the connection, including the MainLoop thread. Sends the protocol version negotiation
   * header, and runs through Connection.Start/.StartOk, Connection.Tune/.TuneOk, and then calls
   * Connection.Open and waits for the OpenOk. Sets heart-beat and frame max values after tuning has
   * taken place.
   *
   * @throws IOException if an error is encountered either before, or during, protocol negotiation;
   *     sub-classes {@link ProtocolVersionMismatchException} and {@link
   *     PossibleAuthenticationFailureException} will be thrown in the corresponding circumstances.
   *     {@link AuthenticationFailureException} will be thrown if the broker closes the connection
   *     with ACCESS_REFUSED. If an exception is thrown, connection resources allocated can all be
   *     garbage collected when the connection object is no longer referenced.
   */
  public void start() throws IOException, TimeoutException {
    initializeConsumerWorkService();
    initializeHeartbeatSender();
    this._running = true;
    // Make sure that the first thing we do is to send the header,
    // which should cause any socket errors to show up for us, rather
    // than risking them pop out in the MainLoop
    AMQChannel.SimpleBlockingRpcContinuation connStartBlocker =
        new AMQChannel.SimpleBlockingRpcContinuation();
    // We enqueue an RPC continuation here without sending an RPC
    // request, since the protocol specifies that after sending
    // the version negotiation header, the client (connection
    // initiator) is to wait for a connection.start method to
    // arrive.
    _channel0.enqueueRpc(connStartBlocker);
    try {
      // The following two lines are akin to AMQChannel's
      // transmit() method for this pseudo-RPC.
      _frameHandler.setTimeout(handshakeTimeout);
      _frameHandler.sendHeader();
    } catch (IOException ioe) {
      _frameHandler.close();
      throw ioe;
    }

    // start the main loop going
    MainLoop loop = new MainLoop();
    final String name = "AMQP Connection " + getHostAddress() + ":" + getPort();
    mainLoopThread = Environment.newThread(threadFactory, loop, name);
    mainLoopThread.start();
    // after this point clear-up of MainLoop is triggered by closing the frameHandler.

    AMQP.Connection.Start connStart;
    AMQP.Connection.Tune connTune = null;
    try {
      connStart =
          (AMQP.Connection.Start) connStartBlocker.getReply(handshakeTimeout / 2).getMethod();

      _serverProperties = Collections.unmodifiableMap(connStart.getServerProperties());

      Version serverVersion = new Version(connStart.getVersionMajor(), connStart.getVersionMinor());

      if (!Version.checkVersion(clientVersion, serverVersion)) {
        throw new ProtocolVersionMismatchException(clientVersion, serverVersion);
      }

      String[] mechanisms = connStart.getMechanisms().toString().split(" ");
      SaslMechanism sm = this.saslConfig.getSaslMechanism(mechanisms);
      if (sm == null) {
        throw new IOException(
            "No compatible authentication mechanism found - "
                + "server offered ["
                + connStart.getMechanisms()
                + "]");
      }

      LongString challenge = null;
      LongString response = sm.handleChallenge(null, this.username, this.password);

      do {
        Method method =
            (challenge == null)
                ? new AMQP.Connection.StartOk.Builder()
                    .clientProperties(_clientProperties)
                    .mechanism(sm.getName())
                    .response(response)
                    .build()
                : new AMQP.Connection.SecureOk.Builder().response(response).build();

        try {
          Method serverResponse = _channel0.rpc(method, handshakeTimeout / 2).getMethod();
          if (serverResponse instanceof AMQP.Connection.Tune) {
            connTune = (AMQP.Connection.Tune) serverResponse;
          } else {
            challenge = ((AMQP.Connection.Secure) serverResponse).getChallenge();
            response = sm.handleChallenge(challenge, this.username, this.password);
          }
        } catch (ShutdownSignalException e) {
          Method shutdownMethod = e.getReason();
          if (shutdownMethod instanceof AMQP.Connection.Close) {
            AMQP.Connection.Close shutdownClose = (AMQP.Connection.Close) shutdownMethod;
            if (shutdownClose.getReplyCode() == AMQP.ACCESS_REFUSED) {
              throw new AuthenticationFailureException(shutdownClose.getReplyText());
            }
          }
          throw new PossibleAuthenticationFailureException(e);
        }
      } while (connTune == null);
    } catch (TimeoutException te) {
      _frameHandler.close();
      throw te;
    } catch (ShutdownSignalException sse) {
      _frameHandler.close();
      throw AMQChannel.wrap(sse);
    } catch (IOException ioe) {
      _frameHandler.close();
      throw ioe;
    }

    try {
      int channelMax = negotiateChannelMax(this.requestedChannelMax, connTune.getChannelMax());
      _channelManager = instantiateChannelManager(channelMax, threadFactory);

      int frameMax = negotiatedMaxValue(this.requestedFrameMax, connTune.getFrameMax());
      this._frameMax = frameMax;

      int heartbeat = negotiatedMaxValue(this.requestedHeartbeat, connTune.getHeartbeat());

      setHeartbeat(heartbeat);

      _channel0.transmit(
          new AMQP.Connection.TuneOk.Builder()
              .channelMax(channelMax)
              .frameMax(frameMax)
              .heartbeat(heartbeat)
              .build());
      _channel0.exnWrappingRpc(
          new AMQP.Connection.Open.Builder().virtualHost(_virtualHost).build());
    } catch (IOException ioe) {
      _heartbeatSender.shutdown();
      _frameHandler.close();
      throw ioe;
    } catch (ShutdownSignalException sse) {
      _heartbeatSender.shutdown();
      _frameHandler.close();
      throw AMQChannel.wrap(sse);
    }

    // We can now respond to errors having finished tailoring the connection
    this._inConnectionNegotiation = false;
  }