Logging on file android - android

I would like to use something like log4j to log on files. I can see the logback project but I can't understand how to use a FileAppender programmatically.
Is There a simple way to log on files?

To write Logs on SD Card i am using below class.
You can try or modify as per your need ,
public class Log {
/** you can use application storage just change below line to your like getFile or package name etc.. **/
private static String enable_path=Environment.getExternalStorageDirectory().toString() + "/your foder name";
private static File logFolderPath = new File(enable_path);
private static boolean isLogEnabled(){
return logFolderPath.exists();
}
public static void e(String tag, String msg) {
if(isLogEnabled()){
android.util.Log.e(tag, msg);
put('E', tag, msg, null);
}
}
public static void e(String tag, String msg, Throwable thr) {
if(isLogEnabled()){
android.util.Log.e(tag, msg, thr);
put('E', tag, msg + ": " + thr.getMessage(), thr);
}
}
private static final File LOG_FILE = new File(logFolderPath, "Logs.txt");
private static FileWriter fw;
private static final SimpleDateFormat sdf = new SimpleDateFormat(" yyyy-MM-dd HH:mm:ss.SSS ", Locale.US);
private static long lastMs, lastNs;
private synchronized static void put(char level, String tag, String msg,Throwable thr) {
try {
if(!LOG_FILE.exists())
LOG_FILE.createNewFile();
/* if (fw == null)*/ {
fw = new FileWriter(LOG_FILE, true); // true: append
}
Date d = new Date();
long nowMs = d.getTime();
long nowNs = System.nanoTime();
if (lastMs == 0)
lastMs = nowMs;
if (lastNs == 0)
lastNs = nowNs;
fw.write(level);
fw.write(sdf.format(d));
fw.write(Long.toString(nowMs - lastMs));
fw.write(' ');
fw.write(Long.toString(nowNs));
fw.write(' ');
fw.write(Double.toString((nowNs - lastNs) / 1e6));
fw.write(' ');
fw.write(tag);
fw.write(' ');
fw.write(msg);
fw.write('\n');
if (thr != null)
thr.printStackTrace(new PrintWriter(fw));
fw.flush();
lastMs = nowMs;
lastNs = nowNs;
}
catch (IOException ex) {
android.util.Log.e(tag, "IOException", ex);
}
}
}

To programmatically create a FileAppender in logback-android, use the following code:
//import org.slf4j.LoggerFactory;
//import org.slf4j.Logger;
//import ch.qos.logback.core.FileAppender;
//import ch.qos.logback.core.util.StatusPrinter;
//import ch.qos.logback.classic.LoggerContext;
//import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
//import ch.qos.logback.classic.Level;
//import ch.qos.logback.classic.spi.ILoggingEvent;
private void configureFileAppender() {
// reset the default context (which may already have been initialized)
// since we want to reconfigure it
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
context.reset();
final String LOG_DIR = getApplicationInfo().dataDir + "/logs";
FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
fileAppender.setAppend(true);
fileAppender.setContext(context);
fileAppender.setFile(LOG_DIR + "/log.txt");
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern("%logger{35} - %msg%n");
encoder.setContext(context);
encoder.start();
fileAppender.setEncoder(encoder);
fileAppender.start();
// add the newly created appender to the root logger;
// qualify Logger to disambiguate from org.slf4j.Logger
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.TRACE);
root.addAppender(fileAppender);
// DEBUG: print any status messages (warnings, etc) encountered in logback config
//StatusPrinter.print(context);
}
You might want to consider using a RollingFileAppender instead to avoid filling up your disk. See example from the logback-android Wiki.

Related

ThreadpoolExecutor data getting mixed up

I am using android's thread pool executor framework (initialized as below).
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
ExecutorService executorService = new ThreadPoolExecutor(totalCores, totalCores * 3, 10, TimeUnit.SECONDS, taskQueue);
Now, consider the following function onFrameProcessed -
public void onFrameProcessed(RenderedImage renderedImage) {
String timeNow = new SimpleDateFormat("d-M-Y_HH_mm_ss_SSS").format(new Date()).toString();
CustomRunnable3 customRunnable3 = new CustomRunnable3(renderedImage, timeNow);
executorService.execute(customRunnable3);
}
Definition of CustomRunnable3 is as follows:
class CustomRunnable3 implements Runnable {
RenderedImage renderedImageLocal;
String basePath, timeNowCopy;
int hashCode;
CustomRunnable3(RenderedImage renderedImage, String timeNow) {
renderedImageLocal = renderedImage;
this.basePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
this.timeNowCopy = timeNow;
hashCode = renderedImageLocal.hashCode();
}
#Override
public void run() {
if (renderedImageLocal.imageType() == RenderedImage.ImageType.ThermalRadiometricKelvinImage) {
int[] thermalData = renderedImageLocal.thermalPixelValues();
String dataPath = basePath + "/" + this.timeNowCopy + ".csv";
try {
PrintWriter printWriter = new PrintWriter(dataPath);
int dataLen = thermalData.length;
for (int i = 0; i < dataLen; i++) {
printWriter.println(thermalData[i]);
}
printWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
String imgPath = basePath + "/" + this.timeNowCopy + ".jpg";
try {
if (hashCode != renderedImageLocal.hashCode()) {
Log.e("Checking", "Hash code changed..");
}
renderedImageLocal.getFrame().save(new File(imgPath), frameProcessor);
if (hashCode != renderedImageLocal.hashCode()) {
Log.e("Checking", "Hash code changed after writing..");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Usage Scenario : onFrameReceived is being called multiple times per second(like 4-5 times). In each call to onFrameReceived, I am saving two files from renderedImage object (1 csv file, 1 jpg file). Both of these files must be related to each other because both are created from one parent and have same name(except the extension).
Problem : But that is not happening and somehow I am ending up with jpg file content from 1 renderedImage and csv content from another renderedImage object.
What are the possible reasons for this problem, please share your opinion.

How can i add custom header fields while uploading file into Amazon s3

I need to store multimedia files in Amazons3.
I used the following code for uploading a the file.
Method 1:
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.StrictMode;
import android.util.Log;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.AbortMultipartUploadRequest;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest;
import com.amazonaws.services.s3.model.CompleteMultipartUploadResult;
import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
import com.amazonaws.services.s3.model.InitiateMultipartUploadResult;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PartETag;
import com.amazonaws.services.s3.model.ProgressEvent;
import com.amazonaws.services.s3.model.ProgressListener;
import com.amazonaws.services.s3.model.UploadPartRequest;
import com.amazonaws.services.s3.model.UploadPartResult;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class AmazonUploader {
private static final long MIN_DEFAULT_PART_SIZE = 5 * 1024 * 1024;
private static final String TAG = "AmazonUploader";
private static final String PREFS_NAME = "preferences_simpl3r";
private static final String PREFS_UPLOAD_ID = "_uploadId";
private static final String PREFS_ETAGS = "_etags";
private static final String PREFS_ETAG_SEP = "~~";
private AmazonS3Client s3Client;
private String s3bucketName;
private String s3key;
private File file;
private SharedPreferences prefs;
private long partSize = MIN_DEFAULT_PART_SIZE;
private UploadProgressListener progressListener;
private long bytesUploaded = 0;
private boolean userInterrupted = false;
private boolean userAborted = false;
public AmazonUploader(Context context, AmazonS3Client s3Client, String s3bucketName, String s3key, File file) {
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
this.s3Client = s3Client;
this.s3key = s3key;
this.s3bucketName = s3bucketName;
this.file = file;
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}
/**
* Initiate a multipart file upload to Amazon S3
*
* #return the URL of a successfully uploaded file
*/
public String start() {
// initialize
List<PartETag> partETags = new ArrayList<PartETag>();
final long contentLength = file.length();
long filePosition = 0;
int startPartNumber = 1;
userInterrupted = false;
userAborted = false;
bytesUploaded = 0;
// check if we can resume an incomplete download
String uploadId = getCachedUploadId();
if (uploadId != null) {
// we can resume the download
Log.i(TAG, "resuming upload for " + uploadId);
// get the cached etags
List<PartETag> cachedEtags = getCachedPartEtags();
partETags.addAll(cachedEtags);
// calculate the start position for resume
startPartNumber = cachedEtags.size() + 1;
filePosition = (startPartNumber - 1) * partSize;
bytesUploaded = filePosition;
Log.i(TAG, "resuming at part " + startPartNumber + " position " + filePosition);
} else {
// initiate a new multi part upload
Log.i(TAG, "initiating new upload");
InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(s3bucketName, s3key);
// ObjectMetadata obj = new ObjectMetadata();
// obj.setContentType("image/jpeg");
// obj.setHeader(Constants.APP_HEADER_REFERER, Constants.APP_REFERER_URL);
// initRequest.setObjectMetadata(obj);
configureInitiateRequest(initRequest);
InitiateMultipartUploadResult initResponse = s3Client.initiateMultipartUpload(initRequest);
uploadId = initResponse.getUploadId();
}
final AbortMultipartUploadRequest abortRequest = new AbortMultipartUploadRequest(s3bucketName, s3key, uploadId);
for (int k = startPartNumber; filePosition < contentLength; k++) {
long thisPartSize = Math.min(partSize, (contentLength - filePosition));
Log.i(TAG, "starting file part " + k + " with size " + thisPartSize);
UploadPartRequest uploadRequest = new UploadPartRequest().withBucketName(s3bucketName)
.withKey(s3key).withUploadId(uploadId)
.withPartNumber(k).withFileOffset(filePosition).withFile(file)
.withPartSize(thisPartSize);
ProgressListener s3progressListener = new ProgressListener() {
public void progressChanged(ProgressEvent progressEvent) {
// bail out if user cancelled
// TODO calling shutdown too brute force?
if (userInterrupted) {
s3Client.shutdown();
throw new UploadIterruptedException("User interrupted");
} else if (userAborted) {
// aborted requests cannot be resumed, so clear any cached etags
clearProgressCache();
s3Client.abortMultipartUpload(abortRequest);
s3Client.shutdown();
}
bytesUploaded += progressEvent.getBytesTransfered();
//Log.d(TAG, "bytesUploaded=" + bytesUploaded);
// broadcast progress
float fpercent = ((bytesUploaded * 100) / contentLength);
int percent = Math.round(fpercent);
if (progressListener != null) {
progressListener.progressChanged(progressEvent, bytesUploaded, percent);
}
}
};
uploadRequest.setProgressListener(s3progressListener);
UploadPartResult result = s3Client.uploadPart(uploadRequest);
partETags.add(result.getPartETag());
// cache the part progress for this upload
if (k == 1) {
initProgressCache(uploadId);
}
// store part etag
cachePartEtag(result);
filePosition += thisPartSize;
}
CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(
s3bucketName, s3key, uploadId,
partETags);
CompleteMultipartUploadResult result = s3Client.completeMultipartUpload(compRequest);
bytesUploaded = 0;
Log.i(TAG, "upload complete for " + uploadId);
clearProgressCache();
return result.getLocation();
}
private String getCachedUploadId() {
return prefs.getString(s3key + PREFS_UPLOAD_ID, null);
}
private List<PartETag> getCachedPartEtags() {
List<PartETag> result = new ArrayList<PartETag>();
// get the cached etags
ArrayList<String> etags = SharedPreferencesUtils.getStringArrayPref(prefs, s3key + PREFS_ETAGS);
for (String etagString : etags) {
String partNum = etagString.substring(0, etagString.indexOf(PREFS_ETAG_SEP));
String partTag = etagString.substring(etagString.indexOf(PREFS_ETAG_SEP) + 2, etagString.length());
PartETag etag = new PartETag(Integer.parseInt(partNum), partTag);
result.add(etag);
}
return result;
}
private void cachePartEtag(UploadPartResult result) {
String serialEtag = result.getPartETag().getPartNumber() + PREFS_ETAG_SEP + result.getPartETag().getETag();
ArrayList<String> etags = SharedPreferencesUtils.getStringArrayPref(prefs, s3key + PREFS_ETAGS);
etags.add(serialEtag);
SharedPreferencesUtils.setStringArrayPref(prefs, s3key + PREFS_ETAGS, etags);
}
private void initProgressCache(String uploadId) {
// store uploadID
Editor edit = prefs.edit().putString(s3key + PREFS_UPLOAD_ID, uploadId);
AmazonSharedPreferencesCompact.apply(edit);
// create empty etag array
ArrayList<String> etags = new ArrayList<String>();
SharedPreferencesUtils.setStringArrayPref(prefs, s3key + PREFS_ETAGS, etags);
}
private void clearProgressCache() {
// clear the cached uploadId and etags
Editor edit = prefs.edit();
edit.remove(s3key + PREFS_UPLOAD_ID);
edit.remove(s3key + PREFS_ETAGS);
AmazonSharedPreferencesCompact.apply(edit);
}
public void interrupt() {
userInterrupted = true;
}
public void abort() {
userAborted = true;
}
/**
* Override to configure the multipart upload request.
* <p/>
* By default uploaded files are publicly readable.
*
* #param initRequest S3 request object for the file to be uploaded
*/
protected void configureInitiateRequest(InitiateMultipartUploadRequest initRequest) {
initRequest.setCannedACL(CannedAccessControlList.PublicRead);
ObjectMetadata obj = new ObjectMetadata();
obj.setContentType("image/jpeg");
obj.setHeader(Constants.APP_HEADER_REFERER, Constants.APP_REFERER_URL);
initRequest.withObjectMetadata(obj);
}
public void setPrefs(SharedPreferences prefs) {
this.prefs = prefs;
}
public long getPartSize() {
return partSize;
}
public void setPartSize(long partSize) {
if (partSize < MIN_DEFAULT_PART_SIZE) {
throw new IllegalStateException("Part size is less than S3 minimum of " + MIN_DEFAULT_PART_SIZE);
} else {
this.partSize = partSize;
}
}
public void setProgressListener(UploadProgressListener progressListener) {
this.progressListener = progressListener;
}
public interface UploadProgressListener {
public void progressChanged(ProgressEvent progressEvent, long bytesUploaded, int percentUploaded);
}
}
Method 2:
TransferObserver transferObserver = transferUtility.upload(
Constants.S3_BUCKET_NAME, /* The bucket to upload to */
fileName, /* The key for the uploaded object */
new File(imagePath), /* The file where the data to upload exists */
objectMetadata);
transferObserverListener(transferObserver);
in both method i got the following error
com.amazonaws.services.s3.model.AmazonS3Exception: Forbidden (Service: Amazon S3; Status Code: 403; Error Code: 403 Forbidden; Request...
Here i must pass customer header parameters, so i add like following
ObjectMetadata objectMetadata = new ObjectMetadata();
HashMap<String, String> mMetaMap = new HashMap<String, String>();
mMetaMap.put("content-type", "image/jpeg");
mMetaMap.put(Constants.APP_HEADER_REFERER, Constants.APP_REFERER_URL);
objectMetadata.setUserMetadata(mMetaMap);
But still i got the above error.
Is i'm passing the header parameters in correct way either i need to do changes. Kindly advise on this. Thanks

Android Download manager not downloading zip file

I have made Android application that download zip file from server using Android Download Manager class and then uncompress the file and store that into SD card on pictures folder. On some of the phones.
The zip file is not downloading and download manager progress bar never show progress even if I keep it for hours. Whereas on other phones this works perfectly.
The file size is 40 MB. Is there any known limitation of Android Download Manager or in case of .zip files?
I have been using a variation of (using another class for unzipping, but since the issue here is related to downloading, i am suggesting it) this class for purposes of downloading (the file name retains specific implementation, but it is just a matter of renaming accordingly...). The work() method of this class can be called from within the run method of a Runnable object for parallel threading as inferred from the initial comment:
package com.package;
/*
This class is intended to download file filtering purpose and suffix from the server.
IMPORTANT:This is intended to be instantiated within a separate thread (i.e., != UI Thread)
*/
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
final class FileDownloader
{
// Declaring a the maximum buffer size
private static final int MAXIMUM_BUFFER_SIZE = 1024;
// Declaring static final byte fields for coding the status
protected static final byte ISDOWNLOADING = 0;
protected static final byte ERROROCCURRED = 1;
protected static final byte DOWNLOADISCOMPLETE = 2;
// Declaring a private URL field for storing the file for downloading
private java.net.URL url = null;
// Declaring a private int field for storing the file size in bytes
private int filesize;
// Declaring a private int field for storing the amount of downloaded bytes
private int bytesDownloaded;
// Declaring a private byte field for storing the current status of the download
private byte currentStatus;
// A private static final string for storing the server contents location
private static final String SERVER = "https://server.com/zipfiles/";
// Declaring a private field for storing the caller context, used for defining
// the path for saving files
private android.content.Context callerContext = null;
// The following rule is going to be applied for distributing purpose and their contents:
// 'purpose.x.zip' zip file to store the folders of the the x purpose_id and its inherent
// structure
private static final String PURPOSE= "purpose";
private String x = null;
private static final String SUFFIX = "zip";
// The remote file to be downloaded is going to be [stringed as]:
// SERVER + PURPOSE + "." + ((String.valueOf(x)).trim()) + "." + suffix
private String remoteFile = null;
// Defining a private static final File field for storing the purposes' contents within it.
// Specifically, this is being designed to be:
// java.io.File seekingRegisteredUserFolder =
// new java.io.File(callerContext.getFilesDir(), "RegisteredUser");
private final java.io.File seekingRegisteredUserFolder;
// The class constructor. The constructor depends on constructing elements for downloading
// the remoteFile respective to the element_ [cf. constructor parameter] under consideration,
// viz.:
protected FileDownloader(final String x_, final android.content.Context callerContext_)
throws
java.net.MalformedURLException,
java.io.FileNotFoundException,
java.lang.SecurityException
{
this.x = x_;
this.remoteFile = SERVER + PURPOSE + "." + ((String.valueOf(this.x)).trim()) + "." + SUFFIX;
int parsedW = 0;
try
{
parsedW = Integer.parseInt(x_);
}
catch (Exception throwableThrownParsingW)
{
throw new java.net.MalformedURLException();
}
// Implementation specific
if (parsedW < 1)
{
throw new java.net.MalformedURLException();
}
this.callerContext = callerContext_;
this.seekingRegisteredUserFolder = new java.io.File((this.callerContext).getFilesDir(), "RegisteredUser");
if (!((this.seekingRegisteredUserFolder).exists()))
{
throw new java.io.FileNotFoundException();
}
this.url = new java.net.URL(this.remoteFile);
this.filesize = -1;
this.bytesDownloaded = 0;
this.currentStatus = ISDOWNLOADING;
}
// Begins the file download. This is to be called under an object of this class instantiation
boolean work()
{
final java.io.RandomAccessFile[] randomAccessFile = {null};
final java.io.InputStream[] inputStream = {null};
final java.io.File[] purpose = {null};
try
{
purpose[0] = new java.io.File(seekingRegisteredUserFolder, (PURPOSE + "." + x + "." + SUFFIX));
// Opens a connection to the URL via ssl
final javax.net.ssl.HttpsURLConnection[] connection = {null};
connection[0] = (javax.net.ssl.HttpsURLConnection) url.openConnection();
// Defines the file part to download
connection[0].setRequestProperty("Range", "bytes=" + bytesDownloaded + "-");
// Connects to the server
connection[0].connect();
// The response code must be within the 200 range
if ((connection[0].getResponseCode() / 100) != 2)
{
currentStatus = ERROROCCURRED;
}
// Inferring the validity of the content size
final int[] contentLength = {0};
contentLength[0] = connection[0].getContentLength();
if (contentLength[0] < 1)
{
currentStatus = ERROROCCURRED;
}
// Configuring the download size, case not yet configured
if (filesize == -1)
{
filesize = contentLength[0];
}
// Opens the file, seeking its final
randomAccessFile[0] = new java.io.RandomAccessFile(purpose[0], "rw");
randomAccessFile[0].seek(bytesDownloaded);
inputStream[0] = connection[0].getInputStream();
while (currentStatus == ISDOWNLOADING)
{
// Defines the buffer according to the left amount of file to complete
byte[] byteBuffer = null;
if ((filesize - bytesDownloaded) > MAXIMUM_BUFFER_SIZE)
{
byteBuffer = new byte[MAXIMUM_BUFFER_SIZE];
}
else
{
byteBuffer = new byte[filesize - bytesDownloaded];
}
// Reads from server to the buffer
int read = inputStream[0].read(byteBuffer);
if (read == -1)
{
break;
}
// Writes from buffer to file
randomAccessFile[0].write(byteBuffer, 0, read);
bytesDownloaded += read;
}
// Changing the status for complete since this point of code has been reached
if (currentStatus == ISDOWNLOADING)
{
currentStatus = DOWNLOADISCOMPLETE;
}
}
catch (java.lang.Exception connectionException)
{
currentStatus = ERROROCCURRED;
}
finally
{
// Closes the [RandomAccessFile] file
if (randomAccessFile[0] != null)
{
try
{
randomAccessFile[0].close();
}
catch (java.lang.Exception closingFileException)
{
currentStatus = ERROROCCURRED;
}
}
if (inputStream[0] != null)
{
try
{
inputStream[0].close();
}
catch (java.lang.Exception closingConnectionException)
{
currentStatus = ERROROCCURRED;
}
}
}
if ((currentStatus == DOWNLOADISCOMPLETE) && (purpose[0] != null) &&
(purpose[0]).isFile() && (purpose[0].length() > 0) && (purpose[0].length() == filesize))
{
((AppCompatActivity) callerContext).runOnUiThread
(
new Runnable()
{
#Override
public final void run()
{
Toast.makeText(callerContext, "Downloaded: " + remoteFile.substring(remoteFile.indexOf(SERVER) + SERVER.length()), Toast.LENGTH_LONG).show();
}
}
);
return true;
}
return false;
}
}

How to check permission is granted for a directory path and won't thorow EACCES error?

I have a photo editing android app that users can choose the output directory of the the result photos. Problem is Google made a change on sdcard write permission with the KITKAT version and devices with Android KITKAT version won't allow apps to write secondary sdcards. Now I need to check if the choosen directory by user has granted the permission and won't throw EACCES error. I am already checking canRead and canWrite but these won't help. Could you please tell me how can I check if the choosen directory won't throw EACCES. My only solution is trying to write a file in a try catch, however I am hoping there is better way to do it.
[update k3b 2016-09-19]
i tried this on my android-4.4 but without success
Uri uri = Uri.fromFile(file);
int permissionCode =
context.checkCallingOrSelfUriPermission(uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (permissionCode == PackageManager.PERMISSION_DENIED) {
// on my android-4.4 i always get PERMISSION_DENIED even
// if i can overwrite the file
return false;
}
try {
Process p = new ProcessBuilder("ls", "-l", "-s", dir.getCanonicalPath()).start();
String line;
ArrayList<String> lineOut = new ArrayList<>();
BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while ((line = error.readLine()) != null) {
Log.e(TAG, "ls error = "+line);
}
error.close();
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = input.readLine()) != null) {
lineOut.add(line);
}
input.close();
String[] strings = lineOut.toArray(new String[]{});
List<FilesLS.FileEntry> fileEntries = FilesLS.processNewLines(strings);
for(FilesLS.FileEntry file : fileEntries){
Log.d(TAG, file.name +" = " + file.permissions);
}
} catch (IOException e) {
e.printStackTrace();
}
And some edits to this class
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class FilesLS {
/**
* Entry type: File
*/
public static final int TYPE_FILE = 0;
/**
* Entry type: Directory
*/
public static final int TYPE_DIRECTORY = 1;
/**
* Entry type: Directory Link
*/
public static final int TYPE_DIRECTORY_LINK = 2;
/**
* Entry type: Block
*/
public static final int TYPE_BLOCK = 3;
/**
* Entry type: Character
*/
public static final int TYPE_CHARACTER = 4;
/**
* Entry type: Link
*/
public static final int TYPE_LINK = 5;
/**
* Entry type: Socket
*/
public static final int TYPE_SOCKET = 6;
/**
* Entry type: FIFO
*/
public static final int TYPE_FIFO = 7;
/**
* Entry type: Other
*/
public static final int TYPE_OTHER = 8;
/**
* Device side file separator.
*/
public static final String FILE_SEPARATOR = "/"; //$NON-NLS-1$
/**
* Regexp pattern to parse the result from ls.
*/
private static Pattern sLsPattern = Pattern
.compile("^([bcdlsp-][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xstST])\\s+(\\S+)\\s+ (\\S+)\\s+(\\d{4}-\\d\\d-\\d\\d)\\s+(\\d\\d:\\d\\d)\\s+(.*)$"); //$NON-NLS-1$ \s+([\d\s,]*)
public static List<FileEntry> processNewLines(String[] lines) {
List<FileEntry> listOfFiles = new ArrayList<FileEntry>();
for (String line : lines) {
// no need to handle empty lines.
if (line.length() == 0) {
continue;
}
// run the line through the regexp
Matcher m = sLsPattern.matcher(line);
if (m.matches() == false) {
continue;
}
// get the name
String name = m.group(6);
// get the rest of the groups
String permissions = m.group(1);
String owner = m.group(2);
String group = m.group(3);
// String size = m.group(4);
String date = m.group(4);
String time = m.group(5);
String info = null;
// and the type
int objectType = TYPE_OTHER;
switch (permissions.charAt(0)) {
case '-':
objectType = TYPE_FILE;
break;
case 'b':
objectType = TYPE_BLOCK;
break;
case 'c':
objectType = TYPE_CHARACTER;
break;
case 'd':
objectType = TYPE_DIRECTORY;
break;
case 'l':
objectType = TYPE_LINK;
break;
case 's':
objectType = TYPE_SOCKET;
break;
case 'p':
objectType = TYPE_FIFO;
break;
}
// now check what we may be linking to
if (objectType == TYPE_LINK) {
String[] segments = name.split("\\s->\\s"); //$NON-NLS-1$
// we should have 2 segments
if (segments.length == 2) {
// update the entry name to not contain the link
name = segments[0];
// and the link name
info = segments[1];
// now get the path to the link
String[] pathSegments = info.split(FILE_SEPARATOR);
if (pathSegments.length == 1) {
// the link is to something in the same directory,
// unless the link is ..
if ("..".equals(pathSegments[0])) { //$NON-NLS-1$
// set the type and we're done.
objectType = TYPE_DIRECTORY_LINK;
} else {
// either we found the object already
// or we'll find it later.
}
}
}
// add an arrow in front to specify it's a link.
info = "-> " + info; //$NON-NLS-1$;
}
FileEntry entry = new FileEntry();
entry.permissions = permissions;
entry.name = name;
// entry.size = size;
entry.date = date;
entry.time = time;
entry.owner = owner;
entry.group = group;
if (objectType == TYPE_LINK) {
entry.info = info;
}
listOfFiles.add(entry);
}
return listOfFiles;
}
public final static class FileEntry {
String name;
String info;
String permissions;
String size;
String date;
String time;
String owner;
String group;
int type;
}
}
Add the permission(s) you need to the array:
private static final int REQUEST_CODE_PERMISSION = 2;
String[] mPermission = {
Manifest.permission.INTERNET,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.CHANGE_NETWORK_STATE,
Manifest.permission.ACCESS_WIFI_STATE
};
Add this to onCreate or where you want it to be:
try {
if (
ActivityCompat.checkSelfPermission(this, mPermission[0])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[1])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[2])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[3])
!= MockPackageManager.PERMISSION_GRANTED
) {
Log.e("TAGTAG", "DENIED");
ActivityCompat.requestPermissions(
this, mPermission, REQUEST_CODE_PERMISSION
);
// 'Will execute recursively if any of the permissions was not granted.
} else {
Log.e("TAGTAG", "GRANTED");
}
} catch (Exception e) {
e.printStackTrace();
}
Don't forget to declare the permissions in AndroidManifest.xml.

Can someone explain how TrafficStats works its magic in the Android OS?

I've written a nice little Android app to check data usage, unfortunately it relies heavily on android.net.TrafficStats which was introduced with Froyo (Android 2.2).
I'm attempting to back-port this class for my non-Froyo users, and what I'm able to determine from the Android source is:
TrafficStats.java is just a native pointer to a c file
The c file opens two files (see below) and reads their contents
If either contains a numeric value it spits it back as the "bytes used" count
Here's my challenge... when I call TrafficStats via the API on my device, I get a reading (ex. 1113853 bytes). When I open the two files and check their contents, one file doesn't exist and the other file is 0 bytes.
So clearly I mis-understood what TrafficStats is doing. Can anyone shed some light on how it's working it's magic?
Thanks for the help.
(here is my attempt to port the c file to java)
package com.suttco.net;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.suttco.IOUtils;
import com.suttco.StringUtils;
import android.util.Log;
public class TrafficStatsFile {
private static final String mobileRxFile_1 = "/sys/class/net/rmnet0/statistics/rx_bytes";
private static final String mobileRxFile_2 = "/sys/class/net/ppp0/statistics/rx_bytes";
private static final String mobileTxFile_1 = "/sys/class/net/rmnet0/statistics/tx_bytes";
private static final String mobileTxFile_2 = "/sys/class/net/ppp0/statistics/tx_bytes";
private static final String LOGGING_TAG = TrafficStatsFile.class.getSimpleName();
public long getMobileRxBytes() {
return tryBoth(mobileRxFile_1, mobileRxFile_2);
}
public long getMobileTxBytes() {
return tryBoth(mobileTxFile_1, mobileTxFile_2);
}
// Return the number from the first file which exists and contains data
private static long tryBoth(String a, String b) {
long num = readNumber(a);
return num >= 0 ? num : readNumber(b);
}
// Returns an ASCII decimal number read from the specified file, -1 on error.
private static long readNumber(String filename) {
File f = new File(filename);
if(f.exists()) {
if(f.canRead()) {
try {
Log.d(LOGGING_TAG, "f.length() = " + f.length());
String contents = IOUtils.readFileAsString(f);
if(StringUtils.IsNotNullOrEmpty(contents)) {
try {
return Long.parseLong(contents);
}
catch(NumberFormatException nfex) {
Log.w(LOGGING_TAG, "File contents are not numeric: " + filename);
}
}
else {
Log.w(LOGGING_TAG, "File contents are empty: " + filename);
}
}
catch (FileNotFoundException fnfex) {
Log.w(LOGGING_TAG, "File not found: " + filename, fnfex);
}
catch(IOException ioex) {
Log.w(LOGGING_TAG, "IOException: " + filename, ioex);
}
}
else {
Log.w(LOGGING_TAG, "Unable to read file: " + filename);
}
}
else {
Log.w(LOGGING_TAG, "File does not exist: " + filename);
}
return -1;
}
}
Here's a working, modified version of the code above. This one is using RandomAccessFile and doesn't rely on custom imports but uses only built-in String functions with their Exceptions.
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import android.util.Log;
public class TrafficStatsFile {
private static final String mobileRxFile_1 = "/sys/class/net/rmnet0/statistics/rx_bytes";
private static final String mobileRxFile_2 = "/sys/class/net/ppp0/statistics/rx_bytes";
private static final String mobileTxFile_1 = "/sys/class/net/rmnet0/statistics/tx_bytes";
private static final String mobileTxFile_2 = "/sys/class/net/ppp0/statistics/tx_bytes";
private static final String LOGGING_TAG = TrafficStatsFile.class.getSimpleName();
public long getMobileRxBytes() {
return tryBoth(mobileRxFile_1, mobileRxFile_2);
}
public long getMobileTxBytes() {
return tryBoth(mobileTxFile_1, mobileTxFile_2);
}
// Return the number from the first file which exists and contains data
private static long tryBoth(String a, String b) {
long num = readNumber(a);
return num >= 0 ? num : readNumber(b);
}
// Returns an ASCII decimal number read from the specified file, -1 on error.
private static long readNumber(String filename) {
try {
RandomAccessFile f = new RandomAccessFile(filename, "r");
try {
Log.d(LOGGING_TAG, "f.length() = " + f.length());
String contents = f.readLine();
if(!contents.isEmpty() && contents!=null) {
try {
return Long.parseLong(contents);
}
catch(NumberFormatException nfex) {
Log.w(LOGGING_TAG, "File contents are not numeric: " + filename);
}
}
else {
Log.w(LOGGING_TAG, "File contents are empty: " + filename);
}
}
catch (FileNotFoundException fnfex) {
Log.w(LOGGING_TAG, "File not found: " + filename, fnfex);
}
catch(IOException ioex) {
Log.w(LOGGING_TAG, "IOException: " + filename, ioex);
}
}catch(FileNotFoundException ffe){
Log.w(LOGGING_TAG, "File not found: " + filename, ffe);
}
return -1;
}
}
Changing it to a RandomAccessFile instead of File worked.
Edit: See IBoS's answer for working code. Changing the accepted answer to his.

Categories

Resources