Android ContentProvider openFile: need to serve "modified" file - java

I want to serve image files that saved on "external storage" via ContentProvider.
These image files are 'mangled' - first 50 bytes are XORed with some arbitary value. I want to do 'demangle' within ContentProvider so that other applications don't need to do special treatment.
I'm using Mininum SDK version 14.
Here is my first try - using piped ParcelFileDescriptor:
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// basic uri/mode check here
return openPipeHelper(uri, getType(uri), null, new FileInputStream(getImageFile(uri)), new PipeDataWriter<InputStream>() {
#Override
public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, final String mimeType, Bundle opts, InputStream input) {
InputStream fin = new FilterInputStream(input) {
private int cnt = 0;
private byte mask;
#Override
public int read() throws IOException {
byte[] buffer = new byte[1];
return read(buffer) == -1 ? -1 : (buffer[0] & 0xff);
}
#Override
public int read(#NonNull byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length);
}
#Override
public int read(#NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException {
int ret = super.read(buffer, byteOffset, byteCount);
if (ret <= 0) return ret;
if (cnt == 0) {
switch (mimeType) {
case "image/png":
mask = (byte) (buffer[byteOffset] ^ 137);
break;
case "image/webp":
mask = (byte) (buffer[byteOffset] ^ 'R');
break;
}
}
for (int i = byteOffset; i < byteOffset + ret && cnt < 50; i++, cnt++) {
buffer[i] ^= mask;
}
return ret;
}
};
OutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(output);
byte[] buf = new byte[1024 * 1024];
try {
while (true) {
int n = fin.read(buf);
if (n == -1) break;
Log.i(TAG, "openFile get n=" + n);
fout.write(buf, 0, n);
fout.flush();
}
} catch (IOException ex) {
// EPIPE likely means pipe closed on other end; treat it as WAI.
if (!ex.getMessage().contains("EPIPE")) {
Log.w(TAG, "openFile failed", ex);
}
} finally {
try {
fin.close();
} catch (IOException ex) {
Log.w(TAG, "openFile failed closing input", ex);
}
try {
fout.close();
} catch (IOException ex) {
Log.w(TAG, "openFile failed closing output", ex);
}
}
}
});
}
Result:
Works well with ImageView.setImageURI().
Don't work with android default Gallery (Intent.ACTION_VIEW with setDataAndType())
Works well with ES image viewer
It seems that Gallery don't like "piped stream".
Here is second try - read whole file, demangle, and serve as ParcelFileDescriptor.fromData():
File file = getImageFile(uri);
byte[] buffer = readFully(file);
String mimeType = getType(uri);
byte mask;
switch (mimeType) {
case "image/png":
mask = (byte) (buffer[0] ^ 137);
break;
case "image/webp":
mask = (byte) (buffer[0] ^ 'R');
break;
default:
mask = 0;
break;
}
for (int i = 0; i < 50; i++) buffer[i] ^= mask;
return (ParcelFileDescriptor) ParcelFileDescriptor.class.getMethod("fromData", byte[].class, String.class).invoke(null, buffer, getImageFile(uri).getName());
Result:
Don't work well with ImageView.setImageURI().
Works well with android default Gallery
Works well with ES image viewer
It seems that from time to time, MemoryFile made in ParcelFileDescriptor.fromData() is closed and disposed before ImageView.setImageURI() get data.
Here is third try - write demangled image to temporary file:
// buffer contains readFully and demangled image binary
try {
File tmpFile = File.createTempFile("image", getImageExtension(uri));
OutputStream os = new FileOutputStream(tmpFile);
try {
os.write(buffer);
} finally {
try {
os.close();
} catch (IOException ex2) {
Log.w(TAG, "openFile(): closing failed", ex2);
}
}
return ParcelFileDescriptor.open(tmpFile, ParcelFileDescriptor.MODE_READ_ONLY);
} catch (IOException ex2) {
Log.e(TAG, "openFile(): writing failed", ex2);
return null;
}
Result:
Works well with ImageView.setImageURI().
Works well with android default Gallery
Works well with ES image viewer
However, I don't like this solution, as it is very hard to determine when I could delete temporary files.
These 3 solutions have their flaws, and I couldn't find flawless solution. What is the "right" way to do such things?

Related

Android - Audio Clipping when recording audio (crest/peak clipping and periodic 0 bit values in between)

I am trying to record an audio stream via a Bluetooth device. I am using Bluetooth SCO for getting Bluetooth audio and AudioRecord class to record audio.
I am recording RAW .PCM files with MONO Channel with a sampling rate of 16000
I am calculating BufferSize like this
private static final int BUFFER_SIZE_FACTOR = 2;
private static final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLING_RATE_IN_HZ,CHANNEL_CONFIG, AUDIO_FORMAT) * BUFFER_SIZE_FACTOR;
This is how I am getting/writing audio currently,
private class RecordingRunnable implements Runnable {
#Override
public void run() {
setFileNameAndPath();
final ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
try (final FileOutputStream outStream = new FileOutputStream(mFilePath)) {
while (recordingInProgress.get()) {
int result = recorder.read(buffer, BUFFER_SIZE);
if (result < 0) {
throw new RuntimeException("Reading of audio buffer failed: " +
getBufferReadFailureReason(result));
}
outStream.write(buffer.array(), 0, BUFFER_SIZE);
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Writing of recorded audio failed", e);
}
}
I did a little research and found that the clipping effect could be because of the wrong Byte order (LITTLE_ENDIAN or BIG_ENDIAN) or Because of poor multithreading. However in this current implementation, I am not able to understand how bytes are being ordered and saved & what can I do to fix the clipping/noise problem.
I am starting my recorder runnable like this
recordingThread = new Thread(new RecordingRunnable(), "Recording Thread");
recordingThread.start();
recordingThread.setPriority(Thread.MAX_PRIORITY);
I got same issue and I resolved this problem with below code.
private byte[] short2byte(short[] sData, int size) {
int shortArrsize = size;
byte[] bytes = new byte[shortArrsize * 2];
for (int i = 0; i < shortArrsize; i++) {
bytes[i * 2] = (byte) (sData[i] & 0x00FF);
bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
sData[i] = 0;
}
return bytes;
}
......
int bufferSize = AudioRecord.getMinBufferSize(48000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
short[] buffer = new short[bufferSize];
int source = MediaRecorder.AudioSource.VOICE_RECOGNITION;
mAudioRecorder = new AudioRecord(source, 48000,
AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
int state = mAudioRecorder.getState();
if (state != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Can not support");
return;
}
mAudioRecorder.startRecording();
while (mIsRecording) {
int bufferReadResult = mAudioRecorder.read(buffer, 0, bufferSize);
if (bufferReadResult < 0) {
continue;
}
try {
byte data[] = short2byte(buffer, bufferReadResult);
fos.write(data, 0, bufferReadResult * 2);
} catch (IOException e) {
e.printStackTrace();
}
}

Timing requirements to avoid "Unexpected end of ZLIB input stream"

I have a log analyzing tool that needs to grab *.gz files from Linux servers and unzip them on both Linux and Windows clients. I am getting "Unexpected end of ZLIB input stream" in many instances, which I assume is a difference in detail in the files on Linux and Windows.
Below is my function. It's pretty basic. How do I improved it to prevent the EOF error?
The "in" symbol is a FileInputStream that is created when constructing the class that this function is part of.
public void unzip(File fileTo) throws IOException {
OutputStream out = new FileOutputStream(fileTo);
LOGGER.info("Setting up the file for outputstream : "+fileTo);
try {
in = new GZIPInputStream(in);
byte[] buffer = new byte[65536];
int noRead;
while ((noRead = in.read(buffer)) != -1) {
out.write(buffer, 0, noRead);
}
} finally {
try { out.close(); } catch (Exception e) {}
}
}
I changed from the above to this and now it works. It seems that it was trying to load the output stream before it was done loading the input stream.
public void unzip(File fileTo, String f) throws IOException,
EOFException, InterruptedException {
LOGGER.info("Setting up the file for outputstream : "+fileTo);
GZIPInputStream cIn = new GZIPInputStream(new FileInputStream(f));
OutputStream out = new FileOutputStream(fileTo);
fileTo.setReadable(true, false);
fileTo.setWritable(true, false);
byte[] buffer = new byte[65536];
int noRead;
for (int i = 10; i > 0 && cIn.available() == 1; i--) {
Thread.sleep(1000);
}
try {
while ((noRead = cIn.read(buffer)) != -1) {
out.write(buffer, 0, noRead);
}
} finally {
try { out.close();cIn.close();in.close(); } catch (Exception e) {}
}
}

Turning byte array into Bitmap

I have a server in cpp and a client in Java where I am sending an image from the server to the client in segments. On the client side I pick up all the segment to form a single array of bytes.
My problem is that I don't seem to be able to successfully create a bitmap out of this byte array, since I get null using BitmapFactory.decodeByteArray()
Client code:
byte[] response_bytes;
private Bitmap receive_image ( final String protocol, final int image_size, final int buffer_size)
{
if (image_size <= 0 || buffer_size <= 0)
return null;
Thread image_receiver = new Thread(new Runnable() {
#Override
public void run() {
ByteBuffer byteBuffer = ByteBuffer.allocate(image_size);
byte[] buffer = new byte[buffer_size];
int bytesReadSum = 0;
try {
while (bytesReadSum != image_size) {
activeReader.read(buffer);
String message = new String(buffer);
if (TextUtils.substring(message, 0, len_of_protocol_number).equals(protocol)) {
int bytesToRead = Integer.parseInt(TextUtils.substring(message,
len_of_protocol_number,
len_of_protocol_number + len_of_data_len));
byteBuffer.put(Arrays.copyOfRange(buffer,
len_of_protocol_number + len_of_data_len,
bytesToRead + len_of_protocol_number + len_of_data_len));
bytesReadSum += bytesToRead;
} else {
response_bytes = null;
break;
}
}
if (bytesReadSum == image_size) {
byte[] image_bytes = byteBuffer.array();
if (image_bytes.length > 0)
response_bytes = image_bytes;
else
response_bytes = null;
}
} catch (IOException e) {
response_bytes = null;
}
}
});
image_receiver.start();
try {
image_receiver.join();
} catch (InterruptedException e) {
response_bytes = null;
}
if (response_bytes != null) { //Here image_bitmap is null
Bitmap image_bitmap = BitmapFactory.decodeByteArray(response_bytes, 0, response_bytes.length);
return image_bitmap;
} else {
return null;
}
At this point I'd also like to mention that the image bytes reading seems to work fine and I manage to read all the bytes of the image without fail.
Yet, I'm unable to turn that array of bytes into a bitmap.

Read file of objects and write it back to a new one a single character at a time

How to read an entire record from a txt file, get each field separately and convert each field into a separate character stream. Then write the character streams of individual characters (in a loop) to a plain ASCII output text file.
I have my class definition, I just cannot seem to write the output file properly which has to be one individual plain ascii text character at a time. I just need a little help. Here is what I have so far:
----- This is my first question guys. Sorry if it isn't formatted well :( I'm trying to covert a file of objects to a plain ASCII character text file which i called "yankees.txt" I read it in with the ObjectInputStream then I'm supposed to get each field separately and convert each field into a seperate character stream, and write the characters one character at a time from each field to my "yankees.txt"
public class yankeesfilemain {
public static void main(String[] args) throws EOFException {
ObjectInputStream is;
OutputStream os;
yankees y;
int i, j, k;
String name, pos;
int number;
File fout;
try {
is = new ObjectInputStream(new
FileInputStream("yankees.yanks"));
y = (yankees)is.readObject();
fout = new File("yankees.txt");
os = new FileOutputStream(fout);
while (y != null) {
name = y.getname();
pos = y.getpos();
number = y.getnum();
for (i = 0; i < .length(); i++) {}
for (j = 0; j < .length(); j++) {
pos = y.getpos();
}
for (k = 0; k < .length(); k++) {
number = y.getnum();
}
break;
}
os.close();
is.close();
} catch(EOFException eof) {
eof.printStackTrace();
System.exit(0);
} catch(NullPointerException npe) {
npe.printStackTrace();
System.exit(0);
} catch(NumberFormatException nfe) {
nfe.printStackTrace();
System.exit(0);
} catch(IOException e) {
e.printStackTrace();
System.exit(0);
}
}
}
Please refer to the following code
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream("C:\\11.txt");
OutputStream out = new FileOutputStream("C:\\12.txt", true);
try {
byte[] buffer = new byte[1024];
while (true) {
int byteRead = in.read(buffer);
if (byteRead == -1)
break;
out.write(buffer, 0, byteRead);
}
}
catch (MalformedURLException ex) {
System.err.println(args[0] + " is not a URL Java understands.");
} finally {
if (in != null)
in.close();
if (out != null) {
out.close();
}
}
}

DataOutputStream writing too much

What I currently have
I'm currently trying to create a little download manager in Java and I have a problem with writing the loaded bytes in a file. I'm using a DataOutputStream to write the byte-array which I read from a DataInputStream. Here is the class I created to do that:
public class DownloadThread extends Thread{
private String url_s;
private File datei;
public DownloadThread(String url_s, File datei){
this.url_s = url_s;
this.datei = datei;
}
public void run(){
// Connect:
int size = 0;
URLConnection con = null;
try {
URL url = new URL(url_s);
con = url.openConnection();
size = con.getContentLength();
// Set Min and Max of the JProgressBar
prog.setMinimum(0);
prog.setMaximum(size);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// Download:
if (con != null || size != 0){
byte[] buffer = new byte[4096];
DataInputStream down_reader = null;
// Output:
DataOutputStream out = null;
try {
out = new DataOutputStream(new FileOutputStream(datei));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
// Load:
try {
down_reader = new DataInputStream(con.getInputStream());
int byte_counter = 0;
int tmp = 0;
int progress = 0;
// Read:
while (true){
tmp = down_reader.read(buffer);
// Check for EOF
if (tmp == -1){
break;
}
out.write(buffer);
out.flush();
// Set Progress:
byte_counter += tmp;
progress = (byte_counter * 100) / size;
prog.setValue( byte_counter );
prog.setString(progress+"% - "+byte_counter+"/"+size+" Bytes");
}
// Check Filesize:
prog.setString("Checking Integrity...");
if (size == out.size()){
prog.setString("Integrity Check passed!");
} else {
prog.setString("Integrity Check failed!");
System.out.println("Size: "+size+"\n"+
"Read: "+byte_counter+"\n"+
"Written: "+out.size() );
}
// ENDE
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
out.close();
down_reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// Clean Up...
load.setEnabled(true);
try {
this.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
This is currently an inner-class and the prog-Object is a JProgressBar from it's mother-class, so it can be accessed directly.
Example
I'm trying to download the Windows .exe Version of the TinyCC, which should be 281KB size. The file i downloaded with my download manager is 376KB big.
The Output from the Script looks like this:
Size: 287181
Read: 287181
Written: 385024
So it seems that the read bytes match the file-size but there are more bytes written. What am I missing here?
This is wrong:
out.write(buffer);
It should be
out.write(buffer, 0, tmp);
You need to specify how many bytes to write, a read doesn't always read a full buffer.
Memorize this. It is the canonical way to copy a stream in Java.
int count;
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}

Categories

Resources