Android: Java Class to Save Bluetooth Receiving File Info

Receiving File Info Storage in Android Bluetooth Application

This below Java class in Android Bluetooth Application Development is to store information about a single receiving file. It will only be used for inbounds share, e.g. receive a file to determine a correct save filename

Java Class to Store Bluetooth Receiving File Information


/*
* Copyright (c) 2008-2009, Motorola, Inc.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the Motorola, Inc. nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

package com.android.bluetooth.opp;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.StatFs;
import android.os.SystemClock;
import android.util.Log;

/**
* This class stores information about a single receiving file. It will only be
* used for inbounds share, e.g. receive a file to determine a correct save file
* name
*/
public class BluetoothOppReceiveFileInfo {
private static final boolean D = Constants.DEBUG;
private static final boolean V = Constants.VERBOSE;
private static String sDesiredStoragePath = null;

/** absolute store file name */
public String mFileName;

public long mLength;

public FileOutputStream mOutputStream;

public int mStatus;

public String mData;

public BluetoothOppReceiveFileInfo(String data, long length,
int status) {
mData = data;
mStatus = status;
mLength = length;
}

public BluetoothOppReceiveFileInfo(String filename, long length,
FileOutputStream outputStream, int status) {
mFileName = filename;
mOutputStream = outputStream;
mStatus = status;
mLength = length;
}

public BluetoothOppReceiveFileInfo(int status) {
this (null, 0, null, status);
}

// public static final int BATCH_STATUS_CANCELED = 4;
public static BluetoothOppReceiveFileInfo generateFileInfo(
Context context, int id) {

ContentResolver contentResolver = context.getContentResolver();
Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/"
+ id);
String filename = null, hint = null;
long length = 0;
Cursor metadataCursor = contentResolver.query(contentUri,
new String[] { BluetoothShare.FILENAME_HINT,
BluetoothShare.TOTAL_BYTES,
BluetoothShare.MIMETYPE }, null, null, null);
if (metadataCursor != null) {
try {
if (metadataCursor.moveToFirst()) {
hint = metadataCursor.getString(0);
length = metadataCursor.getInt(1);
}
} finally {
metadataCursor.close();
}
}

File base = null;
StatFs stat = null;

if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
String root = Environment.getExternalStorageDirectory()
.getPath();
base = new File(root + Constants.DEFAULT_STORE_SUBDIR);
if (!base.isDirectory() && !base.mkdir()) {
if (D)
Log.d(Constants.TAG,
"Receive File aborted - can't create base directory "
+ base.getPath());
return new BluetoothOppReceiveFileInfo(
BluetoothShare.STATUS_FILE_ERROR);
}
stat = new StatFs(base.getPath());
} else {
if (D)
Log.d(Constants.TAG,
"Receive File aborted - no external storage");
return new BluetoothOppReceiveFileInfo(
BluetoothShare.STATUS_ERROR_NO_SDCARD);
}

/*
* Check whether there's enough space on the target filesystem to save
* the file. Put a bit of margin (in case creating the file grows the
* system by a few blocks).
*/
if (stat.getBlockSize()
* ((long) stat.getAvailableBlocks() - 4) < length) {
if (D)
Log.d(Constants.TAG,
"Receive File aborted - not enough free space");
return new BluetoothOppReceiveFileInfo(
BluetoothShare.STATUS_ERROR_SDCARD_FULL);
}

filename = choosefilename(hint);
if (filename == null) {
// should not happen. It must be pre-rejected
return new BluetoothOppReceiveFileInfo(
BluetoothShare.STATUS_FILE_ERROR);
}
String extension = null;
int dotIndex = filename.lastIndexOf(".");
if (dotIndex < 0) {
// should not happen. It must be pre-rejected
return new BluetoothOppReceiveFileInfo(
BluetoothShare.STATUS_FILE_ERROR);
} else {
extension = filename.substring(dotIndex);
filename = filename.substring(0, dotIndex);
}
filename = base.getPath() + File.separator + filename;
// Generate a unique filename, create the file, return it.
String fullfilename = chooseUniquefilename(filename, extension);

if (!safeCanonicalPath(fullfilename)) {
// If this second check fails, then we better reject the transfer
return new BluetoothOppReceiveFileInfo(
BluetoothShare.STATUS_FILE_ERROR);
}
if (V)
Log.v(Constants.TAG, "Generated received filename "
+ fullfilename);

if (fullfilename != null) {
try {
new FileOutputStream(fullfilename).close();
int index = fullfilename.lastIndexOf('/') + 1;
// update display name
if (index > 0) {
String displayName = fullfilename.substring(index);
if (V)
Log.v(Constants.TAG, "New display name "
+ displayName);
ContentValues updateValues = new ContentValues();
updateValues.put(BluetoothShare.FILENAME_HINT,
displayName);
context.getContentResolver().update(contentUri,
updateValues, null, null);

}
return new BluetoothOppReceiveFileInfo(fullfilename,
length, new FileOutputStream(fullfilename), 0);
} catch (IOException e) {
if (D)
Log.e(Constants.TAG, "Error when creating file "
+ fullfilename);
return new BluetoothOppReceiveFileInfo(
BluetoothShare.STATUS_FILE_ERROR);
}
} else {
return new BluetoothOppReceiveFileInfo(
BluetoothShare.STATUS_FILE_ERROR);
}

}

private static boolean safeCanonicalPath(String uniqueFileName) {
try {
File receiveFile = new File(uniqueFileName);
if (sDesiredStoragePath == null) {
sDesiredStoragePath = Environment
.getExternalStorageDirectory().getPath()
+ Constants.DEFAULT_STORE_SUBDIR;
}
String canonicalPath = receiveFile.getCanonicalPath();

// Check if canonical path is complete - case sensitive-wise
if (!canonicalPath.startsWith(sDesiredStoragePath)) {
return false;
}

return true;
} catch (IOException ioe) {
// If an exception is thrown, there might be something wrong with the file.
return false;
}
}

private static String chooseUniquefilename(String filename,
String extension) {
String fullfilename = filename + extension;
if (!new File(fullfilename).exists()) {
return fullfilename;
}
filename = filename + Constants.filename_SEQUENCE_SEPARATOR;
/*
* This number is used to generate partially randomized filenames to
* avoid collisions. It starts at 1. The next 9 iterations increment it
* by 1 at a time (up to 10). The next 9 iterations increment it by 1 to
* 10 (random) at a time. The next 9 iterations increment it by 1 to 100
* (random) at a time. ... Up to the point where it increases by
* 100000000 at a time. (the maximum value that can be reached is
* 1000000000) As soon as a number is reached that generates a filename
* that doesn't exist, that filename is used. If the filename coming in
* is [base].[ext], the generated filenames are [base]-[sequence].[ext].
*/
Random rnd = new Random(SystemClock.uptimeMillis());
int sequence = 1;
for (int magnitude = 1; magnitude < 1000000000; magnitude *= 10) {
for (int iteration = 0; iteration < 9; ++iteration) {
fullfilename = filename + sequence + extension;
if (!new File(fullfilename).exists()) {
return fullfilename;
}
if (V)
Log.v(Constants.TAG, "file with sequence number "
+ sequence + " exists");
sequence += rnd.nextInt(magnitude) + 1;
}
}
return null;
}

private static String choosefilename(String hint) {
String filename = null;

// First, try to use the hint from the application, if there's one
if (filename == null && !(hint == null) && !hint.endsWith("/")
&& !hint.endsWith("\\")) {
// Prevent abuse of path backslashes by converting all backlashes '\\' chars
// to UNIX-style forward-slashes '/'
hint = hint.replace('\\', '/');
// Convert all whitespace characters to spaces.
hint = hint.replaceAll("\\s", " ");
// Replace illegal fat filesystem characters from the
// filename hint i.e. :"<>*?| with something safe.
hint = hint.replaceAll("[:\"<>*?|]", "_");
if (V)
Log.v(Constants.TAG, "getting filename from hint");
int index = hint.lastIndexOf('/') + 1;
if (index > 0) {
filename = hint.substring(index);
} else {
filename = hint;
}
}
return filename;
}
}


Related Topic Android: Java Class for Patch Files Sending and Receiving
Android: Java Class for Bluetooth OBEX Server Session Data Transfer
Android: Java Class to Store Bluetooth Sent File Info
How to Save Android File Transfer Details
Android's Bluetooth Inbound and Outbound Transfer

nScraps.com 2011   Privacy Policy  Terms of Service  Feedback