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