Dentro de um WebView, uma página que pede a câmera ou a localização precisa passar por duas portas separadas — a permissão do SO e a permissão do WebView — e no Android o padrão é negar a segunda se o app não implementar o callback. Pior ainda, o WebView do Android dá ao app contenedor o controle total do tratamento de permissões web, sem o consentimento por site de um navegador de verdade: uma vez concedida a permissão do SO, o app pode compartilhar a localização ou a câmera com qualquer página do WebView sem aviso. No iOS, o WKWebView acrescenta um segundo aviso além do do app, e navegadores integrados como TikTok e Instagram não têm suporte oficial. Um runtime próprio do host oferece um único intermediário de permissões que alinha o consentimento nativo e web, aplica concessões por origem, exige contexto seguro e mostra um só aviso consistente.
Duas portas, e a segunda diz não por padrão
No Android, uma página que chama getUserMedia ou navigator.geolocation esbarra em duas verificações. Primeiro a permissão do SO — CÂMERA, RECORD_AUDIO ou localização declarados no manifest e concedidos em runtime (API 23+). Depois a permissão do WebView: mesmo com a concessão do SO, a página chega ao app como WebChromeClient.onPermissionRequest (câmera/mic) ou onGeolocationPermissionsShowPrompt (localização), e a resposta padrão é não. Se você não implementar o callback, o sintoma é desconcertante — permissão concedida, getUserMedia chamado, nada acontece, sem diálogo, uma promessa rejeitada. A localização só é chamada para origens seguras em apps voltados ao Android 7.0+, então uma página HTTP é negada antes de qualquer toque.
Sem consentimento por site, diferente de um navegador
Um estudo de 2025 sobre a execução de permissões web no WebView do Android mostra a brecha estrutural: navegadores tradicionais avisam por site e deixam o usuário conceder ou negar cada um, mas o WebView do Android delega a decisão inteira ao app contenedor. Se o app implementa o callback, ele pode conceder ou negar para qualquer origem, e não há memória por site nem controle visível ao usuário. O risco mais fundo é que, uma vez concedida a permissão de localização do SO ao app, o app pode entregar essa localização a qualquer site carregado no WebView sem outro consentimento do usuário — o usuário não vê qual página acessa o quê. Os desenvolvedores precisam construir essa UX de consentimento eles mesmos, ou ela não existe.
iOS: um segundo aviso, e rebaixamentos silenciosos
No iOS, o acesso a câmera e microfone do WKWebView exige que o app implemente requestMediaCapturePermissionFor (iOS 15+) e os entitlements correspondentes; antes disso, o WebRTC simplesmente não estava disponível no WKWebView. Mesmo implementado, o usuário vê dois avisos — um do app, um do site — e a solução documentada é o app pular o segundo via um delegate, o que significa que a escolha por página do usuário é decidida pelo app, não pela página. Quando a página é carregada por um custom scheme handler, o aviso mostra o nome do app como origem, o que confunde os usuários. Navegadores integrados (Instagram, TikTok) trazem restrições adicionais e UI quebrada e não têm suporte explícito.
O que você perde sem um intermediário de permissões
- Uma única fonte de verdade. Os estados de permissão nativos e web vivem em lugares diferentes; divergem e se contradizem.
- Controle por origem. O WebView do Android não tem memória de concessão por site; o app concede tudo ou nega tudo.
- Visibilidade para o usuário. O usuário não sabe qual página acessa a câmera ou a localização, porque o consentimento vive no código do app.
- Contexto seguro obrigatório. Uma página HTTP perde a localização em silêncio no Android moderno, sem razão visível.
- Um aviso consistente. O iOS avisa duas vezes; os navegadores integrados quebram por completo.
A solução do runtime
Um runtime próprio do host oferece um único intermediário de permissões abaixo da página. Ele mantém um armazenamento de consentimento unificado para nativo e web, aplica concessões por origem como um navegador, exige contexto seguro antes de oferecer qualquer API sensível, e apresenta um só aviso consistente e legível que nomeia a página e o recurso. A concessão do SO do app e o pedido web da página são reconciliados em um só lugar, assim o consentimento é visível, revogável e nunca é compartilhado em silêncio com uma origem desconhecida.
| Dimensão | WebView | Runtime próprio do host |
|---|---|---|
| Modelo de consentimento | O app decide tudo, sem memória por site | Armazenamento unificado, concessões por origem |
| Porta 2 no Android | onPermissionRequest nega por padrão | Intermediário concede por origem, visível |
| Avisos no iOS | Dois avisos, o app pode pular o do site | Um só aviso consistente |
| Contexto seguro | Localização HTTP negada em silêncio | Obrigatório, explicado |
| Visibilidade para o usuário | Não se vê qual página acessa o quê | O aviso nomeia página e recurso |
| Navegadores integrados | Sem suporte, UI quebrada | Um runtime, suporte em todo lugar |
Um navegador conquista confiança fazendo a pergunta certa, para a parte certa, no momento certo. Um WebView de fábrica empurra essa decisão para um código de app opaco e depois diz não por padrão. Um runtime recupera o contrato de consentimento do navegador — por origem, visível e revogável — assim uma página que precisa da câmera recebe um aviso que o usuário realmente entende.
Fontes
- Android Developers — WebChromeClient.onPermissionRequest e PermissionRequest: o app deve implementar o callback ou todos os pedidos web são negados por padrão — a segunda porta e seu padrão silencioso.
- Android Developers — WebSettings.setGeolocationEnabled: a localização só é chamada para origens seguras em apps voltados ao Android 7.0+ — por que uma página HTTP perde a localização em silêncio.
- CISPA — Estudo dos riscos de privacidade na execução de permissões web do Android WebView (2025): sem consentimento por site, o app pode compartilhar concessões do SO com qualquer página — a brecha estrutural de privacidade.
- Apple Developer Documentation — WKUIDelegate.requestMediaCapturePermissionFor e WKPermissionDecision — o duplo aviso do iOS e a omissão via delegate.
- web.dev — Web on Android: WebView delega o comportamiento sensível ao app contenedor — por que o tratamento de permissões é do lado do app, não do web.