3

For a very limited number of users, my app is crashing with the following:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.cloud3squared.meteogram.pro/com.cloud3squared.meteogram.MeteogramWidgetConfigureActivity}:
        java.lang.SecurityException: Permission denial: writing to settings requires android.permission.WRITE_SETTINGS
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2693)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758)
    at android.app.ActivityThread.access$900(ActivityThread.java:177)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:5942)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1388)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)
Caused by: java.lang.SecurityException: Permission denial: writing to settings requires android.permission.WRITE_SETTINGS
    at android.os.Parcel.readException(Parcel.java:1540)
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:185)
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
    at android.content.ContentProviderProxy.call(ContentProviderNative.java:643)
    at android.provider.Settings$NameValueCache.putStringForUser(Settings.java:1204)
    at android.provider.Settings$System.putStringForUser(Settings.java:1505)
    at android.provider.Settings$System.putIntForUser(Settings.java:1616)
    at android.provider.Settings$System.putInt(Settings.java:1607)
    at android.app.WallpaperManager$Globals.getDefaultWallpaperLocked(WallpaperManager.java:483)
    at android.app.WallpaperManager$Globals.peekWallpaperBitmap(WallpaperManager.java:303)
    at android.app.WallpaperManager.getDrawable(WallpaperManager.java:550)
    at com.cloud3squared.meteogram.ax.c(Unknown Source)
    at com.cloud3squared.meteogram.MeteogramWidgetConfigureActivity.a(Unknown Source)
    at com.cloud3squared.meteogram.MeteogramWidgetConfigureActivity.c(Unknown Source)
    at com.cloud3squared.meteogram.MeteogramWidgetConfigureActivity.onCreate(Unknown Source)
    at android.app.Activity.performCreate(Activity.java:6288)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
    ... 10 more

Whilst a solution to the issue is given in the crash report (just add android.permission.WRITE_SETTINGS to the Manifest), it troubles me that I need to, because my app doesn't knowingly write to settings, and the vast majority of users don't have any issue without the permission.

It is particularly troubling because the docs say that:

There are a couple of permissions that don't behave like normal and dangerous permissions. SYSTEM_ALERT_WINDOW and WRITE_SETTINGS are particularly sensitive, so most apps should not use them. If an app needs one of these permissions, it must declare the permission in the manifest, and send an intent requesting the user's authorization. The system responds to the intent by showing a detailed management screen to the user.

I want to do everything I can to avoid having this permission in my app, given that it's apparently "dangerous", and having to request this permission at runtime is enough to scare off even non-tin-hatted users. I really don't need to write to settings... I write to SharedPreferences and that's about it.

So is there any way of avoiding this problem without requiring the permission?

EDIT to add code as per comment below (this function is called from my Activity):

static String getAverageWallpaperColour(Context context) {
    final WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
    final Drawable wallpaperDrawable = wallpaperManager.getDrawable();
    Bitmap bmp = ((BitmapDrawable)wallpaperDrawable).getBitmap();
    int color = getAverageColour(bmp); // details of function not relevant... just reads the bmp to work out an average colour
    return colorToHexString(color); // details of function not relevant... just converts into color to String in particular format
}
Community
  • 1
  • 1
drmrbrewer
  • 11,491
  • 21
  • 85
  • 181
  • My guess is that You are touching something inside a class that requires usually that permission. You should post Your .MeteogramWidgetConfigureActivity or the relevant part of code.... – Opiatefuchs Jan 19 '16 at 13:52
  • Yeah it must be inadvertent / indirect. It's just odd that it only seems to throw an exception on some devices... if I was touching something that requires the permission, it would surely be needed for every user on every device? I'll dig into the code to see if I can pull out something relevant that might be the cause. – drmrbrewer Jan 19 '16 at 13:56
  • I don´t really know if this makes a difference to Android marshmallow. Since this API, You have to possibly grant the permissions at runtime, if the user has disabled that permission for Your app. Maybe this is not the behaviour at lower APIs and this API still does accept that. Or..... – Opiatefuchs Jan 19 '16 at 13:59
  • Do You execute some stuff with wallpaper in Your app? Have You declared the permission SET_WALLPAPER inside Your manifest? – Opiatefuchs Jan 19 '16 at 14:00
  • All I'm doing with wallpaper is *getting* it, using code along the lines of http://stackoverflow.com/a/9939358/4070848. The crash happens on the Config Activity for my AppWidget, but curiously the Activity doesn't crash the *first* time it is opened (when the widget is first configured and placed) but only when the Activity is opened for the second time (from the running widget). – drmrbrewer Jan 19 '16 at 14:14
  • Seems that the `Settings$System.putInt` and `putIntForUser` and `putStringForUser` would be the culprits, but I have no idea why those are being called, and from which line in my own code -- I find these crash logs very difficult to interpret sometimes. – drmrbrewer Jan 19 '16 at 14:22
  • can You post this part of Your activity? Maybe four eyes see more than one. And I read about that Exception that it is a KitKat error on some devices (for example samsung)....maybe You can´t handle it.. – Opiatefuchs Jan 19 '16 at 14:23
  • This post here may shed some light... similar to me... http://developer.samsung.com/forum/thread/securityexception-calling-wallpapermanagergetdrawable/202/279150 --- seems like when wallpaper is set to null there is an issue. I'll add the relevant code to my question. – drmrbrewer Jan 19 '16 at 14:28
  • I've added some code to the question. I'm pretty confident now that the issue is caused by that bit of code... I mentioned above that the crash only happens on the second opening of the `Activity`, and in fact that bit of code isn't executed on the first opening... so it kinda points to that as being the culprit. Maybe a null wallpaper on some devices causes the OS to try and create a (temporary) wallpaper or something, thereby trying to write to Settings. So just querying the wallpaper (even if not trying to add/change the wallpaper) might cause the permission exception. – drmrbrewer Jan 19 '16 at 15:01
  • Is it best to just wrap the call to the "offending" function in a `try`/`catch` for `java.lang.SecurityException` and then handle that exception accordingly? – drmrbrewer Jan 19 '16 at 15:06
  • No, I think it´s better to avoid any kind of exception also if it could be catched. I recommend to exclude that the wallpaperDrawable could be null with: if(wallpaperDrawable!=null){}. In that case if it is null, return some HexColor string as default. – Opiatefuchs Jan 19 '16 at 16:21
  • But if `wallpaperDrawable` is `null` then why is a `SecurityException` thrown (permission related) and not a `NullPointerException`? – drmrbrewer Jan 19 '16 at 17:37
  • This is one of the secrets of android :) ...sorry, I don´t know why, but my assumption is, that this comes from the "translation" of android system for the different devices. If this only happens on samsung, then samsung developers had overlooked something. – Opiatefuchs Jan 20 '16 at 07:05
  • Update: the user affected has just told me that they have a *live wallpaper* on their homescreen, so this would explain when the method causes an issue, because it's not a normal bitmap. I still don't really know why this would lead to an attempt to write to Settings... but at least I now know how to catch the problem and prevent such an attempt. – drmrbrewer Jan 22 '16 at 11:30
  • So please post the answer to the problem, I think it could happen other poeple too.... :) – Opiatefuchs Jan 22 '16 at 11:32

1 Answers1

0

I have now determined that the problem lies in the code snippet I added to the Question in an edit:

static String getAverageWallpaperColour(Context context) {
    final WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
    final Drawable wallpaperDrawable = wallpaperManager.getDrawable();
    Bitmap bmp = ((BitmapDrawable)wallpaperDrawable).getBitmap();
    int color = getAverageColour(bmp); // details of function not relevant... just reads the bmp to work out an average colour
    return colorToHexString(color); // details of function not relevant... just converts into color to String in particular format
}

Basically, a user for whom the app was crashing has told me that they have a live wallpaper active on their device, rather than a conventional static wallpaper.

So, for some reason I still don't understand, either in trying to get the drawable, or get a bitmap from the drawable, this somehow leads to the Settings$System.putInt and Settings$System.putIntForUser and Settings$System.putStringForUser log entries, which in turn leads to the SecurityException: Permission denial error (attempting to write to Settings without permission).

This post is along similar lines, reporting that the error seemed to be associated with a situation in which the "system wallpaper was null". Quite why there isn't just a NullPointerException when I try to do something unspeakable with null, and instead there is a dire permissions error after apparently attempting to tamper with the Settings, I don't know.

drmrbrewer
  • 11,491
  • 21
  • 85
  • 181