I've implemented a service that listens to commands issued through ADB. An example of a command sent through ADB could look like this:
adb shell am startservice -a com.testandroid.SEND_SMS -e number 123123123 -e message "åäö"
Now, the problem here is that the encoding of the string "åäö" seems to mess up. If I take that string extras and immediately output it to the log, I get a square "[]", unknown character. If I send this message I get chinese characters in the messages app. As long as I stick to non-umlaut characters (ASCII I guess), everything works fine.
I'm using Windows 7 and the command line for this. I have not touched the encoding of the command line and I've tried to process the extras string by getting the byte characters, passing in UTF-8 as an encoding argument, then creating a new String passing in UTF-8 as an encoding argument there as well. No dice, though.
The values of the bytes, when using getBytes() are å: -27, ä: -92, ö: -74
How do I get this to play nice so I can make use of at least the umlauts?
All of this works perfectly fine in Linux.
i ran into the same issue, but finally i got it work!
if you use for example C#, you have to do it like the following example:
02.12.2019
According to the protocol.txt, the ADB-Protocol supports "smart-sockets". Those sockets can be used to do all the stuff, the ADB-Client inside the adb.exe does. For example if you want upload an file, you have to request such an "smart-socket". After that, you have to follow the protocol assigned to the service (for an service overview see SERVICE.txt) as described, for example, in the SYNC.txt.
13.10.2014
public static List<string> ExecuteBG(string exe, string args, int timeOut = -1)
{
if (File.Exists(exe) || exe == "cmd.exe")
{
ProcessStartInfo StartInfo = new ProcessStartInfo();
StartInfo.FileName = exe;
StartInfo.Arguments = Encoding.Default.GetString(Encoding.UTF8.GetBytes(args));
StartInfo.CreateNoWindow = true;
StartInfo.UseShellExecute = false;
StartInfo.RedirectStandardError = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.StandardErrorEncoding = Encoding.UTF8;
StartInfo.StandardOutputEncoding = Encoding.UTF8;
AutoResetEvent errorWaitHandle = new AutoResetEvent(false);
AutoResetEvent outputWaitHandle = new AutoResetEvent(false);
List<string> response = new List<string>();
Process proc = new Process();
proc.StartInfo = StartInfo;
proc.ErrorDataReceived += (s, e) =>
{
if (String.IsNullOrEmpty(e.Data))
{
errorWaitHandle.Set();
}
else
{
response.Add(e.Data);
}
};
proc.OutputDataReceived += (s, e) =>
{
if (String.IsNullOrEmpty(e.Data))
{
outputWaitHandle.Set();
}
else
{
response.Add(e.Data);
}
};
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit(timeOut);
errorWaitHandle.WaitOne(timeOut);
outputWaitHandle.WaitOne(timeOut);
return response;
}
return new List<string>();
}
Really important is this part "StartInfo.Arguments = Encoding.Default.GetString(Encoding.UTF8.GetBytes(args));", here we convert the UTF8 string into the Windows "default" charset which is known by cmd. So we send a "destroyed" "default" encoded string to cmd and the Android shell will convert it back to UTF8. So we have the "umlauts" like "üöäÜÖÄàè etc.".
Hope this helps someone.
PS: If u need a working "Framework" which supports UTF8 push/pull for files/folders also have a look at my AndroidCtrl.dll it's C# .NET4 written.
Regards,
Sebastian
Concluding, either the problem is situated in cmd.exe or adb.exe. Until either one or both are updated to be more compliant with eachother I will sadly not be able to make use of this for the time being.
Related
I want to send the log lines to email in every 10 minutes.
To do that, I have used a Timer and inside of timer I send the logs via email.
However I loose some log lines between 2 emails.
For example my first email contains no lines which is normal according to my algorithm.
My second email contains log lines between 15.37 and 15.38 seconds.
My third email contains logs in between 15.44 and 15.48 time intervals.
My fourth email contains logs in between 15.55 and 15.58 time intervals.
As you can see I loose some of my logs but I could not find a way to avoid that.
Following is my code in my service class:
#Override
public void onCreate() {
super.onCreate();
mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
sendLogFile();
}
}, 0, 1000 * 60 * 10 );
}
Inside of sendSupport method the second parameter is sent as a content of the log lines where logs is a static string variable.
private void sendLogFile() {
mInteractor.sendSupport("LOG FILE", "MSG"+logs, "SUBJECT"+ System.currentTimeMillis(), "",
result -> {
Timber.log(Log.DEBUG, "sendSupport Thread.currentThread().getName() " + Thread.currentThread().getName());
if (result.isSuccess) {
Timber.d("is sent");
writeLogFile();
} else {
Timber.d("is NOT sent");
}
}
);
}
private void writeLogFile()
{
try {
StringBuilder logBuilder = new StringBuilder();
process = Runtime.getRuntime().exec( "logcat -d");
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
logBuilder.append(line + "\n");
}
logs = logBuilder.toString();
} catch (IOException e) {
e.printStackTrace();
}
}
As a result I could not figure out how am I going to be able to get all logs in periodically in my email.
Thanks.
While the #Knossos answer is pointing the reason why some logs are missing, it doesn't suggest how to use that knowledge to reliably get the logs from users' phones, when you don't have an access to their devices to run some adb commands.
Here is what I suggest to do instead:
As you do already use Timber, just add some more power to it. Namely, add an additional LoggingTree that will save the logs into a file, instead of just posting them to Logcat. You can have many Timber Trees working simultaneously, so you can have both Logcat and File logs if needed.
Use the same timer to send an email message when and where needed. But, instead of using access to logcat -d, simply use the file where Timber have already written the logs. Don't forget to flush the stream before sending the email. In order not to send the same logs again and again, configure the FileTree in a way that it creates a new file every time when a previous one is sent (seta a new file name via a method call, for example).
Profit :)
To log into two different systems (Trees), you need to simply add one more to Timber:
Timber.plant(new Timber.DebugTree());
Timber.plant(new FileLoggingTree());
And here is an example of FileLoggingTree(source):
public class FileLoggingTree extends Timber.DebugTree {
private static Logger mLogger = LoggerFactory.getLogger(FileLoggingTree.class);
private static final String LOG_PREFIX = "my-log";
public FileLoggingTree(Context context) {
final String logDirectory = context.getFilesDir() + "/logs";
configureLogger(logDirectory);
}
private void configureLogger(String logDirectory) {
// reset the default context (which may already have been initialized)
// since we want to reconfigure it
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.reset();
RollingFileAppender<ILoggingEvent> rollingFileAppender = new RollingFileAppender<>();
rollingFileAppender.setContext(loggerContext);
rollingFileAppender.setAppend(true);
rollingFileAppender.setFile(logDirectory + "/" + LOG_PREFIX + "-latest.html");
SizeAndTimeBasedFNATP<ILoggingEvent> fileNamingPolicy = new SizeAndTimeBasedFNATP<>();
fileNamingPolicy.setContext(loggerContext);
fileNamingPolicy.setMaxFileSize("1MB");
TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<>();
rollingPolicy.setContext(loggerContext);
rollingPolicy.setFileNamePattern(logDirectory + "/" + LOG_PREFIX + ".%d{yyyy-MM-dd}.%i.html");
rollingPolicy.setMaxHistory(5);
rollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(fileNamingPolicy);
rollingPolicy.setParent(rollingFileAppender); // parent and context required!
rollingPolicy.start();
HTMLLayout htmlLayout = new HTMLLayout();
htmlLayout.setContext(loggerContext);
htmlLayout.setPattern("%d{HH:mm:ss.SSS}%level%thread%msg");
htmlLayout.start();
LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<>();
encoder.setContext(loggerContext);
encoder.setLayout(htmlLayout);
encoder.start();
// Alternative text encoder - very clean pattern, takes up less space
// PatternLayoutEncoder encoder = new PatternLayoutEncoder();
// encoder.setContext(loggerContext);
// encoder.setCharset(Charset.forName("UTF-8"));
// encoder.setPattern("%date %level [%thread] %msg%n");
// encoder.start();
rollingFileAppender.setRollingPolicy(rollingPolicy);
rollingFileAppender.setEncoder(encoder);
rollingFileAppender.start();
// add the newly created appenders 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.DEBUG);
root.addAppender(rollingFileAppender);
// print any status messages (warnings, etc) encountered in logback config
StatusPrinter.print(loggerContext);
}
#Override
protected void log(int priority, String tag, String message, Throwable t) {
if (priority == Log.VERBOSE) {
return;
}
String logMessage = tag + ": " + message;
switch (priority) {
case Log.DEBUG:
mLogger.debug(logMessage);
break;
case Log.INFO:
mLogger.info(logMessage);
break;
case Log.WARN:
mLogger.warn(logMessage);
break;
case Log.ERROR:
mLogger.error(logMessage);
break;
}
}
}
The problem is that logcat -d is only delivering you the latest X bytes of data from the stream. You aren't guaranteed to get everything between 10 minute intervals.
In the best case, you get what you want. In the worst cases, you miss log data or log sections overlap (you get some from the previous dump too).
You can see this here: adb logcat -d | dd
...
03-27 11:36:27.474 791 22420 E ResolverController: No valid NAT64 prefix (147, <unspecified>/0)
03-27 11:36:27.612 3466 3521 I PlayCommon: [657] alsu.c(187): Successfully uploaded logs.
453+111 records in
499+1 records out
255863 bytes (256 kB, 250 KiB) copied, 0,136016 s, 1,9 MB/s
As you can see, it is clearly a 256 kB chunk that is pulled through logcat -d.
On the plus side, you can change that! If you look at adb logcat --help you can see options.
For example, if you use adb logcat -d -t '100000' | dd (all logs in the last 100000 seconds. I now have the following:
...
03-27 11:45:41.687 791 1106 I netd : bandwidthSetGlobalAlert(2097152) <0.90ms>
03-27 11:45:42.098 21897 23376 V FA : Inactivity, disconnecting from the service
2237+1558 records in
2879+1 records out
1474408 bytes (1,5 MB, 1,4 MiB) copied, 1,20785 s, 1,2 MB/s
1.5 MB of logs. You should be able to get all logs with this.
You log the timestamp of each logcat pull, then use that each time to determine the seconds since the last pull.
I hope that helps!
You cannot send an email from the device without user interaction or implementing the email function yourself that allows to do this without user interaction.
Most apps have some api endpoint to send the logs to.
I am intermittently getting Too many open files exceptions when I try to write to a text file in my Xamarin / Android app.
I understand that this is because each Android app has a limit on the number of files it can have open, and obviously my app is exceeding it in some circumstances. Hence I must be leaving some files open / not disposing of them properly somewhere.
I would like to know if I can programatically list the files that my app has open at any given time? Obviously the operating system knows this, is it possible to do this from within my app? This will help me find the problem.
Thanks to fiddler, this is how I solved it in case anyone else needs to do it in the future (in C# / Xamarin):
private static List<string> m_commandData;
private static void GetOpenedFiles()
{
m_commandData = new List<string>();
RunCommand("lsof");
RunCommand("ps | grep TestNoFilesOpen");
}
private static void RunCommand(string command)
{
string data;
using (Java.Lang.Process process = Java.Lang.Runtime.GetRuntime().Exec(command))
{
Stream stream = process.InputStream;
StreamReader bodyReader = new StreamReader(stream);
data = bodyReader.ReadToEnd();
}
m_commandData.Add(data);
}
private static void WriteCommands()
{
for (int i = 0; i < m_commandData.Count; i++)
{
using (TextWriter tw = new StreamWriter(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/NoFilesOpen/adb_" + i.ToString() + ".txt"))
{
tw.Write(m_commandData[i]);
tw.Close();
}
}
m_commandData = new List<string>();
}
You could use the Unix lsof command in the ADB shell.
1) Start the shell:
$ adb shell
2) Find the process ID of your app:
shell#android:/ $ ps | grep <packagename>
3) List files opened by your app:
shell#android:/ $ lsof <pid>
I am trying to test a sample project called Android.Routing.Offline from OsmSharp.Samples in Github.
After two taps on the screen (the first one gets just the GeoCoordinate) I get a ProtoBuf.ProtoException in the Router.cs
private static IBasicRouterDataSource<CHEdgeData> _graph;
public static void Initialize()
{
var routingSerializer = new CHEdgeDataDataSourceSerializer();
_graph = routingSerializer.Deserialize(
Assembly.GetExecutingAssembly().GetManifestResourceStream(#"Android.Routing.Offline.kempen-big.contracted.mobile.routing"));
}
public static Route Calculate(GeoCoordinate from, GeoCoordinate to)
{
try
{
lock(_graph)
{
var router = Router.CreateCHFrom(_graph, new CHRouter(), new OsmRoutingInterpreter());
// The exception happens here below
var fromResolved = router.Resolve(Vehicle.Car, from);
var toResolved = router.Resolve(Vehicle.Car, to);
if(fromResolved != null && toResolved !=null)
{
return router.Calculate(Vehicle.Car, fromResolved, toResolved);
}
}
}
catch(Exception ex)
{
OsmSharp.Logging.Log.TraceEvent("Router", OsmSharp.Logging.TraceEventType.Critical, "Unhandled exception occured: {0}", ex.ToString());
}
return null;
}
And the exception:
> {ProtoBuf.ProtoException: Invalid wire-type; this usually means you
> have over-written a file without truncating or setting the length; see
> http://stackoverflow.com/q/2152978/23354 at
> ProtoBuf.ProtoReader.ReadSingle () ...
I didnt overwrite the file (kempen-big.contracted.mobile.routing) just added it as a linked file in the project. Any ideas how I can solve this issue?
Well, the first thing to try is to check that the contents of the Stream you are reading (via GetManifestResourceStream) contains exactly the contents you are expecting, and not some wrapper or otherwise-corrupt mess. If you have some checksum algorithm you can run: great! Checking just the .Length would be a great start. Otherwise, you could cheat (just for the purposes of validating the contents) by getting the hex:
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
string hex = BitConverter.ToString(
ms.GetBuffer(), 0, (int)ms.Length);
// dump this string, and compare it to the same output run on the
// oringal file; they should be identical
}
Note that this duplicates the contents in-memory, purely so we can get a byte[] (oversized) to get the hex from - it isn't intended for "real" code, but until you are sure that the contents are correct, all other bets are off. I strongly suspect that you'll find that the contents are not identical to the contents in the original file. Note that I'm also implicitly assuming that the original file works fine in terms of deserialization. If the original file doesn't work: again, all bets are off.
I have been working on this small project in college about changing the default DNS of wifi network to a custom DNS like Google, OpenDNS, Metacert, etc.
I know I have to write a shellscript inside the app's code that would edit the hosts file in the filesystem.
The problem is I have no idea where to start from. I have researched on google for some time and I couldn't figure anything.
If anyone knows about it, please guide me. Please tell me the name of the file to be edited, its location, what commands are required and how to run those commands' combination as a shellscript on a click of a button on the UI of app.
EDIT : I'm stuck only at this. Any help will be greatly appreciated.
I'm not sure about which files you would have to edit but this should give you the tools you need to do that.
The first thing you need to do is root the phone if you haven't already. If it's not rooted, you'll run into an issue like: Working Directory : null environment when running Process.Builder on android
There are a lot of guides available for that online. Install SuperSU as well. In order to run shell commands or scripts you should look at the ProcessBuilder class in Android:
http://developer.android.com/reference/java/lang/ProcessBuilder.html
I've given some sample code below to help you along the way. You could execute this in an OnClick() for a button.
/**
* Runs the shell command.
*
* #param command an array of Strings. command[0] contains the name of the
* shell command. command[1]... contains parameters.
*
* #return the text outputted by the command to stderr or stdout
*/
String runCmd(String[] command, boolean readOutput,
boolean waitForExit) {
ProcessBuilder probuilder = new ProcessBuilder()
.command(command)
.redirectErrorStream(true);
String output = "";
Process process;
// Log.d("MyShellCommand", "Executing " + command[0]);
try {
process = probuilder.start();
} catch(IOException e) {
return e.getMessage();
}
if (readOutput) {
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
try {
while ((line = br.readLine()) != null) {
// Log.d("MyShellCommand", "Read a line: " + line);
output += line + "\n";
}
} catch(IOException e) {
output = e.getMessage();
}
}
I am using Eclipse for Android SDK on Linux, and searching for a way to add the date and starttime of the compilation to one of the xml files. I like to see on the device which build version I am using, without updating this information before every compile step manually.
So far by searching the net I only found hints like "use ant".
I guess I have to use /proc/driver/rtc which is a dynamic "file" provided by the linux kernel that contains real time updated lines with colon separated text named for example "rtc_date" and "rtc_time". Including it and use the app on the device to get the information extracted.
Is there a better way? Like having eclipse either by knowing the time or stripping the information from proc and putting it at compile time in the xml file?
Its my first time using eclipse, so please excuse if I asked something obvious or impossible.
Regards
ct
I am using this code to get application build time. I know this is not outputting to an XML, but if you are trying to get when the app was build, this should work.
private long getAppBuildTime() {
if(cachedAppBuildTime == null) {
try{
ApplicationInfo ai = appContext.getPackageManager().getApplicationInfo(appContext.getPackageName(), 0);
ZipFile zf = new ZipFile(ai.sourceDir);
ZipEntry ze = zf.getEntry("classes.dex");
cachedAppBuildTime = ze.getTime();
log("app build time " + cachedAppBuildTime);
}catch(Throwable t){
return 1;
}
}
return cachedAppBuildTime;
}
The appContext variable in the code is obtained via context.getApplicationContext()
I use the same strategy as yigit except I prefer the MANIFEST.MF file.
This one is regenerated even if a layout is modified (which is not the case for classes.dex).
It result in the following code:
private long mAppBuildTime = -1;
public long getAppBuildTime() {
if (mAppBuildTime == -1) {
try{
ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), 0);
ZipFile zf = new ZipFile(ai.sourceDir);
ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF");
mAppBuildTime = ze.getTime();
zf.close();
}catch(Exception e){
}
}
return mAppBuildTime;
}