Highlighted

File system permissions in Android Marshmallow

Contributor ,
Apr 02, 2016

Copy link to clipboard

Copied

If I set my target SDK to marshmallow and request storage access in my manifest, it will not prompt the user to allow at run time. It will just fail to allow me to access the contents of the local storage. Application directory access is fine but not docs directory. I can change the target sdk back to lollipop but I think users are going to start expecting those granular permissions. Anyone have insight how to do this? I hope we don't need an ane for every permission now...

TOPICS
Development

Views

6.9K

Likes

Translate

Translate

Report

Report
This conversation has been locked.

File system permissions in Android Marshmallow

Contributor ,
Apr 02, 2016

Copy link to clipboard

Copied

If I set my target SDK to marshmallow and request storage access in my manifest, it will not prompt the user to allow at run time. It will just fail to allow me to access the contents of the local storage. Application directory access is fine but not docs directory. I can change the target sdk back to lollipop but I think users are going to start expecting those granular permissions. Anyone have insight how to do this? I hope we don't need an ane for every permission now...

TOPICS
Development

Views

6.9K

Likes

Translate

Translate

Report

Report
Apr 02, 2016 1
Adobe Community Professional ,
Apr 02, 2016

Copy link to clipboard

Copied

Google are changing over to how Apple do things. With iOS you're only asked permission to use a feature at the time you use that feature. Until now on Android you could assume that the user has given permission already.

I have hoped that it will be handled by the OS, but maybe we need to put in the same precautions you have to on iOS. When you attempt to use a feature that might not be approved, you would need to test if the feature is working. A try/catch test would work for most cases. When your code tries to call that feature, marshmallow should ask the user permission, but they might decline, and the catch part of your code would need to tell the user why that feature can't be used.

Something like that!

One thing you should certainly do is test on a real device. It may handle the situation better than I'm guessing it will.

Likes

Translate

Translate

Report

Report
Apr 02, 2016 0
Engaged ,
Apr 03, 2016

Copy link to clipboard

Copied

The new permission system in Marshmallow requires the developer to check if they have permission or not every time code that requires explicit permission is executed and if they don't, then use the new APIs to request that permission and have a graceful fail in place. A native extension could be built to replicate those APIs to request access to X permission where X would be a passed argument. A single ane could cover all the required APIs for handling permissions. One thing to know is that not every permission you use requires the user to grant it. I believe only ones considered "dangerous" require explicit permission.

The simple workaround is to change the target back to Lollipop. Apps running on Marshmallow that are built targeting Lollipop or lower are not held to the "must request permission" standard. If you are not utilizing Marshmallow APIs then there really isn't a reason to change the target. The next version of Android might change that eventually but the current beta doesn't change the current handling of "older" apps that dont make use of new APIs of the latest Android OS.

System Permissions | Android Developers

Likes

Translate

Translate

Report

Report
Apr 03, 2016 1
Contributor ,
Apr 04, 2016

Copy link to clipboard

Copied

The issue is that just because you target lollipop, marshmallow users can still turn off permissions in the settings which may cause unexpected behaviour if you don't account for it. You can likely to a try catch to fail gracefully but it's not going to ask the user for permission, at least from what I have tried, it just fails. This means you will be stcuk on lollipop being able to only fail gracefully. So for instance, you target marshmallow, you will catch an error for not being able to write to file, then what, tell the user to go into the app permission settings? Alternatively, if you target lollipop and the user decides to turn off a permission after the fact, again you are left with a disjointed experience having them re-enable. Users dont want to jump through hoops.

Likes

Translate

Translate

Report

Report
Apr 04, 2016 0
Engaged ,
Apr 04, 2016

Copy link to clipboard

Copied

While it is true that users can disable permissions manually, that is a very active and purposeful process that must be taken to accomplish that and, in my opinion anyway, not something an everyday user is going to do. For example, on my Nexus phone running Marshmallow, I have to launch the Settings app, scroll down and tap "Apps", click the gear icon in top right corner, tap "App permissions", select whatever permission I have concerns about (Storage in your case), and start tapping toggle switches to the "off" position for apps that I don't want to allow the Storage permission for. That is a decent amount of effort for a normal everyday user to go through. Im not positive, but I think I remember seeing that the first time a user does that, they will get a popup warning message saying that could break an app if they start disabling permissions.

You are correct that it will not ask the user for permissions, just fail at the task, and possibly even crash the app depending on what was going on. The developer is supposed to manually check if permission is granted for X permission every time a block of code is executed that would require explicit permission.

If you would like me to, I could look into making a native extension this weekend to replicate that new API that would allow you to check for permission and if you don't have it, present the popup that requests access to the desired permission.

Likes

Translate

Translate

Report

Report
Apr 04, 2016 0
Contributor ,
Apr 04, 2016

Copy link to clipboard

Copied

I do appreciate responses but I am not looking for probabilities or likelihoods, I am looking to see if anyone has encountered a solution to permission management for AIR apps on marshmallow or if Adobe is looking at the issue. The issue I agree is not a big issue today given the marshmallow market share and ability to target lollipop.  BUt what if Google mandates that new or updated apps require the updated permission model? AIR developers are left scrambling to figure it out. Since marshmallow came out over 6 months ago, I think talking about making it work is a proactive approach..

Another valid reason is some users bash ratings based on a permission without even reading why it is required. If they only saw the dangerous permissions when actually needed, I think it could save some negative reviews as well. Lets say an ad network wanted location data but the user paid to have ads removed. In the old model, the app still looks as though it is harvesting location data when in reality it would never do that because it has stopped serving ads.

Likes

Translate

Translate

Report

Report
Apr 04, 2016 0
Engaged ,
Apr 04, 2016

Copy link to clipboard

Copied

I do understand the concern over future changes to Android forcing developers to update their apps to account for the new permission model. Like I said in my previous response, if you would like me to, I can look into creating a native extension this weekend that would replicate the native Java codes ability to check if permission is needed and if it is, present the system popup message requesting access to that permission. I already have a pure Java Android app that has this conditional permission code checking in place to pull from and reference for making a native extension.

Likes

Translate

Translate

Report

Report
Apr 04, 2016 0
Contributor ,
Apr 04, 2016

Copy link to clipboard

Copied

That would be cool if you have something already

Likes

Translate

Translate

Report

Report
Apr 04, 2016 0
Engaged ,
Apr 04, 2016

Copy link to clipboard

Copied

I have some code from an existing app I can use as reference to start building the ANE. Won't promise it will be done this weekend though. Looks like it requires use of the Activity, Context, PackageManager, and Manifest classes as well as an Activity "onRequestPermissionsResult" event to be notified that you were granted or denied the permission you requested access to. Because it requires the use of an Activity event, I will probably have to duplicate an existing ANE I have that gets you access to the main Activity that the AIR app exists in. I have a regular 8:30am to 5:30pm, Monday through Friday job, so that's why I keep mentioning this weekend before I can start.

Likes

Translate

Translate

Report

Report
Apr 04, 2016 0
Engaged ,
Apr 04, 2016

Copy link to clipboard

Copied

Ok, so while I can't start working on this yet while I am at my regular day job, I have been doing some research to get a head start. There is 1 major problem at the moment to making this a full circle API replication of the Android SDK. The "onRequestPermissionsResult" callback event that lets you know if the permission has been granted or denied is part of the Activity class. The Activity class is the top level visual container that represents a "screen". You could equate it to the ActionScript Stage, except every single screen has it's own Stage. In a regular Android app, whenever you tap on a button and there is a visual transition to the next page/screen/phase/frame/etc it is most likely launching a new Activity containing just the visual contents of that screen. For AIR apps on Android, there is just one Activity that the AIR app gets loaded into. Events are a bit different in Android Java from AS3. Events are broadcasted all the time, regardless of you "listening" for them just like in AS3. But the way some of them are handled is by having "on" method callbacks like JavaScript's onload/onchange/onmousedown. In this case, there is the onRequestPermissionsResult method that exists in the base Activity class that gets called every time you run the requestPermission() method. If you want to know the actual results of that request, you have to override the onRequestPermissionsResult() method with your own and that gets you access to the results.

Now to the issue after the long-winded, possibly unnecessary lead up. As far as I know, I cannot override or add methods at runtime to another class. Because the event method that gets you the results is in another class, this leaves me with only two options that I can think of at the moment for getting the results of a permission request.

1) After you call requestPermission(), a timer is setup that would run, lets say, once a second. This timer would run checkPermission() until it gets GRANTED as a result or stops after X amount of time has gone by without getting a GRANTED result. I don't know what this would do performance wise of running this check once a second. It may be harmless and I could be making a bigger deal of it than is needed. This timer method is also something that could just be implemented on your end after running the requestPermission() method.

2) The native extension would launch a new, essentially blank Activity for the purpose of triggering the popup and actually being able to wait for the onRequestPermissionsResult() response, and when it got a response (positive or negative), it would close the Activity and go back to where you where before in your app and let your AS3 code know the response code (GRANTED or DENIED) through an AS3 event. You would also have to add this Activity to the manifest portion of your AppName-app.xml file under the <manifestAdditions> tag. It might be possible to have an invisible Activity launch on top of the AIR Activity that the user would never see. Not sure if any transition effects would still play though.

With that buttload of information to have to digest, what would you prefer as the approach method?

Likes

Translate

Translate

Report

Report
Apr 04, 2016 0
Engaged ,
Apr 09, 2016

Copy link to clipboard

Copied

Since I havent heard back from you yet on what you would like me to do, I went ahead and took the route of forcing the developer using the ANE to have to add an additional line to the "AppName-app.xml" file. This lets it have a full and true event dispatch that can be listened for to respond to. Spent all fracking day working on this and just finally got it to return the results of the permission request. Had to come up with a hack to make it work.

So, currently you can request access to permissions, it will launch an empty activity that is black in color and does NOT animate to it. That empty activity will prompt the user to grant or deny the permission being requested access to. Once they have granted or denied all permissions requested, it will save that data, close the activity, package the data together in a way that AS3 can digest, and then dispatch an event with the data in the event for you to get access to. The blank activity kinda looks like it is part of a modal with the popup requesting access to permissions, so I might leave it as it is for now. At the moment, I havent been able to get it to be transparent even though I have seemingly tried everything I can find to make it be that way.

Need to keep testing it out and test the other methods I added like the "checkPermission()" method that lets you check if you have access to X permission or not. But after much yelling at my computer, it is coming together. So happy I can go to bed now and not be pissed off at not being able to figure it out.

Will update you later once I have it ready to go. Might end up being later in the upcoming week depending.

Likes

Translate

Translate

Report

Report
Apr 09, 2016 0
Engaged ,
Apr 10, 2016

Copy link to clipboard

Copied

Well I think I have the ANE in a good place. You can check for permissions, request permissions, and receive an event containing the results of the permission request after the user has granted or denied them. Been working on the ASDocs most of the day. The docs are incredibly long but there is a lot to digest when it comes to the new permission model in Android 6.0 Marshmallow. Most of the documentation is taken straight from Google's own API documentation with some tweaks, additions, and subtractions in case of things not completely relevant to Flash.

Anyone who would like this native extension should PM me. If you don't get a response within a day, the notification got flagged as spam by Gmail and just reply back here and we will try to work through the PMing in case you don't want to make your email address public. I still have some more work I want to do to the ASDocs, so it may be another day before I am willing to hand it off.

Currently this native extension does not check to see if the app using this ANE is running on a device with Android 6.0 Marshmallow or higher. Calling the requestPermission(), requestPermissions(), or shouldShowRequestPermissionRationale() methods will result in a Java error in the app and possibly/probably crash the app. The checkPermission() method uses an Android API level 1 method for checking if permission is granted or not, so it should have no trouble working on any device. I might update the ANE later to have the other methods account for the OS version, but not right this moment. As long as you use checkPermission() first and don't make unneeded calls to the others, it should be fine.

Likes

Translate

Translate

Report

Report
Apr 10, 2016 0
Contributor ,
Apr 10, 2016

Copy link to clipboard

Copied

Wow, seems like you put a lot of work in to it. I haven't been working much on this at all and if you have a solution, I would be willing to give it a try.

Likes

Translate

Translate

Report

Report
Apr 10, 2016 0
Engaged ,
Apr 11, 2016

Copy link to clipboard

Copied

Put more work into than I expected it would take. Probably spent about 1/3 of the time working on the documentation for it. I'm crazy about trying to have good documentation. Linked is a zip containing a folder with a dirty timeline example FLA just showing the basics of how to use it. Also included in the zip is the ASDocs and of course the ANE itself. Still working on the ASDocs but it is pretty much done for now. Please give them a thorough read as there is a crap ton of information in them. I apologize for any bad english or misspelt words. Let me know your experience with it and any comments you may have.

Dropbox - NativeExtensionTesting.zip

Likes

Translate

Translate

Report

Report
Apr 11, 2016 0
Engaged ,
Apr 15, 2016

Copy link to clipboard

Copied

Let me know if you have had a chance to try it out. I'm eager (obviously) to get some feedback and if you had any issues or not getting it to work and if the docs made sense or not.

Likes

Translate

Translate

Report

Report
Apr 15, 2016 0
Engaged ,
May 01, 2016

Copy link to clipboard

Copied

Hey wade, would you mind posting another link for the ane? I ran into this thread and would love to try it out, I could help you test/write it as well since I am good with Java anes.

Likes

Translate

Translate

Report

Report
May 01, 2016 0
Engaged ,
May 01, 2016

Copy link to clipboard

Copied

Since you asked so nicely and offered to provide feedback

Dropbox - PermissionsManagerTesting.zip

Likes

Translate

Translate

Report

Report
May 01, 2016 0
Contributor ,
May 02, 2016

Copy link to clipboard

Copied

I will get you some feedback too, it just didn't make it into this cycle but another cycle is coming so will post back experience. Do appreciate it,

Cheers

Likes

Translate

Translate

Report

Report
May 02, 2016 0
Engaged ,
May 17, 2016

Copy link to clipboard

Copied

Have you had a chance to use the ANE yet and have any feedback?

Likes

Translate

Translate

Report

Report
May 17, 2016 0
New Here ,
May 21, 2016

Copy link to clipboard

Copied

Hi wadedwalked. Wanted to say thank you for writing the permissions ANE and the very clear documentation. You're a hero.

Feedback: it works!  I'm using Flash Builder, Air 20, testing on a Moto X 2015 phone. Testing permissions for Camera, Record_audio, and write_storage. Can correctly detect if permission has been granted, pop up the request window, get the events, etc.

I did run into one bug that's probably Adobe's fault. Using the ANE to grant access to the Camera, then creating a Video object and loading the device camera into it doesn't work the first time. Invisible. If the app is restarted after permission is granted it works properly, but it never works the first time. Probably just loading the adobe camera libraries does something that assumes there's already access.

I will try to update to Air 22 Beta later and see if that makes a difference.

Likes

Translate

Translate

Report

Report
May 21, 2016 0
Engaged ,
May 23, 2016

Copy link to clipboard

Copied

Interesting result with the camera. I almost wonder if you have to wait for permission to be granted before even creating an object that contains Camera code.

Can you give feedback about the documentation and if there is something in that doesn't make sense or could be improved?

Did you also see the examples of how to present a popup with and without a black screen in the background?

Thank you.

Likes

Translate

Translate

Report

Report
May 23, 2016 0
New Here ,
May 23, 2016

Copy link to clipboard

Copied

The documentation was clear and made sense to me, though I admit I just skipped to the example and implemented it from there. I also benefitted from reading this discussion thread first, in particular the point about how you have to start another activity and why it needs to be included in the xml manifest.

I did find the PermissionGroups a bit underdocumented. I get that they're basically preformatted permissions that you can pass to requestPermissions, but I don't really know how they map, so it was easier just to make the groups myself. Like, is there a difference from "Permissions.CAMERA" and "PermissionGroups.CAMERA?"  (I used the former, that's not part of my problem, is it?)

And I wasn't sure if PermissionsManager.isSupported would return true or false on a non-android 6 device. Generally not too sure what I should expect on non 6.0 devices.

I did see the examples on different popup themes, works well. The translucent one on my device has a slightly darker title bar area (which is blank), but most users probably won't notice it.

Thanks!

Likes

Translate

Translate

Report

Report
May 23, 2016 0
Engaged ,
May 23, 2016

Copy link to clipboard

Copied

I can certainly see your point on the PermissionGroups class. A majority of the documentation is taken straight from Google's own Android API documentation (since this is basically replicating the same functionality) and then tweaked some for context within this native extension. Taking a look at it again after so long, I can definitely see where more explanation could be given to explain the differences. In the case of the Camera group, it only has the Camera permission. In the case of something like the Contacts group, that includes the READ_CONTACTS, WRITE_CONTACTS, and GET_ACCOUNTS. I should probably add all that to the documentation of each group to say what each group includes.

System Permissions | Android Developers

The PermissionsManager.isSupported call at the moment, strictly checks to see if the code is running on an Android device or not, no version checking is being made. It is basically just using the flash.system.Capabilities.version property to check to see if contains "AND" for Android. As far as I know there is no way, within Flash/AS3, to detect the version of Android. I would need to change it into or add a new property/method that calls native code to determine if it is running on Android 6.0 or higher.

Would you be able to take screenshots of both examples so I can see what you mean about the title bar? I may or may not be able to adjust that.

Likes

Translate

Translate

Report

Report
May 23, 2016 0
New Here ,
May 24, 2016

Copy link to clipboard

Copied

Sure, I'll try to do that tonight. The title bars are subtle though, not a big deal.

I can probably confirm that the camera bug is something to do with Adobe's implementation; I was able to use Distriqt's Camera ANE after using  your PermissionManager.  Incidentally, their Camera ANE also comes with a permissions module (though it only handles relevant permissions) and they ask for the same activity added to the manifest. I assume they came up with the same hack you did.

Likes

Translate

Translate

Report

Report
May 24, 2016 0
New Here ,
May 25, 2016

Copy link to clipboard

Copied

Hi wadedwalker,

Here's a screenshot and a bug report for you: https://dl.dropboxusercontent.com/u/517645/Screenshot_20160525-105906.png

So those aren't title bars near the top, but are actually textfields. There's a blinking cursor in the top left one. The bug is that on some android devices (e.g. Nexus 9), this actually causes the keyboard to popup, which obscures the permissions as well. Does that make sense to you?

Ah, and on documentation questions: how do I provide a rationale for a permissions request?

Once again, thanks so much for making this!

Likes

Translate

Translate

Report

Report
May 25, 2016 0
Engaged ,
May 25, 2016

Copy link to clipboard

Copied

In regards to the textfields and the keyboard, does your app create those and something is triggering them to appear when the permission request is made? I guess I am a tad confused on what is going on in that area.

For the rationale question, this one is a bit annoying. This might end up being long winded, so grab some cafe. The "shouldShowRequestPermissionRationale()" method returns a true or false value depending on multiple situations. If the user has:

1) Never seen the permission request prompt for X permission OR the user has both denied the request previously AND checked the "Never ask again", then that method will return false.

2) Has seen the permission request prompt for X permission before AND denied the permission previously, this method will return true.

In the case of the user denying the permission and having checked "Never ask again", it sucks because you as the developer don't know if they are getting the permission for the first time or are not getting it at all. In that instance, you basically will need to create a SharedObject to keep track of all the permissions you have requested and the result given from each request so that you may know how your UI should respond to the user trying to use features that require explicit permission.

At some point I will probably refine my examples to go ahead and include the "shouldShowRequestPermissionRationale()" functionality and even include the SharedObject preference tracking of which permissions have or have not been requested so the developer will know how to communicate with the user.

At the moment, I have been focusing my time on working on a desktop AIR app, so I may not get back to this ANE for a week or so, but I greatly appreciate the feedback and it is all going to be taken into consideration when improving the API and the documentation.

Likes

Translate

Translate

Report

Report
May 25, 2016 0
New Here ,
May 25, 2016

Copy link to clipboard

Copied

Thanks for the explanation! And please, don't feel obligated to work on this or anything.

I don't think my app makes the text fields--you can actually see a bit of my app's flash-level UI under the translucent textfield, so it's probably part of the activity. I'll play around with a simplified text case and perhaps try distriqt's implementation to see if they have the same bug. It does seem very weird.

Likes

Translate

Translate

Report

Report
May 25, 2016 0
New Here ,
May 25, 2016

Copy link to clipboard

Copied

Quick note-- the Distriqt Camera ANE, which I believe has pretty much the same hack, doesn't have the same textfield problem. Is it possible that it's part of the activity template you or using? I have no idea how these things are put together.

Likes

Translate

Translate

Report

Report
May 25, 2016 0
Engaged ,
May 25, 2016

Copy link to clipboard

Copied

It shouldnt be. My activity is blank other than an invisible LinearLayout which I think a layout is required so I can make it be invisible. Below is the activity's XML and Java files used. I also looked at the ActionScript files and I dont even have an import of any of the flash.text.* package.

Does the dummy FLA I included in the ZIP do the same thing? If you want to send me some of your files related to the class that triggers the permission request, I can take a look at it. Also, does it do that on every device you have tested with Marshmallow?

request_permissions_activity.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

  xmlns:android="http://schemas.android.com/apk/res/android"

  android:layout_width="match_parent"

  android:layout_height="match_parent"

  android:orientation="vertical"

  android:theme="@android:style/Theme.Translucent"

  style="@android:style/Theme.Translucent"

  android:background="#00000000"

  android:visibility="gone">

</LinearLayout>

RequestPermissionsActivity.java

package com.wadedwalker.nativeExtension.permissions;

import com.wadedwalker.nativeExtension.permissions.R;

import android.annotation.TargetApi;

import android.app.Activity;

import android.os.Build;

//import android.graphics.Color;

//import android.graphics.drawable.ColorDrawable;

import android.os.Bundle;

@TargetApi(23)

final public class RequestPermissionsActivity extends Activity {

  @Override

  final protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    //setTheme(android.R.style.Theme_Translucent_NoTitleBar);

    setContentView(R.layout.request_permissions_activity);

    //setVisible(false);

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {

      if (getActionBar() != null) {

        getActionBar().hide();

      }

    }

    //getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

    this.requestPermissions(getIntent().getExtras().getStringArray("permissions"), android.os.Process.myUid());

  }

  @Override

  final public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    String results = "";

    for (int i = 0; i < permissions.length; i += 1) {

      results += permissions + "=" + grantResults;

      if (i < permissions.length-1) {

        results += ",";

      }

    }

    PermissionsManager.context.dispatchStatusEventAsync("onRequestPermissionsResult", results);

    finish();

    this.overridePendingTransition(0,0);

  }

}

Likes

Translate

Translate

Report

Report
May 25, 2016 0