permissions.rst (15573B)
1 .. -*- Mode: rst; fill-column: 80; -*- 2 3 ============================= 4 Working with Site Permissions 5 ============================= 6 7 When a website wants to access certain services on a user’s device, it 8 will send out a permissions request. This document will explain how to 9 use GeckoView to receive those requests, and respond to them by granting 10 or denying those permissions. 11 12 .. contents:: :local: 13 14 The Permission Delegate 15 ----------------------- 16 17 The way an app interacts with site permissions in GeckoView is through 18 the 19 `PermissionDelegate <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html>`_. 20 There are three broad categories of permission that the 21 ``PermissionDelegate`` handles, Android Permissions, Content Permissions 22 and Media Permissions. All site permissions handled by GeckoView fall 23 into one of these three categories. 24 25 To get notified about permission requests, you need to implement the 26 ``PermissionDelegate`` interface: 27 28 .. code:: java 29 30 private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate { 31 @Override 32 public void onAndroidPermissionsRequest(final GeckoSession session, 33 final String[] permissions, 34 final Callback callback) { } 35 36 @Override 37 public void onContentPermissionRequest(final GeckoSession session, 38 final String uri, 39 final int type, final Callback callback) { } 40 41 @Override 42 public void onMediaPermissionRequest(final GeckoSession session, 43 final String uri, 44 final MediaSource[] video, 45 final MediaSource[] audio, 46 final MediaCallback callback) { } 47 } 48 49 You will then need to register the delegate with your 50 `GeckoSession <https://mozilla.github.io/geckoview/https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.html>`_ 51 instance. 52 53 .. code:: java 54 55 public class GeckoViewActivity extends AppCompatActivity { 56 @Override 57 protected void onCreate(Bundle savedInstanceState) { 58 super.onCreate(savedInstanceState); 59 60 ... 61 62 final ExamplePermissionDelegate permission = new ExamplePermissionDelegate(); 63 session.setPermissionDelegate(permission); 64 65 ... 66 } 67 } 68 69 Android Permissions 70 ~~~~~~~~~~~~~~~~~~~ 71 72 Android permissions are requested whenever a site wants access to a 73 device’s navigation or input capabilities. 74 75 The user will often need to grant these Android permissions to the app 76 alongside granting the Content or Media site permissions. 77 78 When you receive an 79 `onAndroidPermissionsRequest <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#onAndroidPermissionsRequest(org.mozilla.geckoview.GeckoSession,java.lang.String[],org.mozilla.geckoview.GeckoSession.PermissionDelegate.Callback)>`_ 80 call, you will also receive the ``GeckoSession`` the request was sent 81 from, an array containing the permissions that are being requested, and 82 a 83 `Callback`_ 84 to respond to the request. It is then up to the app to request those 85 permissions from the device, which can be done using 86 `requestPermissions <https://developer.android.com/reference/android/app/Activity#requestPermissions(java.lang.String%5B%5D,%2520int)>`_. 87 88 Possible ``permissions`` values are: 89 `ACCESS_COARSE_LOCATION <https://developer.android.com/reference/android/Manifest.permission.html#ACCESS_COARSE_LOCATION>`_, 90 `ACCESS_FINE_LOCATION <https://developer.android.com/reference/android/Manifest.permission.html#ACCESS_FINE_LOCATION>`_, 91 `CAMERA <https://developer.android.com/reference/android/Manifest.permission.html#CAMERA>`_ 92 or 93 `RECORD_AUDIO <https://developer.android.com/reference/android/Manifest.permission.html#RECORD_AUDIO>`_. 94 95 .. code:: java 96 97 private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate { 98 private Callback mCallback; 99 100 public void onRequestPermissionsResult(final String[] permissions, 101 final int[] grantResults) { 102 if (mCallback == null) { return; } 103 104 final Callback cb = mCallback; 105 mCallback = null; 106 for (final int result : grantResults) { 107 if (result != PackageManager.PERMISSION_GRANTED) { 108 // At least one permission was not granted. 109 cb.reject(); 110 return; 111 } 112 } 113 cb.grant(); 114 } 115 116 @Override 117 public void onAndroidPermissionsRequest(final GeckoSession session, 118 final String[] permissions, 119 final Callback callback) { 120 mCallback = callback; 121 requestPermissions(permissions, androidPermissionRequestCode); 122 } 123 } 124 125 public class GeckoViewActivity extends AppCompatActivity { 126 @Override 127 public void onRequestPermissionsResult(final int requestCode, 128 final String[] permissions, 129 final int[] grantResults) { 130 if (requestCode == REQUEST_PERMISSIONS || 131 requestCode == REQUEST_WRITE_EXTERNAL_STORAGE) { 132 final ExamplePermissionDelegate permission = (ExamplePermissionDelegate) 133 getCurrentSession().getPermissionDelegate(); 134 permission.onRequestPermissionsResult(permissions, grantResults); 135 } else { 136 super.onRequestPermissionsResult(requestCode, permissions, grantResults); 137 } 138 } 139 } 140 141 Content Permissions 142 ~~~~~~~~~~~~~~~~~~~ 143 144 Content permissions are requested whenever a site wants access to 145 content that is stored on the device. The content permissions that can 146 be requested through GeckoView are: 147 `Geolocation <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_GEOLOCATION>`_, 148 `Site Notifications <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_DESKTOP_NOTIFICATION>`_, 149 `Persistent Storage <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_PERSISTENT_STORAGE>`_, 150 `XR <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_XR>`_, 151 `Autoplay Inaudible <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_AUTOPLAY_INAUDIBLE>`_, 152 `Autoplay Audible <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_AUTOPLAY_AUDIBLE>`_, 153 and 154 `DRM Media access <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_MEDIA_KEY_SYSTEM_ACCESS>`_. 155 Additionally, `tracking protection exceptions <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_TRACKING>`_ 156 are treated as a type of content permission. 157 158 When you receive an 159 `onContentPermissionRequest <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#onContentPermissionRequest(org.mozilla.geckoview.GeckoSession,org.mozilla.geckoview.GeckoSession.PermissionDelegate.ContentPermission)>`_ 160 call, you will also receive the ``GeckoSession`` the request was sent 161 from, and all relevant information about the permission being requested 162 stored in a `ContentPermission <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.ContentPermission.html>`_. 163 It is then up to the app to present UI to the user asking for the 164 permissions, and to notify GeckoView of the response via the returned 165 ``GeckoResult``. 166 167 Once a permission has been set in this fashion, GeckoView will persist it 168 across sessions until it is cleared or modified. When a page is loaded, 169 the active permissions associated with it (both allowed and denied) will 170 be reported in `onLocationChange <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.NavigationDelegate.html#onLocationChange(org.mozilla.geckoview.GeckoSession,java.lang.String,java.util.List)>`_ 171 as a list of ``ContentPermission`` objects; additionally, one may check all stored 172 content permissions by calling `getAllPermissions <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/StorageController.html#getAllPermissions()>`_ 173 and the content permissions associated with a given URI by calling 174 `getPermissions <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/StorageController.html#getPermissions(java.lang.String,java.lang.String)>`_. 175 In order to modify an existing permission, you will need the associated 176 ``ContentPermission`` (which can be retrieved from any of the above methods); 177 then, call `setPermission <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/StorageController.html#setPermission(org.mozilla.geckoview.GeckoSession.PermissionDelegate.ContentPermission,int)>`_ 178 with the desired new value, or `VALUE_PROMPT <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.ContentPermission.html#VALUE_PROMPT>`_ 179 if you wish to unset the permission and let the site request it again in the future. 180 181 Media Permissions 182 ~~~~~~~~~~~~~~~~~ 183 184 Media permissions are requested whenever a site wants access to play or 185 record media from the device’s camera and microphone. 186 187 When you receive an 188 `onMediaPermissionRequest <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#onMediaPermissionRequest(org.mozilla.geckoview.GeckoSession,java.lang.String,org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource[],org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource[],org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaCallback)>`_ 189 call, you will also receive the ``GeckoSession`` the request was sent 190 from, the URI of the site that requested the permission, as a String, 191 the list of video devices available, if requesting video, the list of 192 audio devices available, if requesting audio, and a 193 `MediaCallback <https://searchfox.org/mozilla-central/source/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java#686>`_ 194 to respond to the request. 195 196 It is up to the app to present UI to the user asking for the 197 permissions, and to notify GeckoView of the response via the 198 ``MediaCallback``. 199 200 *Please note, media permissions will still be requested if the 201 associated device permissions have been denied if there are video or 202 audio sources in that category that can still be accessed when listed. 203 It is the responsibility of consumers to ensure that media permission 204 requests are not displayed in this case.* 205 206 .. code:: java 207 208 private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate { 209 @Override 210 public void onMediaPermissionRequest(final GeckoSession session, 211 final String uri, 212 final MediaSource[] video, 213 final MediaSource[] audio, 214 final MediaCallback callback) { 215 // Reject permission if Android permission has been previously denied. 216 if ((audio != null 217 && ContextCompat.checkSelfPermission(GeckoViewActivity.this, 218 Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) 219 || (video != null 220 && ContextCompat.checkSelfPermission(GeckoViewActivity.this, 221 Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) { 222 callback.reject(); 223 return; 224 } 225 226 final String host = Uri.parse(uri).getAuthority(); 227 final String title; 228 if (audio == null) { 229 title = getString(R.string.request_video, host); 230 } else if (video == null) { 231 title = getString(R.string.request_audio, host); 232 } else { 233 title = getString(R.string.request_media, host); 234 } 235 236 // Get the media device name from the `MediaDevice` 237 String[] videoNames = normalizeMediaName(video); 238 String[] audioNames = normalizeMediaName(audio); 239 240 final AlertDialog.Builder builder = new AlertDialog.Builder(activity); 241 242 // Create drop down boxes to allow users to select which device to grant permission to 243 final LinearLayout container = addStandardLayout(builder, title, null); 244 final Spinner videoSpinner; 245 if (video != null) { 246 videoSpinner = addMediaSpinner(builder.getContext(), container, video, videoNames); // create spinner and add to alert UI 247 } else { 248 videoSpinner = null; 249 } 250 251 final Spinner audioSpinner; 252 if (audio != null) { 253 audioSpinner = addMediaSpinner(builder.getContext(), container, audio, audioNames); // create spinner and add to alert UI 254 } else { 255 audioSpinner = null; 256 } 257 258 builder.setNegativeButton(android.R.string.cancel, null) 259 .setPositiveButton(android.R.string.ok, 260 new DialogInterface.OnClickListener() { 261 @Override 262 public void onClick(final DialogInterface dialog, final int which) { 263 // gather selected media devices and grant access 264 final MediaSource video = (videoSpinner != null) 265 ? (MediaSource) videoSpinner.getSelectedItem() : null; 266 final MediaSource audio = (audioSpinner != null) 267 ? (MediaSource) audioSpinner.getSelectedItem() : null; 268 callback.grant(video, audio); 269 } 270 }); 271 272 final AlertDialog dialog = builder.create(); 273 dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { 274 @Override 275 public void onDismiss(final DialogInterface dialog) { 276 callback.reject(); 277 } 278 }); 279 dialog.show(); 280 } 281 } 282 283 To see the ``PermissionsDelegate`` in action, you can find the full 284 example implementation in the `GeckoView example 285 app <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.MediaCallback.html>`_. 286 287 .. _Callback: https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.Callback.html