Wednesday, October 28, 2015

Tips & Tricks: Dangerous Permissions, Part 2

This entry is going to focus on what happens when a User revokes a permission while the app is backgrounded.  

The user can change a permission status at anytime by going to the settings app=>apps=>pick any app=>Permissions.

If the user grants a permission this way then nothing special happens.  Your app (if it is up) just keeps chugging along.

The real interesting stuff happens when the user revokes a permission for your app.  If the app isn’t running, then nothing happens.  If the app is running, then it will fully re-create.  This means that the old application is destroyed.  It will take down all of its Services and all static data is lost.  It will then start a new version of the app.   This will call the Application objects onCreate.  

The really fun problem comes in when the user foregrounds your app again.  The system will restore to its last known point using any saved bundles.

This can leave your app in a weird state: where it has restarted, but also restored to a state without any breadcrumbs.  As one of my coworkers put it: “It’s like a game where you dies and regenerate right in front of the Boss’s door without any of the goodies you built up along the way”.  Pretty apt way to describe it.

For most apps, this isn’t a big deal because they automatically create the database, etc. as part of app start.  However, it is important to understand to avoid painting yourself into a corner data wise.

I included an demonstration of this in my Example using counters.  I maintain a static counter and a local counter.

   1:  
   2:     /**
   3:      * The static counter.  This is here to show that static data resets when revoking a permission
   4:      * via the settings app.
   5:      */
   6:     private static int sStaticCounter;
   7:  
   8:  
   9:     /**
  10:      * a local counter.  This is here to show that activity restores when revoking a permission via
  11:      * the settings app.
  12:      */
  13:     private int mCounter;
  14:  

  Each time the Toolbar’s “Click Me” is clicked I increment both counters.



   1:  
   2:  
   3:     @Override
   4:     public boolean onOptionsItemSelected(MenuItem item) {
   5:         int id = item.getItemId();
   6:  
   7:         //noinspection SimplifiableIfStatement
   8:         if (id == R.id.action_settings) {
   9:             ++mCounter;
  10:             ++sStaticCounter;
  11:  
  12:             updateCounterUi();
  13:             requestPermissions();
  14:             return true;
  15:         }
  16:  
  17:         return super.onOptionsItemSelected(item);
  18:     }

The value of the counters is shown in the UI:


device-2015-10-10-211138


The local counter value is saved into save state bundle in onSaveInstance().  This value is then restored in the activity’s onCreate method.


So here is the recreation:



  • Start the app, notice that both counters are 0.

start



  • Click the “Click Me”, notice that both counters are 1.

device-2015-10-10-211138



  • Click the “Click Me” in the Snackbar. 
  • Grant the permission.
  • Background the app
  • Start the Settings=> App=> PermissionsExample=>Permissions.
  • Notice that the “Contacts” permission is granted
  • Turn off the “Contacts” permission.

    • At this point, the app recreates

  • Foreground the PermissionsExample app.  Notice that the static data is reset, but the data from the savedInstance bundle restored correctly.

device-2015-10-10-212749


A word of Advice


Regardless of whether a permission is not dangerous, I always check the permission status under the app settings.  This is because some normal permissions are grouped under an umbrella permission that can be revoked.   If any permissions shows up then you have to be prepared for the above scenario to occur. 


For instance in the example, I am using the GET_ACCOUNTS permissions which is technically normal*.  However, it is grouped under the “Contacts” group, which can be revoked. That means that I have to deal with the above situations even though the specific permission I am using cannot be revoked.


*There is an issue reported to Google to make GET_ACCOUNTS dangerous.  As of this writing the change is implemented, but not delivered.


Example


The example can be found in my GitHub PermissionsExample repository.

No comments :

Post a Comment