My application is running fine but when it went in testing,it have some issue somewhere it got crashed and some issue also happens my question is how can i get all logs of my application in testing phase from device when device is not connection to computer because tester not use eclipse.
please help me to resolve this. Thanks in advance for any help.
You should use a crash report library like BugSense or Accra.
This kind of libs mimics the Android market feature of released apps, but during testing phase : you can get complete crash reports, with stack traces and other data (first time/last time of bug occurence, device model, etc.)
If you really want log. You can use the code below:
logcatProc = Runtime.getRuntime().exec(cmds);
mReader = new BufferedReader(new InputStreamReader(logcatProc.getInputStream()), 1024);
String line = null;
while ((line = mReader.readLine()) != null) {
if (line.length() == 0) {
continue;
}
if (out != null && line.contains(mPID)) {
out.write((simpleDateFormat2.format(new Date()) + " " + line + "\n").getBytes());
}
}
cmds = "logcat *:e *:i | grep \"(" + mPID + ")\"";
you can use your own grep format.
Tips: The logcat will be flushed off in a while
so you should put above code in a thread
You should try android-logging-log4j.
By the help of this Api you can record all the crash log in form of Text, Xml format and will be stored in the path you will provide like Phone memory or SD card.
So even if device is in Offline mode all the crash entry will be recorded over there. So you can get from that device and go through it for debugging.
Hope this will help you.
You have to use this code to get your own project logs.
public class LogcatFileManager {
private static LogcatFileManager INSTANCE = null ;
private static String PATH_LOGCAT;
private LogDumper mLogDumper = null ;
private int MPID;
private SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat ( "yyyyMMdd" );
private SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat ( "yyyy-MM-dd HH: mm: SS" );
public static LogcatFileManager getInstance () {
if (INSTANCE == null ) {
INSTANCE = new LogcatFileManager ();
}
return INSTANCE;
}
private LogcatFileManager () {
MPID = android.os.Process.myPid ();
}
private void setFolderPath (String folderPath) {
File folder = new File (folderPath);
if (! folder.exists ()) {
folder.mkdirs ();
}
if (! folder.isDirectory ())
throw new IllegalArgumentException ( "The folder path is Not a logcat Directory:" + folderPath);
PATH_LOGCAT = folderPath.endsWith("/") ? folderPath : folderPath + "/";
}
public void start (String saveDirectoy) {
setFolderPath (saveDirectoy);
if (mLogDumper == null )
mLogDumper = new LogDumper (String.valueOf (MPID), PATH_LOGCAT);
mLogDumper.start ();
}
public void stop () {
if (mLogDumper!=null ) {
mLogDumper.stopLogs();
mLogDumper = null ;
}
}
public class LogDumper extends Thread {
private Process logcatProc;
private BufferedReader MReader = null ;
private boolean mRunning = true ;
String cmds = null ;
private String MPID;
private FileOutputStream out = null ;
public LogDumper (String pid, String dir) {
MPID = pid;
try {
out = new FileOutputStream (new File(dir, "logcat-" + simpleDateFormat1.format (new Date ()) +".log"),true);
} catch (FileNotFoundException e) {
e.printStackTrace ();
}
cmds = "logcat *:e *:i | grep \"(" + MPID + ")\"";
}
public void stopLogs () {
mRunning = false ;
}
#Override
public void run() {
// TODO Auto-generated method stub
try {
logcatProc = Runtime.getRuntime().exec(cmds);
MReader = new BufferedReader ( new InputStreamReader (logcatProc.getInputStream ()), 1024 );
String line = null ;
while (mRunning && (line=MReader.readLine())!=null ) {
if (! mRunning) {
break ;
}
if (line.length () == 0 ) {
continue ;
}
if (out!=null && line.contains (MPID)) {
out.write ((simpleDateFormat2.format(new Date())+""+line+"\n").getBytes());
}
}
} catch (IOException e) {
e.printStackTrace ();
} finally {
if (logcatProc!=null ) {
logcatProc.destroy();
logcatProc = null ;
}
if (MReader!=null) {
try {
MReader.close ();
MReader = null ;
} catch (IOException e) {
e.printStackTrace ();
}
}
if (out!=null) {
try {
out.close ();
} catch (IOException e) {
e.printStackTrace ();
}
out = null ;
}
}
}
}
}
crashlytics is something you need: http://try.crashlytics.com/
You will be informed about errors, version of app that crashed etc...
Related
I'm using package com.hierynomus.smbj.share; to connect via smb2 to a network location in order to check it's connectivity however this only runs at the start of my activity (in background).
I've added a listener to return after the task has returned a string value (fail, success) once the the listener in the activity (i called the task from) is called, I then create a new instance of the Connection Status (this loops it)
This is how i check for the connection at onCreate() of an activity
//Global ConnectionStatus connectionStatus;
connectionStatus = new ConnectionStatus();
connectionStatus.setContext(this);
connectionStatus.setListener(this);
connectionStatus.setCon(databaseReadWrite.getMydbc());
connectionStatus.execute("CheckDatabase", "nodelay");
This is how I'm trying to cancel task when another activity is opened
protected void onPause() {
delay.equals("cancel");
super.onPause();
MyApplication.activityPaused();
if (connectionStatus != null) {
connectionStatus.cancel(true);
}
hasRun = false;
}
Then this runs in the same activity after the async task is completed so i've re-run a new instance of connectionStatus
#Override
public void onTaskCompleted(String result) {
ActionBar actionBar = this.getSupportActionBar();
String title = getTitle().toString();
String code = handlingFunctions.getResultCode(result);
if (code.equals("1")) {
Message.message(this, " Database connected to server!");
setTitle(title + result);
} else if (code.equals("2")) {
actionBar.setBackgroundDrawable(new ColorDrawable(Color.RED));
alertDialog.message(this, result);
} else if (code.equals("0")) {
actionBar.setBackgroundDrawable(new ColorDrawable(Color.RED));
setTitle(title + " Database connected in offline mode");
alertDialog.message(this, result);
} else if (code.equals("3")) {
//alertDialog.message(this, result);
imageStatus.setText(result);
} else if (code.equals("4")) {
imageStatus.setText(result);
}
connectionStatus = new ConnectionStatus();
connectionStatus.setContext(getApplicationContext());
connectionStatus.setListener(AllParts.this);
connectionStatus.setCon(databaseReadWrite.getMydbc());
connectionStatus.execute("CheckDatabase", "delay");
}
}
public class ConnectionStatus extends AsyncTask<String, Void, String> {
private OnConnectionStatusComplete listener;
private ArrayList<String> imageNames;
private ArrayList<ImageDetails> currentImageDetails;
private SQLiteConnection sqLiteConnection;
private Context context;
public void setListener(OnConnectionStatusComplete listen) {
this.listener = listen;
}
public void setImageNames(ArrayList<String> ImageNames) {
this.imageNames = ImageNames;
}
public void setCurrentDetails(ArrayList<ImageDetails> details) {
this.currentImageDetails = details;
}
#Override
protected void onPostExecute(String s) {
listener.onTaskCompleted(s);
}
public void setContext(Context mContext) {
context = mContext;
}
public void setCon(SQLiteConnection sqlite) {
sqLiteConnection = sqlite;
}
#Override
protected String doInBackground(String... strings) {
if(this.isCancelled()){
return "";
}else{
MyApplication myApplication = (MyApplication) context.getApplicationContext();
String IPAddress = myApplication.getIpaddress();
String domain = myApplication.getDomainname();
String username = myApplication.getUsername();
String password = myApplication.getPassword();
String containingFolder = myApplication.getSharefolder();
String temp = "";
//return null;
if (strings[1].equals("delay")) {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (strings[0].equals("CheckDatabase")) {
temp = checkDatabaseConnect();
} else {
temp = checkImageConnection(IPAddress, domain, username, password, containingFolder);
}
return temp;
}
}
String checkDatabaseConnect() {
String statement = "PRAGMA sync_status";
JSONArray is_ready = new JSONArray();
try {
SQLiteStatement mystatement = null;
try {
mystatement = sqLiteConnection.prepareStatement(statement);
} catch (SQLException ex) {
try {
java.io.File path = new java.io.File("/sdcard/exports/logs");
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
String currentDateTime = dateFormat.format(new Date()) + " ";
java.io.File myFile = new java.io.File(path, "DBCrashes.txt");
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
myOutWriter.append("\n" +
"\r");
myOutWriter.append(currentDateTime + "Get pragma results: (" + statement + ")" + ex);
myOutWriter.close();
fOut.close();
} catch (java.io.IOException e) {
//do something if an IOException occurs.
}
}
mystatement.step();
int ncols = mystatement.getColumnCount();
if (ncols > 0) {
String result = mystatement.getColumnTextNativeString(0);
JSONObject jObject = new JSONObject(result);
is_ready = jObject.getJSONArray("peers");
if (is_ready.length() > 0) {
return "Success, Database is connected! (1)";
} else {
return "The database is not connected to the server. In offline mode, check network and restart application to connect to server. (0)";
}
}
mystatement.dispose();
} catch (SQLException ex) {
try {
java.io.File path = new java.io.File("/sdcard/exports/logs");
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
String currentDateTime = dateFormat.format(new Date()) + " ";
java.io.File myFile = new java.io.File(path, "DBCrashes.txt");
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
myOutWriter.append("\n" +
"\r");
myOutWriter.append(currentDateTime + " (" + statement + ")" + ex);
myOutWriter.close();
fOut.close();
return "No connection to a database, please try again (2)";
} catch (java.io.IOException e) {
//do something if an IOException occurs.
}
} catch (JSONException e) {
e.printStackTrace();
}
return "Success (5)";
}
}
The problem I'm having is after I leave the activity, the task is still running and returns this error (I know this because it takes into account the delay)
You do have implemented a handling for isCanceled but this implementation is only at the start.
Your current flow is like this:
task starts, immediately checks if it is cancelled or not (since it is the first line in doInBackground)
onPause is called, cancel of the task is invoked
since the cancel check has already been made your task continues to run freely
How do you solve this?
The easiest way would be to check if it is cancelled before checkDatabaseConnection or checkImageConnection is called. And even inside those methods check if it is cancelled or not.
Even if that is done correctly, your Thread.sleep could cause your AsyncTask being cancelled 6s after onPause is called.
For reasons like this, AsyncTask is not used anymore. There are other options for Threading which are much better.
This or this will give you a starting point.
try checking if the task is cancelled after the Thread.sleep(6000) , so even if the task was in sleep when cancel is called it will return when it is done.
if (strings[1].equals("delay")) {
try {
Thread.sleep(6000);
if(this.isCancelled()) {
return "";
}
} catch (InterruptedException e) {
e.printStackTrace();
}
/*if(this.isCancelled()) {
return "";
}*/
}
I am trying to write a custom android print service. I have followed the instruction mentioned in the below blog:
https://github.com/zaki50/MyPrintServiceProject
I am able to create a service, which executes a print job. But after the job doesn't print any thing. My question is, what is the localId that we should provide. Is it printer ip, name? Also is there any way to know why the print job doesn't work.
Basically the two methods that I am concerned about are adding onStartPrinterDiscovery and OnPrintJobQueue as mentioned below:
#Override
public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
String str = Settings.Secure.getString(getBaseContext().getContentResolver(), "enabled_print_services");
Log.d("myprinter", "PrinterDiscoverySession#onStartPrinterDiscovery(priorityList: " + priorityList + ") called")
final List<PrinterInfo> printers = new ArrayList<>();
final PrinterId printerId = generatePrinterId("D8:49:2F:64:04:DB");
final PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId, "D8:49:2F:64:04:DB", PrinterInfo.STATUS_IDLE);
PrinterCapabilitiesInfo.Builder capBuilder = new PrinterCapabilitiesInfo.Builder(printerId);
capBuilder.addMediaSize(PrintAttributes.MediaSize.NA_LETTER, true);
capBuilder.addMediaSize(PrintAttributes.MediaSize.NA_LETTER, false);
capBuilder.addResolution(new PrintAttributes.Resolution("resolutionId", "default resolution", 600, 600), true);
capBuilder.setColorModes(PrintAttributes.COLOR_MODE_COLOR | PrintAttributes.COLOR_MODE_MONOCHROME, PrintAttributes.COLOR_MODE_COLOR);
builder.setCapabilities(capBuilder.build());
printers.add(builder.build());
addPrinters(printers);
}
#Override
protected void onPrintJobQueued(PrintJob printJob) {
Log.d("myprinter", "queued: " + printJob.getId().toString());
printJob.start();
//printJob.complete();
final PrintDocument document = printJob.getDocument();
final FileInputStream in = new FileInputStream(document.getData().getFileDescriptor());
try {
final byte[] buffer = new byte[4];
#SuppressWarnings("unused")
final int read = in.read(buffer);
Log.d("myprinter", "first " + buffer.length + "bytes of content: " + toString(buffer));
} catch (IOException e) {
Log.d("myprinter", "", e);
} finally {
try {
in.close();
} catch (IOException e) {
assert true;
}
}
if( printJob.isStarted()){
printJob.complete();
}
}
My Question is what should be printerId(or localId). How do we know if the printjob has succeeded or failed
You can do detection of print job status with other methods in the class:
if( printJob.isStarted() ){
}
else if( printJob.isCompleted() ){
}
else if( printJob.isCancelled() ){
}
else if( printJob.isFailed() ){
}
In addition, the print service has it's own methods listed in the docs:
#Override
protected void onPrintJobQueued(PrintJob printJob) {
Log.d("printer", "queued: " + printJob.getId().toString());
printJob.start();
//...
printJob.complete();
}
#Override
protected void onRequestCancelPrintJob(PrintJob printJob) {
Log.d("printer", "canceled: " + printJob.getId().toString());
printJob.cancel();
}
So I have a notification in my app. The notification has some text and an image which I fetch from a LruCache which I populate at the start. The problem is that on Kit-Kat devices (Moto E, XIomi) in my case, the notification stops updating if it has already updated a fixed number of times, lets say n (this n is usually fixed for each device, and is <10). This is my code for updating
String title = CommonUtils.getTitleFromID(id, context);
Bitmap bitmap = DataProvider.getInstance(context).diskLruImageCache.getBitmap(id + "");
if (playbackPaused) {
bigView.setImageViewResource(R.id.pause, R.drawable.pause_noti);
smallView.setImageViewResource(R.id.pause1, R.drawable.pause_noti);
remoteViews.setImageViewResource(R.id.pause2, R.drawable.pause_noti);
playbackPaused = false;
Log.d(TAG, "noti-false");
}
bigView.setTextViewText(R.id.title, title + "");
bigView.setImageViewBitmap(R.id.img, bitmap);
smallView.setTextViewText(R.id.title1, title);
smallView.setImageViewBitmap(R.id.img1, bitmap);
mNotificationManager.notify(NOTIFY_ID, notification);
I tried with a bit of debugging and all and I found that if I do not set the bitmap, (and just do
bitmap = null) then everything works fine.
I don't understand why this is happening. This issue is android version specific since I already tested on Nexus 5 (Lollipop) and other Android 5 phones but there this doesn't happen. Does someone have any idea what the reason behind this might be?
I am not expecting an exact situation. Even some ideas in the right direction will be very helpful. Thanks !!
I am also adding the code for my DiskLruCache in case that might be needed,
public class DiskLruImageCache {
private DiskLruCache mDiskCache;
private Bitmap.CompressFormat mCompressFormat = Bitmap.CompressFormat.JPEG;
private int mCompressQuality = 70;
private static final int APP_VERSION = 1;
private static final int VALUE_COUNT = 1;
private static final String TAG = "DiskLruImageCache";
public DiskLruImageCache( Context context,String uniqueName, int diskCacheSize,
Bitmap.CompressFormat compressFormat, int quality ) {
try {
final File diskCacheDir = getDiskCacheDir(context, uniqueName );
mDiskCache = DiskLruCache.open( diskCacheDir, APP_VERSION, VALUE_COUNT, diskCacheSize );
mCompressFormat = compressFormat;
mCompressQuality = quality;
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean writeBitmapToFile( Bitmap bitmap, DiskLruCache.Editor editor )
throws IOException {
OutputStream out = null;
try {
out = new BufferedOutputStream( editor.newOutputStream( 0 ), Utils.IO_BUFFER_SIZE );
return bitmap.compress( mCompressFormat, mCompressQuality, out );
} finally {
if ( out != null ) {
out.close();
}
}
}
private boolean writeUriToFile( Uri bitmap, DiskLruCache.Editor editor )
throws IOException, FileNotFoundException {
OutputStream out = null;
try {
out = new BufferedOutputStream( editor.newOutputStream( 0 ), Utils.IO_BUFFER_SIZE );
return true;
} finally {
if ( out != null ) {
out.close();
}
}
}
private File getDiskCacheDir(Context context, String uniqueName) {
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
// otherwise use internal cache dir
final String cachePath =
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
!Utils.isExternalStorageRemovable() ?
Utils.getExternalCacheDir(context).getPath() :
context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}
public void put( String key, Bitmap data ) {
DiskLruCache.Editor editor = null;
try {
editor = mDiskCache.edit( key );
if ( editor == null ) {
return;
}
if( writeBitmapToFile( data, editor ) ) {
mDiskCache.flush();
editor.commit();
if ( BuildConfig.DEBUG ) {
Log.d( "cache_test_DISK_", "image put on disk cache " + key );
}
} else {
editor.abort();
if ( BuildConfig.DEBUG ) {
Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
}
}
} catch (IOException e) {
if ( BuildConfig.DEBUG ) {
Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
}
try {
if ( editor != null ) {
editor.abort();
}
} catch (IOException ignored) {
}
}
}
public void put( String key, Uri data ) {
DiskLruCache.Editor editor = null;
try {
editor = mDiskCache.edit( key );
if ( editor == null ) {
return;
}
if( writeUriToFile( data, editor ) ) {
mDiskCache.flush();
editor.commit();
if ( BuildConfig.DEBUG ) {
Log.d( "cache_test_DISK_", "image put on disk cache " + key );
}
} else {
editor.abort();
if ( BuildConfig.DEBUG ) {
Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
}
}
} catch (IOException e) {
if ( BuildConfig.DEBUG ) {
Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
}
try {
if ( editor != null ) {
editor.abort();
}
} catch (IOException ignored) {
}
}
}
public synchronized Bitmap getBitmap( String key ) {
Bitmap bitmap = null;
DiskLruCache.Snapshot snapshot = null;
try {
snapshot = mDiskCache.get( key );
if ( snapshot == null ) {
return null;
}
final InputStream in = snapshot.getInputStream( 0 );
if ( in != null ) {
final BufferedInputStream buffIn =
new BufferedInputStream( in, Utils.IO_BUFFER_SIZE );
bitmap = BitmapFactory.decodeStream(buffIn);
}
} catch ( IOException e ) {
e.printStackTrace();
} finally {
if ( snapshot != null ) {
snapshot.close();
}
}
if ( BuildConfig.DEBUG ) {
Log.d( "cache_test_DISK_", bitmap == null ? "" : "image read from disk " + key);
}
return bitmap;
}
public boolean containsKey( String key ) {
boolean contained = false;
DiskLruCache.Snapshot snapshot = null;
try {
snapshot = mDiskCache.get( key );
contained = snapshot != null;
} catch (IOException e) {
e.printStackTrace();
} finally {
if ( snapshot != null ) {
snapshot.close();
}
}
return contained;
}
public boolean removeKey( String key ) {
boolean removed=false;
DiskLruCache.Snapshot snapshot = null;
try {
removed = mDiskCache.remove( key );
} catch (IOException e) {
e.printStackTrace();
} finally {
if ( snapshot != null ) {
snapshot.close();
}
}
return removed;
}
public void clearCache() {
if ( BuildConfig.DEBUG ) {
Log.d("cache_test_DISK_", "disk cache CLEARED");
}
try {
mDiskCache.delete();
} catch ( IOException e ) {
e.printStackTrace();
}
}
public File getCacheFolder() {
return mDiskCache.getDirectory();
}
}
put if condition for kitkat and lower versions ... becoz after kitkat version changes in color in notification system.. my mipmap notify img siz is 96*96
NOTIFICATION_ID= Integer.parseInt(arr_msg[7].toString());
mBuilder.setSmallIcon(R.mipmap.alert_nodification_icon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.alert_nodification_icon))
.setContentTitle(notification_vehicle_no)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(notification_msg))
.setContentText(notification_msg);}
mBuilder.setContentIntent(contentIntent);
Notification notification = mBuilder.build();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
mNotificationManager.notify(NOTIFICATION_ID,notification);
I am working on Brix android x86 system. After inserting a module(introduced memory crashed code in the module for causing panic), it panics. But, the system does not auto-reboot. It hangs. Could you please tell the steps for auto-rebooting the android after panic ?
Thanks And Regards,
Pankaj
Depending upon your requirement you can put these peice of code where you require.
You can do with these 2 scenarios
You application must be signed with system application
// must be a system app
void reboot(Context context) {
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
pm.reboot(null);
}
You device must be rooted
if (ShellInterface.isSuAvailable()) {
ShellInterface.runCommand("-su");
ShellInterface.runCommand("reboot");
}
ShellInterface.class
public class ShellInterface {
private static final String TAG = "ShellInterface";
private static String shell;
// uid=0(root) gid=0(root)
private static final Pattern UID_PATTERN = Pattern
.compile("^uid=(\\d+).*?");
enum OUTPUT {
STDOUT, STDERR, BOTH
}
private static final String EXIT = "exit\n";
private static final String[] SU_COMMANDS = new String[] { "su",
"/system/xbin/su", "/system/bin/su" };
private static final String[] TEST_COMMANDS = new String[] { "id",
"/system/xbin/id", "/system/bin/id" };
public static synchronized boolean isSuAvailable() {
if (shell == null) {
checkSu();
}
return shell != null;
}
public static synchronized void setShell(String shell) {
ShellInterface.shell = shell;
}
private static boolean checkSu() {
for (String command : SU_COMMANDS) {
shell = command;
if (isRootUid())
return true;
}
shell = null;
return false;
}
private static boolean isRootUid() {
String out = null;
for (String command : TEST_COMMANDS) {
out = getProcessOutput(command);
if (out != null && out.length() > 0)
break;
}
if (out == null || out.length() == 0)
return false;
Matcher matcher = UID_PATTERN.matcher(out);
if (matcher.matches()) {
if ("0".equals(matcher.group(1))) {
return true;
}
}
return false;
}
public static String getProcessOutput(String command) {
try {
return _runCommand(command, OUTPUT.STDERR);
} catch (IOException ignored) {
return null;
}
}
public static boolean runCommand(String command) {
try {
_runCommand(command, OUTPUT.BOTH);
return true;
} catch (IOException ignored) {
return false;
}
}
private static String _runCommand(String command, OUTPUT o)
throws IOException {
DataOutputStream os = null;
Process process = null;
try {
process = Runtime.getRuntime().exec(shell);
os = new DataOutputStream(process.getOutputStream());
InputStreamHandler sh = sinkProcessOutput(process, o);
os.writeBytes(command + '\n');
os.flush();
os.writeBytes(EXIT);
os.flush();
process.waitFor();
if (sh != null) {
String output = sh.getOutput();
Log.d(TAG, command + " output: " + output);
return output;
} else {
return null;
}
} catch (Exception e) {
final String msg = e.getMessage();
Log.e(TAG, "runCommand error: " + msg);
throw new IOException(msg);
} finally {
try {
if (os != null) {
os.close();
}
if (process != null) {
process.destroy();
}
} catch (Exception ignored) {
}
}
}
public static InputStreamHandler sinkProcessOutput(Process p, OUTPUT o) {
InputStreamHandler output = null;
switch (o) {
case STDOUT:
output = new InputStreamHandler(p.getErrorStream(), false);
new InputStreamHandler(p.getInputStream(), true);
break;
case STDERR:
output = new InputStreamHandler(p.getInputStream(), false);
new InputStreamHandler(p.getErrorStream(), true);
break;
case BOTH:
new InputStreamHandler(p.getInputStream(), true);
new InputStreamHandler(p.getErrorStream(), true);
break;
}
return output;
}
private static class InputStreamHandler extends Thread {
private final InputStream stream;
private final boolean sink;
StringBuffer output;
public String getOutput() {
return output.toString();
}
InputStreamHandler(InputStream stream, boolean sink) {
this.sink = sink;
this.stream = stream;
start();
}
#Override
public void run() {
try {
if (sink) {
while (stream.read() != -1) {
}
} else {
output = new StringBuffer();
BufferedReader b = new BufferedReader(
new InputStreamReader(stream));
String s;
while ((s = b.readLine()) != null) {
output.append(s);
}
}
} catch (IOException ignored) {
}
}
}
}
You can force the kernel panic with "echo c > /proc/sysrq-trigger" command.
More debug logs can be seen via kernel panic log.
Goal
Collect periodic updates of the LogCat and save (append) those chunks of text to a file on the SDcard
Problem
The Log class doesn't provide updates since a specific time-stamp
Possible solution
My plan is to periodically run code that is similar to: http://www.helloandroid.com/tutorials/reading-logs-programatically
or https://stackoverflow.com/a/9039352/550471
However, with one notable difference: use the -v time parameter to ensure that each line is time-stamped.
After each time the LogCat data is collected, the app will store the time-stamp of the last Log entry. The next time the LogCat data is collected the app will search through the text to find the time-stamp and then save the chunk of data to sdcard that was added to the Log since the specified time-stamp.
Possible problem
If the LogCat data is collected at too short periods then the CPU is busy processing a lot of 'old' data.
If the Logcat data is collected at too long periods then some data could be missed.
Is there a better way ?
This is what I came up with - it works very well when it doesn't freeze up.
As you might know, Runtime.getRuntime().exec("") has a pretty good chance of causing an ANR in Android earlier than Jelly Bean. If someone has a solution to overcome the ANR, then please share.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import android.os.Environment;
import android.util.Log;
/*
* For (compressed) buffer sizes, see: http://elinux.org/Android_Logging_System
* buffer:main = 64KB
* buffer:radio = 64KB
* buffer:system = 64KB
* buffer:event = 256KB
*
* NOTE: the 'command' must include "-d -v time" !!
* to switch buffers, use "-b <buffer>"
*/
public class LogCatReader {
// constants
private static final String CR = "\r\n";
private static final String END_OF_DATE_TIME = "): ";
private static final int DEFAULT_SEARCH_START_INDEX = 0;
// member variables
private StringBuilder mLog;
private LogThread mLogThread = null;
private String mLastLogReadToken = "";
private String mLogCommand = "";
private int mStringCapacity;
private File mFileTarget = null;
// constructor
public LogCatReader(String command, int capacity) {
mLogCommand = command;
mStringCapacity = capacity;
}
// returns complete logcat buffer
// note: takes about 1.5sec to finish
synchronized public StringBuilder getLogComplete() {
try {
// capacity should be about 25% bigger than buffer size since the
// buffer is compressed
mLog = new StringBuilder(mStringCapacity);
// command to capture log
Process process = Runtime.getRuntime().exec(mLogCommand);
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
// append() is costly if capacity needs to be increased, be sure
// to reserve enough in the first place
mLog.append(line + CR);
}
} catch (IOException e) {
}
return mLog;
}
public String getLogUpdatesOnly() {
String strReturn = "";
StringBuilder sbLog = getLogComplete();
try {
int iStartindex = DEFAULT_SEARCH_START_INDEX;
// if there exists a token from a previous search then use that
if (mLastLogReadToken.length() > 0) {
iStartindex = sbLog.indexOf(mLastLogReadToken);
// if string not found then start at beginning
if (iStartindex == -1) {
// start search at beginning of log
iStartindex = DEFAULT_SEARCH_START_INDEX;
}
}
int iEndindex = sbLog.length();
// if token is found then move index to the next line
if (iStartindex > DEFAULT_SEARCH_START_INDEX) {
iStartindex = sbLog.indexOf(CR, iStartindex);
if (iStartindex != -1) {
iStartindex += CR.length();
} else {
// return an empty string
iStartindex = iEndindex;
}
}
// grab the data between the start and end indices
strReturn = sbLog.substring(iStartindex, iEndindex);
// grab date/time token for next search
iStartindex = sbLog.lastIndexOf(END_OF_DATE_TIME);
if (iStartindex != -1) {
iEndindex = iStartindex;
iStartindex = sbLog.lastIndexOf(CR, iEndindex);
iStartindex += CR.length();
if (iStartindex == -1) {
// read from beginning
iStartindex = 0;
}
mLastLogReadToken = sbLog.substring(iStartindex, iEndindex);
}
} catch (Exception e) {
strReturn = "";
}
return strReturn;
}
public void startPeriodicLogCatReader(int timePeriod, String logfilename) {
if (mLogThread == null) {
mLogThread = new LogThread(timePeriod, logfilename);
mLogThread.start();
}
}
public void stopPeriodicLogCatReader() {
if (mLogThread != null) {
mLogThread.interrupt();
mLogThread = null;
}
}
private class LogThread extends Thread {
private boolean mInterrupted;
private int mTimePeriod;// in seconds
private String mLogref;
private BufferedWriter mBuffWriter = null;
public boolean mPauseLogCollection = false;
// constructor: logfilename is optional - pass null to not use
public LogThread(int timePeriod, String logfilename) {
mTimePeriod = timePeriod;
if (logfilename != null) {
File fLogFolder = new File(
Environment.getExternalStorageDirectory() + "/logfiles");
if (fLogFolder.exists() == false) {
if (fLogFolder.mkdirs() == false) {
Log.e("LogCatReader",
"Could not create "
+ fLogFolder.getAbsolutePath());
}
}
mFileTarget = new File(
Environment.getExternalStorageDirectory() + "/logfiles",
logfilename);
if (mFileTarget.exists() == false) {
try {
// file doesn't yet exist - create a fresh one !
mFileTarget.createNewFile();
} catch (IOException e) {
e.printStackTrace();
mFileTarget = null;
}
}
}
}
#Override
public void interrupt() {
mInterrupted = true;
super.interrupt();
}
#Override
public void run() {
super.run();
// initialization
mInterrupted = false;
// set up storage
if (mFileTarget != null) {
try {
mBuffWriter = new BufferedWriter(new FileWriter(
mFileTarget, true), 10240);
} catch (IOException e) {
e.printStackTrace();
}
}
while ((mInterrupted == false) && (mBuffWriter != null)) {
if (mPauseLogCollection == false) {
// read log updates
mLogref = getLogUpdatesOnly();
// save log updates to file
try {
mBuffWriter.append(mLogref);
mBuffWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
if (!mInterrupted) {
try {
sleep(mTimePeriod * 1000);
} catch (InterruptedException e) {
}
}
}
if (mBuffWriter != null) {
try {
mBuffWriter.close();
mBuffWriter = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}// end of inner class
}// end of outer class
The procedure I used to find only the updates is to capture the date and time of the very last line of a logcat and use that as the search token next time around.
To use this class, the following is an example:
LogCatReader logcatPeriodicReader = new LogCatReader("logcat -b main -d -v time", 80 * 1024);//collect "main" buffer, exit after reading logcat
logcatPeriodicReader.startPeriodicLogReader(90, "log.txt");//read logcat every 90 secs