Android Code to Flash Binary in Eclipse

I’m trying to add the ability to push an OTA firmware update from a local binary from my app (Spark Pixels)… The app is developed in Eclipse. I’m trying to use a BufferedInputStream, but my syntax isn’t quite right. The JSON response from Particle reports error: “Nothing to do?” Any help would be appreciated.

Here’s my code:

    Response putFirmware(URL url, String stringData) {
	HttpURLConnection connection = okHttpClient.open(url);
	OutputStream out = null;
	InputStream in = null;
	FileInputStream streamFileInputStream;
	BufferedInputStream streamFileBufferedInputStream;
	int responseCode = -1;
	String responseData = "";

	try {
		try {
			String firmwareFile = "/data/data/kc.spark.pixels.android/files/data/spark_pixels_firmware/spark_pixels.bin";
			streamFileInputStream = new FileInputStream(firmwareFile);
			streamFileBufferedInputStream = new BufferedInputStream(streamFileInputStream);

			byte[] streamFileBytes = new byte[firmwareFile.length()];
			int bytesRead = 0;
			int totalBytesRead = 0;
			
			connection.setRequestMethod("PUT");
			connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			connection.setDoOutput(true);
			connection.connect();

			out = connection.getOutputStream();
			String firstPart = "access_token=<TOKEN>&file_type=binary&file=;
			out.write(getStringDataFirstPart(stringData).getBytes(HTTP.UTF_8));
			
			while ((bytesRead = streamFileBufferedInputStream.read(streamFileBytes)) > 0) {
				out.write(streamFileBytes, 0, bytesRead);
				out.flush();

			    totalBytesRead += bytesRead;
			}
			
			out.close();

			responseCode = connection.getResponseCode();

			in = connection.getInputStream();
			responseData = readAsUtf8String(in);

		} finally {
			// Clean up.
			if (out != null) {
				out.close();
			}
			if (in != null) {
				in.close();
			}
		}
	} catch (IOException e) {
		log.e("Error trying to make " + connection.getRequestMethod() + " request");
	}

	return new Response(responseCode, responseData);
    }

Thanks,
Kevin

I’m going to ping our resident mobile expert, @ido, although he might not be able to reply immediately.

1 Like

I don’t have an actual Android solution, but I can give you a few hints:

The PUT data for uploading a binary is in multipart/form-data format, not application/x-www-form-urlencoded.

https://docs.particle.io/reference/api/#flash-a-device-with-a-pre-compiled-binary

The Content-Type might look something like:

Content-Type: multipart/form-data; boundary="UAtw2vCXbUb5CYav"

And then the PUT data body would be something like:

--UAtw2vCXbUb5CYav
Content-Disposition: form-data; name="file_type"

binary
--UAtw2vCXbUb5CYav
Content-Disposition: form-data; name="file"
Content-Type: application/octet-stream
Content-Length: 4000

<<binary data goes here>
--UAtw2vCXbUb5CYav--

However, you probably shouldn’t try to build that by hand. This example uses Apache HTTP Components; you can substitute your preferred library:

MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setBoundary(multipartBoundary);
    	    	
builder.addTextBody("file_type", "binary");
builder.addBinaryBody("file", binFile);
HttpEntity bodyEntity = builder.build();

Rick

3 Likes

Hi Rick,

Thanks for your response. I’ll look into trying your suggestions. I did find some other sample code segments from stackoverflow from which I put the following together. However, I see I was a little hasty and didn’t get the bin file setup properly.

Response putFirmware2(URL url, String stringData) {
	String param = "value";
	String charset = "UTF-8";
	InputStream in = null;
	int responseCode = -1;
	String responseData = "";
	String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
	String CRLF = "\r\n"; // Line separator required by multipart/form-data.
	HttpURLConnection connection = okHttpClient.open(url);
	BufferedInputStream streamFileBufferedInputStream;
	String firmwareFile = "/data/data/kc.spark.pixels.android/files/data/spark_pixels_firmware/spark_pixels.bin";
	FileInputStream streamFileInputStream = null;
	int bytesRead = 0;
	int totalBytesRead = 0;
	byte[] streamFileBytes = new byte[firmwareFile.length()];
	OutputStream output = null;
	PrintWriter writer = null;
	connection.setDoOutput(true);
	connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
	try {
	try{
		streamFileInputStream = new FileInputStream(firmwareFile);
		streamFileBufferedInputStream = new BufferedInputStream(streamFileInputStream);
	    output = connection.getOutputStream();
	    writer = new PrintWriter(new OutputStreamWriter(output, charset), true);

	    // Send normal param.
	    writer.append("--" + boundary).append(CRLF);
	    writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
	    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
	    writer.append(CRLF).append(param).append(CRLF).flush();

	    // Send binary file.
	    final String fileName = "spark_pixels.bin";
        writer.append("--").append(boundary).append(CRLF)
                .append("Content-Disposition: form-data; name=\"")
                .append(fileName).append("\"; filename=\"").append(fileName).append("\"").append(CRLF)
                .append("Content-Type: ").append("application/octet-stream").append(CRLF)
                .append("Content-Transfer-Encoding: binary").append(CRLF)
                .append(CRLF);
	    
	    output.flush(); // Important before continuing with writer!
	    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

	    // End of multipart/form-data.
	    writer.append("--" + boundary + "--").append(CRLF).flush();
	    writer.close();
	    responseCode = connection.getResponseCode();
	    in = connection.getInputStream();
		responseData = readAsUtf8String(in);
		
	} finally {
		// Clean up.
		if (output != null) {
			output.close();
		}
		if (in != null) {
			in.close();
		}
	}
	} catch (IOException e) {
		log.e("Error trying to make " + connection.getRequestMethod() + " request");
	}

return new Response(responseCode, responseData);
}

I’ll have another go at this routine to see if I can get it to work. Thanks again for pointing me in the right direction.

-Kevin

1 Like

I didn’t go over it that closely, but I’m pretty sure you’re missing a few lines of code before output.flush(). You need to read the contents of spark_pixels.bin and write them to output, then flush. Other than that, it looks pretty close.