I'm trying to set up a log handler to output the Android log to file to external storage. The code below creates the log file, but no output is sent to the file, so something is obviously wrong with how the handler is configured. Or, perhaps this arrangement cannot be expected to work at all?
The function is called in onCreate() from the main activity.
private void logToFile(String path) {
try {
// Get package name
String packageName = MainActivity.class.getPackage().getName();
String logfileName = path + "/" + packageName + ".log";
Logger logger = Logger.getLogger(packageName);
logger.setLevel(Level.FINE);
FileHandler fileTxt = new FileHandler(logfileName);
SimpleFormatter formatterTxt = new SimpleFormatter();
fileTxt.setFormatter(formatterTxt);
logger.addHandler(fileTxt);
Toast.makeText(this, "Logging to " + logfileName, Toast.LENGTH_LONG).show();
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
Log.i(TAG, "logging to filesystem enabled");
}
To write to the logger declared above (and, thus, the attached handler which writes to a file), the following should be used instead of Log.i(TAG, "message")
private static final Logger logger = Logger.getLogger(TAG);
public void someFunction() {
logger.info("message")
}
These log messages will also appear in logCat/debugger, with the supplied TAG.
P.S. Java logging makes my head hurt...
I was frustrated at having to use Logger instead of standard Logcat Log.d(), Log.e(), etc. so I started using this Frankenstein's monster solution of reading from Logcat into a LogRecord and saving that using FileHandler.
This means you can limit the log file size easily, and retain your detailed Android logs.
But this isn't going to give you continuous output to file. If you don't mind pressing a button or calling it once a session though, then it shouldn't really matter since Logcat is constantly updated anyway.
(I strongly recommend calling from a non-UI thread.)
FileHandler fh=null;
String name;
if ( 0 == Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED))
name = Environment.getExternalStorageDirectory().getAbsolutePath();
else
name = Environment.getDataDirectory().getAbsolutePath();
name += "/yourapp/yourapp";
try {
fh = new FileHandler(name, 1024*1024, 7, true); //Limit to 7 x 1MB files.
fh.setFormatter(new SimpleFormatter());
//Try to read Logcat.
try {
//Dumps the entire logcat to std output.
Process processD = Runtime.getRuntime().exec("logcat -v long -d");
BufferedReader bufferedReaderD = new BufferedReader(new InputStreamReader(processD.getInputStream()));
String lineD;
while ((lineD = bufferedReaderD.readLine()) != null){
//Send to the file handler.
fh.publish(new LogRecord(Level.ALL, lineD));
}
//Clear the logcat storage. Don't feel like rewriting old records.
Process processC = Runtime.getRuntime().exec("logcat -c");
} catch (IOException e) {
Log.e(TAG, "Could not get Logcat logs.");
e.printStackTrace();
}
} catch (Exception e) {
Log.e("MyLog", "FileHandler exception", e);
} finally {
if (fh != null)
fh.close();
}
Related
I previously used external storage to store specific data that I would like to share between my applications (without having any contentprovider "host")
File folder = new File(Environment.getExternalStorageDirectory(), "FOLDER_NAME");
File file = new File(folder, "FILE_NAME.dat");
FileOutputStream outputStream = new FileOutputStream(file);
That is why I am trying to use BlobStoreManager, as suggested in google's recommendation for targeting 30 (https://developer.android.com/training/data-storage/shared/datasets)
The read & write are based on a BlobHandle with 4 parameters, one being MessageDigest based on a "content". BlobHandle must use the same 4 parameters, or read will fail (SecurityException).
I managed to write data, and to read it, but it makes no sense:
It seems that in order to write, I need to use the data I want to write to generate the BlobHandle.
Then, to read, as BlobHandle must use the same 4 parameters, I also need the data I wrote to be able to read.
Totally illogic, as I wanted to read this data, I don't have it!
I must miss something or just do not understand how it work. If someone can help :)
Here are my sample:
If I set the following:
createBlobHandle: content = "mydata"
write: data = "mydata"
Then write will success, and read will success too. But it I can not know the value before reading it in a normal usecase :(
If I set the following (which would be logic, at least to me):
createBlobHandle: content = "somekey"
write: data = "mydata"
Then write will fail :(
#RequiresApi(api = Build.VERSION_CODES.R)
private BlobHandle createBlobHandle() {
//Transfer object
String content = "SomeContentToWrite";
String label = "label123";
String tag = "test";
//Sha256 summary of the transmission object
try {
byte[] contentByte = content.getBytes("utf-8");
MessageDigest md = MessageDigest.getInstance("sha256");
byte[] contentHash = md.digest(contentByte);
return BlobHandle.createWithSha256(contentHash, label,0, tag);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
private void write() {
String data = "SomeContentToWrite";
#SuppressLint("WrongConstant") final BlobStoreManager blobStoreManager = ((BlobStoreManager) applicationContext.getSystemService(Context.BLOB_STORE_SERVICE));
//Generate the session of this operation
try {
BlobHandle blobHandle = createBlobHandle();
if (blobHandle == null)
return;
long sessionId = blobStoreManager.createSession(blobHandle);
try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) {
try (OutputStream pfd = new ParcelFileDescriptor.AutoCloseOutputStream(session.openWrite(0, data.getBytes().length))) {
//The abstract of the written object must be consistent with the above, otherwise it will report SecurityException
Log.d(TAG, "writeFile: >>>>>>>>>>text = " + data);
pfd.write(data.getBytes());
pfd.flush();
//Allow public access
session.allowPublicAccess();
session.commit(applicationContext.getMainExecutor(), new Consumer<Integer>() {
#Override
public void accept(Integer integer) {
//0 success 1 failure
Log.d(TAG, "accept: >>>>>>>>" + integer);
}
});
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String read() {
String data = "";
#SuppressLint("WrongConstant") final BlobStoreManager blobStoreManager = ((BlobStoreManager) applicationContext.getSystemService(Context.BLOB_STORE_SERVICE));
BlobHandle blobHandle = createBlobHandle();
if (blobHandle != null) {
try (InputStream pfd = new ParcelFileDescriptor.AutoCloseInputStream(blobStoreManager.openBlob(createBlobHandle()))) {
//Read data
byte[] buffer = new byte[pfd.available()];
pfd.read(buffer);
String text = new String(buffer, Charset.forName("UTF-8"));
Log.d(TAG, "readFile: >>>>>>>>>>>>>>>>>>>>" + text);
} catch (IOException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
return data;
}
According to the official training documentation linked in the question, the missing piece of information, at the time of the question having been asked, is that the four pieces of data contained in the BlobHandler need to be uploaded to a server owned by the client application then subsequently downloaded by which ever other application wants to access the blob via the BlobStorageManager.
So it would seem that on-device blob discovery is not supported. There could also be a solution possible using a Content Provider which could offer up the four required pieces of data, thus circumventing the need for the server infrastructure.
I just started to learn developping android and I have a (probably) basic questions, but I didn't find anything clear.
I'm trying to store data in a JSON file, well, I've understood the logic to store it, my way is:
public boolean writeFileJson(JSONObject jobj) {
try {
FileOutputStream fOut = openFileOutput(file, Context.MODE_PRIVATE);
fOut.write(jobj.toString().getBytes());
fOut.close();
Toast.makeText(getBaseContext(), "file saved", Toast.LENGTH_SHORT).show();
} catch (Exception e1) {
e1.printStackTrace();
}
return true;
}
But my problem is to read, and concretely for the first time, because the way I do it is:
public String readFileJson() {
int c;
String temp = "";
try {
FileInputStream fin = openFileInput(file);
while ((c = fin.read()) != -1) {
temp = temp + Character.toString((char) c);
}
Toast.makeText(getBaseContext(), "file read", Toast.LENGTH_SHORT).show();
} catch (Exception e2) {
}
return temp;
}
So wen I read it for the first time and I want to acces to a parameter of my JSON is obvious that any JSON Object already exist in the file.
So I try to save a first JSON Object with my parameters in onCreate() method and save it in the file, but wen I run the app, and I stop it, it returns again to execute onCreate() and deletes all data stored during the run time.
So my question is: There is any way to init only for one time the parameters of the JSON file to could access for the first time unlike it's empty???
I hope that I'd explained well!!
Thanxxxx!!!!
You can create your own flag boolean and check when you start.
Well I don't understand well why you can use a flag if the flag is set to init value in onCreate(), but I've tried a basic method: check each time if the json file is null. But it's like so basic no? Is there any ther way, or trying to understand how to use flags without reset their values?
msgjson = readFileJson();
if(msgjson == "") {
json.put("ARRAY", jsonArray);
}else{
json = new JSONObject(msgjson);
}
Thanx!!
I am trying to get the Logcat (at least last few lines) on a button click but nothing comes up -
view.findViewById(R.id.logdone).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Make file name.
String fullName = "userlogs";
// Extract to file.
File file = new File(fullName);
InputStreamReader reader = null;
FileWriter writer = null;
try
{
// get input stream
String cmd = "logcat -d -v time";
Process process = Runtime.getRuntime().exec(cmd);
reader = new InputStreamReader (process.getInputStream());
// write output stream
writer = new FileWriter (file);
char[] buffer = new char[10000];
do
{
int n = reader.read (buffer, 0, buffer.length);
if (n == -1)
break;
writer.write (buffer, 0, n);
} while (true);
reader.close();
writer.close();
}
catch (IOException e)
{
if (writer != null)
try {
writer.close();
} catch (IOException e1) {
}
if (reader != null)
try {
reader.close();
} catch (IOException e1) {
}
e.printStackTrace();
return;
}
}
}
NOTE:
I do have permissions -
<uses-permission android:name="android.permission.READ_LOGS" />
UPDATE:
My most of the content is in Log.d() then Log.v() then Log.e() then Log.i(). But how to get the last event lines on a button click. My purpose is to get those lines and send them via email to the developer.
I am using the popular third party API known as ACRA to send email which is working fine.
I can use StringBuilder to put all my device logs into it and then to send via email.
But I am unable to get.
Any elegant way that works well effectively ?
Given the comment I made about the READ_LOGS permission no longer being granted to non-system apps, I would instead recommend using a MemoryHandler with a standard Java Logger. On a button click, you can push the messages to a target StreamHandler (which you can use to just dump them into the output buffer of your choice).
Is it possible to get the last few lines of a logcat on a button click?
There has never been a documented and supported way for apps to get anything from LogCat. And, as Turix notes, things were locked down further in Android 4.2.
Any elegant way that works well effectively ?
Log the data yourself to a file that you control, rather than (or in addition to) logging the data to LogCat.
I'm planning to implement a logging mechanism for security-related messages in Android. I want to be able to use it in the Android source code like the Log class, e.g. SecuLog.log(String msg);
It shall in the following ways differ from the normal Log
No levels like DEBUG, INFO, etc...
Output should directly go into a File on the device. There must not be the need of redirecting Logcat output for example.
As multiple processes shall be able to log security-related messages I failed with just implementing a LoggingClass in com.android.util with a static PrintWriter.
static {
try {
writer = new PrintWriter("data/secu.log");
} catch (FileNotFoundException e) {
Log.e(TAG, "Exception initializing SecuLog.", e);
}
}
This did not work, because Android is designed to run multiple dalvik-VMs that all try to access the given file. So i need some kind of non-blocking File I/O.
Is there a way to reuse any logging mechanism from Android without the need to explicitly redirect logcat output?
How else can I achieve a simple file logging mechanism, that can be called from every other process? Should I implement a logging Service? Does this service has to be a bound service or a started service? Do I have to use AIDL?
After following the comments to my question I chose the following solution:
created multiple log files
one file for each process
used processId as suffix for log files
designed a log viewing app, that puts all logs togheter
For my logging class I used the following code:
static {
try {
File file = new File("data/secu" + android.os.Process.myPid() + ".log");
file.createNewFile();
file.setReadable(true, false);
file.setExecutable(true, false);
writer = new PrintWriter(file);
} catch (IOException e) {
Log.e(TAG, "Exception initializing SecuLog.", e);
}
}
This post is quite old by now. But I recently did this work and want to share it.
Suggestions are welcome.
Multiple libs are available for this purpose, but if you want to do it yourself, here you go.
fun log(tag: String?, message: String) {
try {
val direct = File("${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/$DIRECTORY_NAME")
if (!direct.exists()) {
direct.mkdir()
}
val fileNameTimeStamp = "${SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date())}"
val logTimeStamp = SimpleDateFormat("E MMM dd yyyy 'at' hh:mm:ss:SSS aaa", Locale.getDefault()).format(Date())
val fileName = "$fileNameTimeStamp.html"
val file = File("${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/$DIRECTORY_NAME" + File.separator + fileName)
file.createNewFile()
if (file.exists()) {
val fileOutputStream = FileOutputStream(file, true)
//Here I have added a html tag to beautify/highlight the output in file.
fileOutputStream.write("<p style=\"background:lightgray;\"><strong style=\"background:lightblue;\">  $logTimeStamp :  </strong>  $message</p>".toByteArray())
fileOutputStream.close()
}
} catch (e: Exception) {
Log.e(TAG, "Error while logging into file : $e")
}
}
The purpose of keeping it html file is to open it browser and could highlight different items. Becuase log searching and debugging is very boring and beautifying could reduce the mental stress.
Output file looks like:
UPDATE There are a number of other posts asking how to get a Screenshot in android but none seemed to have a full answer of how to do so. Originally I posted this as a question due to a particular issue I was running into while attempting to open a stream to the Frame Buffer. Now I've swapped over to dumping the Frame Buffer to a file so I've updated my post to show how I got there. For reference (and acknowledgement), I found the command to send the FrameBuffer to a file from this post (unfortunately he didn't provide how he got to that point). I'm just missing how to turn the raw data I pulled from the Frame Buffer into an actual image file.
My intention was to take a full dump of the actual screen on an Android Device. The only way I could find to do so without using the adb bridge was to directly access the Frame Buffer of the system. Obviously this approach will require root privileges on the device and for the app running it! Fortunately for my purposes I have control over how the Device is set up and having the device rooted with root privileges provided to my application is feasible. My testing is currently being done on an old Droid running 2.2.3.
I found my first hints of how to approach it from https://stackoverflow.com/a/6970338/1446554. After a bit more research I found another article that describes how to properly run shell commands as root. They were using it to execute a reboot, I use it to send the current frame buffer to an actual file. My current testing has only gotten as far as doing this via ADB and in a basic Activity (each being provided root). I will be doing further testing from a Service running in the background, updates to come! Here is my entire test activity that can export the current screen to a file:
public class ScreenshotterActivity extends Activity {
public static final String TAG = "ScreenShotter";
private Button _SSButton;
private PullScreenAsyncTask _Puller;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_SSButton = (Button)findViewById(R.id.main_screenshotButton);
_SSButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (_Puller != null)
return;
//TODO: Verify that external storage is available! Could always use internal instead...
_Puller = new PullScreenAsyncTask();
_Puller.execute((Void[])null);
}
});
}
private void runSuShellCommand(String cmd) {
Runtime runtime = Runtime.getRuntime();
Process proc = null;
OutputStreamWriter osw = null;
StringBuilder sbstdOut = new StringBuilder();
StringBuilder sbstdErr = new StringBuilder();
try { // Run Script
proc = runtime.exec("su");
osw = new OutputStreamWriter(proc.getOutputStream());
osw.write(cmd);
osw.flush();
osw.close();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (osw != null) {
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
if (proc != null)
proc.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
sbstdOut.append(readBufferedReader(new InputStreamReader(proc.getInputStream())));
sbstdErr.append(readBufferedReader(new InputStreamReader(proc.getErrorStream())));
}
private String readBufferedReader(InputStreamReader input) {
BufferedReader reader = new BufferedReader(input);
StringBuilder found = new StringBuilder();
String currLine = null;
String sep = System.getProperty("line.separator");
try {
// Read it all in, line by line.
while ((currLine = reader.readLine()) != null) {
found.append(currLine);
found.append(sep);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
class PullScreenAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
File ssDir = new File(Environment.getExternalStorageDirectory(), "/screenshots");
if (ssDir.exists() == false) {
Log.i(TAG, "Screenshot directory doesn't already exist, creating...");
if (ssDir.mkdirs() == false) {
//TODO: We're kinda screwed... what can be done?
Log.w(TAG, "Failed to create directory structure necessary to work with screenshots!");
return null;
}
}
File ss = new File(ssDir, "ss.raw");
if (ss.exists() == true) {
ss.delete();
Log.i(TAG, "Deleted old Screenshot file.");
}
String cmd = "/system/bin/cat /dev/graphics/fb0 > "+ ss.getAbsolutePath();
runSuShellCommand(cmd);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
_Puller = null;
}
}
}
This also requires adding the android.permission.WRITE_EXTERNAL_STORAGE permission to the Manifest. As suggested in this post. Otherwise it runs, doesn't complain, doesn't create the directories nor the file.
Originally I couldn't get usable data from the Frame Buffer due to not understanding how to properly run shell commands. Now that I've swapped to using the streams for executing commands I can use '>' to send the Frame Buffer's current data to an actual file...
Programmatically you can run "adb shell /system/bin/screencap -p /sdcard/img.png" as below :
Process sh = Runtime.getRuntime().exec("su", null,null);
OutputStream os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();
An easy solution for ICS devices is to use the following from the command line
adb shell /system/bin/screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png screenshot.png
This'll save the screenshot.png file in the current directory.
Tested on a Samsung Galaxy SII running 4.0.3.
That would be different for different phones. It depends on the underlying graphics format of your device. You can poll what the graphics format is using system calls. If you are only going to run this on devices that you know the graphics format of you can write a converter that turns it into a known format.
You can have a look at the following project: http://code.google.com/p/android-fb2png/
If you look at the source code for fb2png.c you can see that they poll FBIOGET_VSCREENINFO which contains info about how the device stores the screen image in memory. Once you know that, you should be able to convert it into a format you can use.
I hope this helps.