Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Handle GeckoView EME permission requests.
Browse files Browse the repository at this point in the history
This allows Fenix to play EME content without having to toggle manually
`media.eme.require-app-approval=false` in `about:config`.

Closes #7121
  • Loading branch information
emilio authored and mergify[bot] committed Dec 7, 2020
1 parent 9cf3e88 commit 8f9c61e
Show file tree
Hide file tree
Showing 19 changed files with 226 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_AUTOPLAY
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_AUTOPLAY_INAUDIBLE
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_GEOLOCATION
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_MEDIA_KEY_SYSTEM_ACCESS
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_PERSISTENT_STORAGE
import java.util.UUID

Expand Down Expand Up @@ -59,7 +60,8 @@ sealed class GeckoPermissionRequest constructor(
PERMISSION_GEOLOCATION to Permission.ContentGeoLocation(),
PERMISSION_AUTOPLAY_AUDIBLE to Permission.ContentAutoPlayAudible(),
PERMISSION_AUTOPLAY_INAUDIBLE to Permission.ContentAutoPlayInaudible(),
PERMISSION_PERSISTENT_STORAGE to Permission.ContentPersistentStorage()
PERMISSION_PERSISTENT_STORAGE to Permission.ContentPersistentStorage(),
PERMISSION_MEDIA_KEY_SYSTEM_ACCESS to Permission.ContentMediaKeySystemAccess()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_AUTOPLAY
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_AUTOPLAY_INAUDIBLE
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_GEOLOCATION
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_MEDIA_KEY_SYSTEM_ACCESS
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_PERSISTENT_STORAGE
import java.util.UUID

Expand Down Expand Up @@ -59,7 +60,8 @@ sealed class GeckoPermissionRequest constructor(
PERMISSION_GEOLOCATION to Permission.ContentGeoLocation(),
PERMISSION_AUTOPLAY_AUDIBLE to Permission.ContentAutoPlayAudible(),
PERMISSION_AUTOPLAY_INAUDIBLE to Permission.ContentAutoPlayInaudible(),
PERMISSION_PERSISTENT_STORAGE to Permission.ContentPersistentStorage()
PERMISSION_PERSISTENT_STORAGE to Permission.ContentPersistentStorage(),
PERMISSION_MEDIA_KEY_SYSTEM_ACCESS to Permission.ContentMediaKeySystemAccess()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_AUTOPLAY
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_AUTOPLAY_INAUDIBLE
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_GEOLOCATION
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_MEDIA_KEY_SYSTEM_ACCESS
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.PERMISSION_PERSISTENT_STORAGE
import java.util.UUID

Expand Down Expand Up @@ -59,7 +60,8 @@ sealed class GeckoPermissionRequest constructor(
PERMISSION_GEOLOCATION to Permission.ContentGeoLocation(),
PERMISSION_AUTOPLAY_AUDIBLE to Permission.ContentAutoPlayAudible(),
PERMISSION_AUTOPLAY_INAUDIBLE to Permission.ContentAutoPlayInaudible(),
PERMISSION_PERSISTENT_STORAGE to Permission.ContentPersistentStorage()
PERMISSION_PERSISTENT_STORAGE to Permission.ContentPersistentStorage(),
PERMISSION_MEDIA_KEY_SYSTEM_ACCESS to Permission.ContentMediaKeySystemAccess()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ sealed class Permission {
data class ContentAutoPlayAudible(override val id: String? = "", override val desc: String? = "") : Permission()
data class ContentAutoPlayInaudible(override val id: String? = "", override val desc: String? = "") : Permission()
data class ContentPersistentStorage(override val id: String? = "", override val desc: String? = "") : Permission()
data class ContentMediaKeySystemAccess(override val id: String? = "", override val desc: String? = "") :
Permission()

data class AppCamera(override val id: String? = "", override val desc: String? = "") : Permission()
data class AppAudio(override val id: String? = "", override val desc: String? = "") : Permission()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "f5379c8eb4f1519eb5994e508626ca10",
"entities": [
{
"tableName": "site_permissions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`origin` TEXT NOT NULL, `location` INTEGER NOT NULL, `notification` INTEGER NOT NULL, `microphone` INTEGER NOT NULL, `camera` INTEGER NOT NULL, `bluetooth` INTEGER NOT NULL, `local_storage` INTEGER NOT NULL, `autoplay_audible` INTEGER NOT NULL, `autoplay_inaudible` INTEGER NOT NULL, `media_key_system_access` INTEGER NOT NULL, `saved_at` INTEGER NOT NULL, PRIMARY KEY(`origin`))",
"fields": [
{
"fieldPath": "origin",
"columnName": "origin",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "location",
"columnName": "location",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "notification",
"columnName": "notification",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "microphone",
"columnName": "microphone",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "camera",
"columnName": "camera",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "bluetooth",
"columnName": "bluetooth",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "localStorage",
"columnName": "local_storage",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "autoplayAudible",
"columnName": "autoplay_audible",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "autoplayInaudible",
"columnName": "autoplay_inaudible",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "mediaKeySystemAccess",
"columnName": "media_key_system_access",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "savedAt",
"columnName": "saved_at",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"origin"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f5379c8eb4f1519eb5994e508626ca10')"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,29 @@ class OnDeviceSitePermissionsStorageTest {
assertEquals(Status.ALLOWED.id, cursor.getInt(cursor.getColumnIndexOrThrow("autoplay_inaudible")))
}
}

@Test
fun migrate3to4() {
helper.createDatabase(MIGRATION_TEST_DB, 3).apply {
query("SELECT * FROM site_permissions").use { cursor ->
assertEquals(10, cursor.columnCount)
}
execSQL(
"INSERT INTO " +
"site_permissions " +
"(origin, location, notification, microphone,camera,bluetooth,local_storage,autoplay_audible,autoplay_inaudible,saved_at) " +
"VALUES " +
"('mozilla.org',1,1,1,1,1,1,1,1,1)"
)
}

val dbVersion3 = helper.runMigrationsAndValidate(MIGRATION_TEST_DB, 4, true, Migrations.migration_3_4)

dbVersion3.query("SELECT * FROM site_permissions").use { cursor ->
assertEquals(11, cursor.columnCount)

cursor.moveToFirst()
assertEquals(Status.NO_DECISION.id, cursor.getInt(cursor.getColumnIndexOrThrow("media_key_system_access")))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ data class SitePermissions(
val localStorage: Status = NO_DECISION,
val autoplayAudible: Status = NO_DECISION,
val autoplayInaudible: Status = NO_DECISION,
val mediaKeySystemAccess: Status = NO_DECISION,
val savedAt: Long
) : Parcelable {
enum class Status(
Expand Down Expand Up @@ -53,6 +54,7 @@ data class SitePermissions(
Permission.LOCATION -> location
Permission.AUTOPLAY_AUDIBLE -> autoplayAudible
Permission.AUTOPLAY_INAUDIBLE -> autoplayInaudible
Permission.MEDIA_KEY_SYSTEM_ACCESS -> mediaKeySystemAccess
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import mozilla.components.concept.engine.permission.Permission.ContentNotificati
import mozilla.components.concept.engine.permission.Permission.ContentVideoCamera
import mozilla.components.concept.engine.permission.Permission.ContentVideoCapture
import mozilla.components.concept.engine.permission.Permission.ContentPersistentStorage
import mozilla.components.concept.engine.permission.Permission.ContentMediaKeySystemAccess
import mozilla.components.concept.engine.permission.Permission.AppLocationCoarse
import mozilla.components.concept.engine.permission.Permission.AppLocationFine
import mozilla.components.concept.engine.permission.Permission.AppAudio
Expand Down Expand Up @@ -438,6 +439,9 @@ class SitePermissionsFeature(
is ContentPersistentStorage -> {
permissionFromStore.localStorage.doNotAskAgain()
}
is ContentMediaKeySystemAccess -> {
permissionFromStore.mediaKeySystemAccess.doNotAskAgain()
}
else -> false
}
}
Expand Down Expand Up @@ -499,6 +503,9 @@ class SitePermissionsFeature(
is ContentPersistentStorage -> {
sitePermissions.copy(localStorage = status)
}
is ContentMediaKeySystemAccess -> {
sitePermissions.copy(mediaKeySystemAccess = status)
}
else ->
throw InvalidParameterException("$permission is not a valid permission.")
}
Expand Down Expand Up @@ -526,6 +533,7 @@ class SitePermissionsFeature(
}
}

@Suppress("LongMethod")
@VisibleForTesting
internal fun handlingSingleContentPermissions(
permissionRequest: PermissionRequest,
Expand Down Expand Up @@ -592,6 +600,17 @@ class SitePermissionsFeature(
shouldSelectRememberChoice = true
)
}
is ContentMediaKeySystemAccess -> {
createSinglePermissionPrompt(
context,
host,
permissionRequest,
R.string.mozac_feature_sitepermissions_media_key_system_access_title,
R.drawable.mozac_ic_link,
showDoNotAskAgainCheckBox = false,
shouldSelectRememberChoice = true
)
}
else ->
throw InvalidParameterException("$permission is not a valid permission.")
}
Expand Down Expand Up @@ -727,6 +746,9 @@ internal fun isPermissionGranted(
is ContentPersistentStorage -> {
permissionFromStorage.localStorage.isAllowed()
}
is ContentMediaKeySystemAccess -> {
permissionFromStorage.mediaKeySystemAccess.isAllowed()
}
else ->
throw InvalidParameterException("$permission is not a valid permission.")
}
Expand All @@ -739,7 +761,8 @@ private fun Permission.isSupported(): Boolean {
is ContentPersistentStorage,
is ContentAudioCapture, is ContentAudioMicrophone,
is ContentVideoCamera, is ContentVideoCapture,
is ContentAutoPlayAudible, is ContentAutoPlayInaudible -> true
is ContentAutoPlayAudible, is ContentAutoPlayInaudible,
is ContentMediaKeySystemAccess -> true
else -> false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ data class SitePermissionsRules internal constructor(
val microphone: Action,
val autoplayAudible: Action,
val autoplayInaudible: Action,
val persistentStorage: Action
val persistentStorage: Action,
val mediaKeySystemAccess: Action
) {

constructor(
Expand All @@ -29,15 +30,17 @@ data class SitePermissionsRules internal constructor(
microphone: Action,
autoplayAudible: AutoplayAction,
autoplayInaudible: AutoplayAction,
persistentStorage: Action
persistentStorage: Action,
mediaKeySystemAccess: Action
) : this(
camera = camera,
location = location,
notification = notification,
microphone = microphone,
autoplayAudible = autoplayAudible.toAction(),
autoplayInaudible = autoplayInaudible.toAction(),
persistentStorage = persistentStorage
persistentStorage = persistentStorage,
mediaKeySystemAccess = mediaKeySystemAccess
)

enum class Action {
Expand Down Expand Up @@ -93,6 +96,9 @@ data class SitePermissionsRules internal constructor(
is Permission.ContentAutoPlayInaudible -> {
autoplayInaudible
}
is Permission.ContentMediaKeySystemAccess -> {
mediaKeySystemAccess
}
else -> ASK_TO_ALLOW
}
}
Expand All @@ -118,6 +124,7 @@ data class SitePermissionsRules internal constructor(
autoplayAudible = autoplayAudible.toStatus(),
autoplayInaudible = autoplayInaudible.toStatus(),
localStorage = persistentStorage.toStatus(),
mediaKeySystemAccess = mediaKeySystemAccess.toStatus(),
savedAt = savedAt
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import mozilla.components.feature.sitepermissions.SitePermissionsStorage.Permiss
import mozilla.components.feature.sitepermissions.SitePermissionsStorage.Permission.NOTIFICATION
import mozilla.components.feature.sitepermissions.SitePermissionsStorage.Permission.AUTOPLAY_AUDIBLE
import mozilla.components.feature.sitepermissions.SitePermissionsStorage.Permission.AUTOPLAY_INAUDIBLE
import mozilla.components.feature.sitepermissions.SitePermissionsStorage.Permission.MEDIA_KEY_SYSTEM_ACCESS
import mozilla.components.feature.sitepermissions.db.SitePermissionsDatabase
import mozilla.components.feature.sitepermissions.db.toSitePermissionsEntity

Expand Down Expand Up @@ -116,6 +117,7 @@ class SitePermissionsStorage(
map.putIfAllowed(LOCATION, location, permission)
map.putIfAllowed(AUTOPLAY_AUDIBLE, autoplayAudible, permission)
map.putIfAllowed(AUTOPLAY_INAUDIBLE, autoplayInaudible, permission)
map.putIfAllowed(MEDIA_KEY_SYSTEM_ACCESS, mediaKeySystemAccess, permission)
}
}
return map
Expand Down Expand Up @@ -173,6 +175,6 @@ class SitePermissionsStorage(

enum class Permission {
MICROPHONE, BLUETOOTH, CAMERA, LOCAL_STORAGE, NOTIFICATION, LOCATION, AUTOPLAY_AUDIBLE,
AUTOPLAY_INAUDIBLE
AUTOPLAY_INAUDIBLE, MEDIA_KEY_SYSTEM_ACCESS
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import mozilla.components.feature.sitepermissions.SitePermissions
/**
* Internal database for saving site permissions.
*/
@Database(entities = [SitePermissionsEntity::class], version = 3)
@Database(entities = [SitePermissionsEntity::class], version = 4)
@TypeConverters(StatusConverter::class)
internal abstract class SitePermissionsDatabase : RoomDatabase() {
abstract fun sitePermissionsDao(): SitePermissionsDao
Expand All @@ -38,6 +38,8 @@ internal abstract class SitePermissionsDatabase : RoomDatabase() {
Migrations.migration_1_2
).addMigrations(
Migrations.migration_2_3
).addMigrations(
Migrations.migration_3_4
).build().also { instance = it }
}
}
Expand Down Expand Up @@ -98,4 +100,17 @@ internal object Migrations {
}
}
}

@Suppress("MagicNumber")
val migration_3_4 = object : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
val hasEmeColumn = database.query("SELECT * FROM site_permissions").columnCount == 11
if (!hasEmeColumn) {
database.execSQL(
"ALTER TABLE site_permissions ADD COLUMN media_key_system_access INTEGER NOT NULL DEFAULT 0")
// default is NO_DECISION
database.execSQL("UPDATE site_permissions SET media_key_system_access = 0")
}
}
}
}
Loading

0 comments on commit 8f9c61e

Please sign in to comment.