/**
   * Verifies the response from server and calls appropriate callback method.
   *
   * @param publicKey public key associated with the developer account
   * @param responseCode server response code
   * @param signedData signed data from server
   * @param signature server signature
   */
  public void verify(PublicKey publicKey, int responseCode, String signedData, String signature) {
    String userId = null;
    // Skip signature check for unsuccessful requests
    ResponseData data = null;
    if (responseCode == LICENSED
        || responseCode == NOT_LICENSED
        || responseCode == LICENSED_OLD_KEY) {
      // Verify signature.
      try {
        Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
        sig.initVerify(publicKey);
        sig.update(signedData.getBytes());

        if (!sig.verify(Base64.decode(signature))) {
          Log.e(TAG, "Signature verification failed.");
          handleInvalidResponse();
          return;
        }
      } catch (NoSuchAlgorithmException e) {
        // This can't happen on an Android compatible device.
        throw new RuntimeException(e);
      } catch (InvalidKeyException e) {
        handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PUBLIC_KEY);
        return;
      } catch (SignatureException e) {
        throw new RuntimeException(e);
      } catch (Base64DecoderException e) {
        Log.e(TAG, "Could not Base64-decode signature.");
        handleInvalidResponse();
        return;
      }

      // Parse and validate response.
      try {
        data = ResponseData.parse(signedData);
      } catch (IllegalArgumentException e) {
        Log.e(TAG, "Could not parse response.");
        handleInvalidResponse();
        return;
      }

      if (data.responseCode != responseCode) {
        Log.e(TAG, "Response codes don't match.");
        handleInvalidResponse();
        return;
      }

      if (data.nonce != mNonce) {
        Log.e(TAG, "Nonce doesn't match.");
        handleInvalidResponse();
        return;
      }

      if (!data.packageName.equals(mPackageName)) {
        Log.e(TAG, "Package name doesn't match.");
        handleInvalidResponse();
        return;
      }

      if (!data.versionCode.equals(mVersionCode)) {
        Log.e(TAG, "Version codes don't match.");
        handleInvalidResponse();
        return;
      }

      // Application-specific user identifier.
      userId = data.userId;
      if (TextUtils.isEmpty(userId)) {
        Log.e(TAG, "User identifier is empty.");
        handleInvalidResponse();
        return;
      }
    }

    switch (responseCode) {
      case LICENSED:
      case LICENSED_OLD_KEY:
        int limiterResponse = mDeviceLimiter.isDeviceAllowed(userId, data);
        handleResponse(limiterResponse, data);
        break;
      case NOT_LICENSED:
        handleResponse(Policy.NOT_LICENSED, data);
        break;
      case ERROR_CONTACTING_SERVER:
        Log.w(TAG, "Error contacting licensing server.");
        handleResponse(Policy.RETRY, data);
        break;
      case ERROR_SERVER_FAILURE:
        Log.w(TAG, "An error has occurred on the licensing server.");
        handleResponse(Policy.RETRY, data);
        break;
      case ERROR_OVER_QUOTA:
        Log.w(TAG, "Licensing server is refusing to talk to this device, over quota.");
        handleResponse(Policy.RETRY, data);
        break;
      case ERROR_INVALID_PACKAGE_NAME:
        handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PACKAGE_NAME);
        break;
      case ERROR_NON_MATCHING_UID:
        handleApplicationError(LicenseCheckerCallback.ERROR_NON_MATCHING_UID);
        break;
      case ERROR_NOT_MARKET_MANAGED:
        handleApplicationError(LicenseCheckerCallback.ERROR_NOT_MARKET_MANAGED);
        break;
      default:
        Log.e(TAG, "Unknown response code for license check.");
        handleInvalidResponse();
    }
  }