As part of my app I get a list of apps installed on the device by using ApplicationPackageManager.getInstalledApplications but for some users I get crash reports saying that
TransactionTooLargeException at android.osBinderProxy.tranasact(Native Method)
Can anyone think why I'd get this?
I've found that this was solved on Android 5.1 (proof here, search for "Fix package manager TransactionTooLargeExceptions") as it was reported on multiple places:
https://code.google.com/p/android/issues/detail?id=95749
https://code.google.com/p/android/issues/detail?id=93717
https://code.google.com/p/android/issues/detail?id=69276
However, I wanted to solve this for pre-5.1, so I've come up with a solution (and suggested Google to put it on the support library, here) . Here's a short code version of what I've suggested:
public static List<PackageInfo> getInstalledPackages(Context context,int flags)
{
final PackageManager pm=context.getPackageManager();
try
{
return pm.getInstalledPackages(flags);
}
catch(Exception ignored)
{
//we don't care why it didn't succeed. We'll do it using an alternative way instead
}
// use fallback:
Process process;
List<PackageInfo> result=new ArrayList<>();
BufferedReader bufferedReader=null;
try
{
process=Runtime.getRuntime().exec("pm list packages");
bufferedReader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while((line=bufferedReader.readLine())!=null)
{
final String packageName=line.substring(line.indexOf(':')+1);
final PackageInfo packageInfo=pm.getPackageInfo(packageName,flags);
result.add(packageInfo);
}
process.waitFor();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(bufferedReader!=null)
try
{
bufferedReader.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
return result;
}
What it does it to try using the official way first, and then, if failed, it fetches the package names using ADB command, and get the information of each of the apps, one after another.
It is much slower than the official one, but it didn't crash for me. I've tested it on Android emulators (2.3.x till 5.0.x, including), and on real devices too.
The time it took on my device (Galaxy S3 with custom rom of Android 5.1) is 1375-2012 ms (on 197 apps total) compared to 37-65 ms using the official way .
EDIT: people claim here that it's not fixed on Android 5.1 . I hope that it got fixed on Android 6 .
This exception is kind of difficult to reproduce under normal circumstances. You will get this exception when there IPC memory is exhausted when transferring data. This can occur in both cases, where a service is trying to place data to client or a client is sending data to service. Most probably some of your users might have installed huge number of application, which results in a data size greater than 1MB (which is the size of IPC buffer).
I am afraid in this case, you will not be do anything better. But if you are doing something like, applyBatch, you can separate one large transaction to multiple smaller transactions.
Also have a look at this thread What to do on TransactionTooLargeException
Related
I'm working on an app that uses Android's MediaMuxer for recording the screen. Using Crashlytics, a significant number of users have the "Failed to stop the muxer" crash, but I can't reproduce it locally on any of my devices. According to another question, the MPEG4Writer logs generated while MediaMuxer is running should indicate what the source of the problem is, but since I'm unable to reproduce it locally, I need to collect those logs remotely and pass them over to Crashlytics.
So here's my problem: MediaMuxer and MPEG4Writer are system classes, so obviously I can't edit them to add Crashlytics.log() lines. I've thought of having the app read the Logcat output and storing all entries containing MPEG4Writer, which are then sent to Crashlytics if the muxer crashes, using this implementation as a base. Here's my code:
public class LogRetriever extends Thread {
private static final String TAG = LogRetriever.class.getCanonicalName();
public static ArrayList<String> logStorage = new ArrayList<>();
private AtomicBoolean mLoggingActive = new AtomicBoolean(true);
#Override
public void run() {
try {
String[] command = new String[] { "logcat" };
Process process = Runtime.getRuntime().exec(command);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while (mLoggingActive.get() && ((line = bufferedReader.readLine()) != null)){
if(line.contains("MPEG4Writer")) {
logStorage.add(line);
}
}
}
catch (IOException ex) {
Log.e(TAG, "start failed", ex);
}
}
public void stopLogging() {
mLoggingActive.set(false);
}
}
Using the above method, I only seem to get the first four log lines generated by MPEG4Writer. The rest are visible through Android Studio's logcat, but aren't collected by my code. I've also tried this library which seems to do the same thing, but again, same problem, only the first 4 lines are collected. I suspect that MediaMuxer is creating its own process after those 4 lines, at which point I can no longer read its logcat output because my LogRetriever class is now in a different process. So how am I supposed to collect those logs? Am I taking the wrong approach here?
So how am I supposed to collect those logs?
Generally, unless you are working for a device manufacturer, you don't collect those logs.
First, accessing LogCat at runtime has never been officially supported; hence, the clunky "fork logcat" approach that you have to take.
Beyond that, you need the READ_LOGS permission to get more than what you are. That permission has signature|privileged|development for the protectionLevel, meaning that ordinary apps cannot hold that permission.
This is for privacy reasons. READ_LOGS gives you access to all of LogCat, and lots of apps (and some system processes) log information that may be sensitive.
Is there a way to view the log on a tablet running 4.4? I've downloaded several apps like aLogCat and none of them show what my app writes out with S.o.p or Log.d. I have an intermittent bug that gives the Unfortunately appname has stopped message.Is there any way to view the log after this event without having to connect to a PC and use the adb program?
What other ways are there to get debug output? Would trapping the System.out and System.err classes get the stack trace?
Thanks,
Norm
You're focussing on tring to read out logcat, but there are better solutions for reading crash logs. My personal preference is Crashlytics, which automatically logs fatal exceptions and provides mechanisms for logging other messages.
The way all these crash reporters work, is by defining a UncaughtExceptionHandler:
Thread.setDefaultUncaughtExceptionHandler(
new MyUncaughtExceptionHandler(this));
If you prefer to use your own solution, you may want to look into using this. See this related question for more details.
Is there a way to view the log on a tablet running 4.4?
No, sorry. An app can only see its own log messages, not those from other apps. Hence, a third-party log viewer cannot see your app's messages.
Is there any way to view the log after this event without having to connect to a PC and use the adb program?
Use any standard crash management library, like ACRA, or services like Crashlytics, BugSense, etc.
The AIDE Application (Android Integrated Development Environment) allows one to develop android Apps directly on android device.
One particular feature is to read the logcat.
You can get it here https://play.google.com/store/apps/details?id=com.aide.ui
Here's the code I've put in the program. It seems to work:
// Define inner class to handle exceptions
class MyExceptionHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e){
java.util.Date dt = new java.util.Date();
String fn = LogFilePathPfx + "exception_" + sdf.format(dt) + ".txt";
try{
PrintStream ps = new PrintStream( fn );
e.printStackTrace(ps);
ps.close();
System.out.println("wrote trace to " + fn);
e.printStackTrace(); // capture here also???
SaveStdOutput.stop(); // close here vs calling flush() in class
}catch(Exception x){
x.printStackTrace();
}
lastUEH.uncaughtException(t, e); // call last one Gives: "Unfortunately ... stopped" message
return; //???? what to do here
}
}
lastUEH = Thread.getDefaultUncaughtExceptionHandler(); // save previous one
Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());
I feel like I am on crazy pills right now. A specific part of my application has been working just fine for several days, and today it just stopped working and I can not figure out why. This part of my code used to output the total data since boot that each specific app has sent and received. Now, the values always show up as 0.
A couple things that may or may not be affecting this:
1.) My Nexus 4 was just updated to Android 4.3 today, but I doubt this is an issue because this worked just fine right after I updated.
2.) With the Android API 18 update, some methods from the Traffic Stats API are now deprecated, but these are methods I am not even using, so this should have no effect.
http://developer.android.com/reference/android/net/TrafficStats.html
All help is greatly appreciated.
PackageManager packageManager=this.getPackageManager();
List<ApplicationInfo> appList=packageManager.getInstalledApplications(0);
for (ApplicationInfo appInfo : appList) {
String appLabel = (String) packageManager.getApplicationLabel(appInfo);
int uid = appInfo.uid;
Log.d("data", String.valueOf(TrafficStats.getUidRxBytes(uid) + TrafficStats.getUidTxBytes(uid)));
Update[January 23, 2014]: Testing the getUidRxBytes() and getUidTxBytes() on my Nexus 4 running Android 4.4.2 shows that the values are no longer 0, but are reporting the correct statistics.
I have reported the issue to the AOSP issue tracker: here
I have also created an alternate solution to the problem which I have pasted below:
private Long getTotalBytesManual(int localUid){
File dir = new File("/proc/uid_stat/");
String[] children = dir.list();
if(!Arrays.asList(children).contains(String.valueOf(localUid))){
return 0L;
}
File uidFileDir = new File("/proc/uid_stat/"+String.valueOf(localUid));
File uidActualFileReceived = new File(uidFileDir,"tcp_rcv");
File uidActualFileSent = new File(uidFileDir,"tcp_snd");
String textReceived = "0";
String textSent = "0";
try {
BufferedReader brReceived = new BufferedReader(new FileReader(uidActualFileReceived));
BufferedReader brSent = new BufferedReader(new FileReader(uidActualFileSent));
String receivedLine;
String sentLine;
if ((receivedLine = brReceived.readLine()) != null) {
textReceived = receivedLine;
}
if ((sentLine = brSent.readLine()) != null) {
textSent = sentLine;
}
}
catch (IOException e) {
}
return Long.valueOf(textReceived).longValue() + Long.valueOf(textReceived).longValue();
}
The TrafficStats class get the information about network traffic from the /proc/uid_stat/<uid> directory. This contains information about the tcp, udp bytes and packets sent and received. If the files are not present the TrafficStats class can't get the network stats. You can check if the files are present, If not you are out of luck and should look for other way.
If the files are present you can try to read it yourself.
Also the getUidTxBytes() and getUIDRxBytes() report only the TCP traffic and miss UDP traffic. So if your app is doing lots of UDP traffic (like voip) then you'll not get any info.
There is already a bug filed for this : https://code.google.com/p/android/issues/detail?id=32410
I have done some detailed research about this, and to clarify some details, since Android 4.3 the TrafficStats API has changed in the way it extracts details from the device.
Prior to Android 4.3 the UID traffic stats were available for TCP and UDP and included API for bytes and packets & sent and received. That data was extracted from the /proc/uid_stat/[pid]/* files.
In Android 4.3, the developers has decided to switch to a better and more safe API, using the xt_qtaguid UID statistics, which is part of the netfilter kernel module in Linux.
This API (procfs) allows access based on process UID, and this is why when you try to access to TrafficStats API in Android=>4.3 you will get zero information for not-own UID.
btw, the commit that caused the issue is the following:
https://github.com/android/platform_frameworks_base/commit/92be93a94edafb5906e8bc48e6fee9dd07f5049e
*Improve TrafficStats UID APIs.
Deprecate transport layer statistics, leaving only the summarized
network layer statistics.
Improve documentation to be clear about layers where measurements
occur, and their behavior since boot. Under the hood, move to using
xt_qtaguid UID statistics.
Bug: 6818637, 7013662
Change-Id: I9f26992e5fcdebd88c671e5765bd91229e7b0016*
I'm writing an Android app which receives information from a Bluetooth device. Our client has suggested that the Bluetooth device (which they produce) will change its name depending on certain conditions - for the simplest example its name will sometimes be "xxx-ON" and sometimes "xxx-OFF". My app is just supposed to seek this BT transmitter (I use BluetoothAdapter.startDiscovery() ) and do different things depending on the name it finds. I am NOT pairing with the Bluetooth device (though I suppose it might be possible, the app is supposed to eventually work with multiple Android devices and multiple BT transmitters so I'm not sure it would be a good idea).
My code works fine to detect BT devices and find their names. Also, if the device goes off, I can detect the next time I seek, that it is not there. But it seems that if it is there and it changes name, I pick up the old name - presumably it is cached somewhere. Even if the bluetooth device goes off, and we notice that, the next time I detect it, I still see the old name.
I found this issue in Google Code: here but it was unclear to me even how to use the workaround given ("try to connect"). Has anyone done this and had any luck? Can you share code?
Is there a simple way to just delete the cached names and search again so I always find the newest names? Even a non-simple way would be good (I am writing for a rooted device).
Thanks
I would suggest 'fetchUuidsWithSdp()'. It's significance is that, unlike the similar getUuids() method, fetchUuidsWithSdp causes the device to update cached information about the remote device. And I believe this includes the remote name as well as the SPD.
Note that both the methods I mentioned are hidden prior to 4.0.3, so your code would look l ike this:
public static void startServiceDiscovery( BluetoothDevice device ) {
// Need to use reflection prior to API 15
Class cl = null;
try {
cl = Class.forName("android.bluetooth.BluetoothDevice");
} catch( ClassNotFoundException exc ) {
Log.e(CTAG, "android.bluetooth.BluetoothDevice not found." );
}
if (null != cl) {
Class[] param = {};
Method method = null;
try {
method = cl.getMethod("fetchUuidsWithSdp", param);
} catch( NoSuchMethodException exc ) {
Log.e(CTAG, "fetchUuidsWithSdp not found." );
}
if (null != method) {
Object[] args = {};
try {
method.invoke(device, args);
} catch (Exception exc) {
Log.e(CTAG, "Failed to invoke fetchUuidsWithSdp method." );
}
}
}
}
You'll then need to listen for the BluetoothDevice.ACTION_NAME_CHANGED intent, and extract BluetoothDevice.EXTRA_NAME from it.
Let me know if that helps.
Can I query the Android Market for the latest version of my application in code? I would like to show an update notification for the user when a new version is available.
Related questions:
Process in updating my app in the market
Is there a way to automatically update application on Android?
Android Market Application Updates
I bumped into the same problem here. So I thought... why not use AppBrain.
I wrote a small function that gets your latest app version from the AppBrain website.
public String getLatestVersionNumber()
{
String versionNumber = "0.0.0";
try
{
Document doc = Jsoup.connect("http://www.appbrain.com/app/wallpaper-switch/com.mlevit.wallpaperswitch").get();
Elements changeLog = doc.select("div.clDesc");
for (Element div : changeLog)
{
String divText = div.text();
if (divText.contains("Version"))
{
return divText.split(" ")[1];
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
return versionNumber;
}
I use the jsoup Java HTML Parser to parse the HTML and from there on it's pretty simple.
Once you've retrieved it, since it's a String the best way I can think of to compare two versions together is to remove the full stops (.) that way your version number would go from say 1.1.2 to 112 then it's just a simple matter of comparing two Integers.
I know of no way to make that query, sorry.
I found this one which might be useful for some people
https://code.google.com/p/android-query/wiki/Service
I found a work around that may just work. Name you app like this:
My App - V.1.12
Now you can quay your app page on the market. The title will be: My App - V.1.12 - Android Apps on Google Play
Assuming that you change the app name version on each release, this will work.