Android系統默認賦予瀏覽器權限以及Android惡意覆蓋導致谷歌瀏覽器授權失敗的解決辦法
一、Android系統默認賦予瀏覽器權限
只要是設計到默認賦權,就在framework下找這個類:base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
安卓系統開機的時候會調用這個類里面的下面這個方法:
public void grantDefaultPermissions(int userId) {DelayingPackageManagerCache pm = new DelayingPackageManagerCache();grantPermissionsToSysComponentsAndPrivApps(pm, userId);grantDefaultSystemHandlerPermissions(pm, userId);grantSignatureAppsNotificationPermissions(pm, userId);grantDefaultPermissionExceptions(pm, userId);// Apply delayed statepm.apply();}
grantDefaultSystemHandlerPermissions方法里面會默認賦予瀏覽器權限
private void grantDefaultSystemHandlerPermissions(PackageManagerWrapper pm, int userId) {......// BrowserString browserPackage = ArrayUtils.firstOrNull(getKnownPackages(KnownPackages.PACKAGE_BROWSER, userId));if (browserPackage == null) {browserPackage = getDefaultSystemHandlerActivityPackageForCategory(pm,Intent.CATEGORY_APP_BROWSER, userId);if (!pm.isSystemPackage(browserPackage)) {browserPackage = null;}}grantPermissionsToPackage(pm, browserPackage, userId, false /* ignoreSystemPackage */,true /*whitelistRestrictedPermissions*/, FOREGROUND_LOCATION_PERMISSIONS);
}
代碼中通過"Intent.CATEGORY_APP_BROWSER"這個標識去判斷是否是瀏覽器應用,如果是瀏覽器應用將會通過grantPermissionsToPackage方法去賦予瀏覽器權限
@SafeVarargsprivate final void grantPermissionsToPackage(PackageManagerWrapper pm, String packageName,int userId, boolean ignoreSystemPackage, boolean whitelistRestrictedPermissions,Set<String>... permissionGroups) {grantPermissionsToPackage(pm, pm.getPackageInfo(packageName),userId, false /* systemFixed */, ignoreSystemPackage,whitelistRestrictedPermissions, permissionGroups);}
通過代碼可以看到permissionGroups是一個可變參數,如果想給瀏覽器添加更多的默認參數只要在后面添加相應的權限即可,如下:
grantPermissionsToPackage(pm, browserPackage, userId, false /* ignoreSystemPackage */,
true /*whitelistRestrictedPermissions*/, FOREGROUND_LOCATION_PERMISSIONS,NOTIFICATION_PERMISSIONS,STORAGE_PERMISSIONS,CAMERA_PERMISSIONS,MICROPHONE_PERMISSIONS);
二、Android惡意覆蓋導致谷歌瀏覽器授權失敗的解決辦法
在通過谷歌瀏覽器使用zoom會議的過程中我們發現,當授予zoom會議攝像頭或者麥克風權限的時候,點擊確認授予權限時會彈出以下彈窗導致授權失敗,從而導致zoom會議無法打開攝像頭和麥克風。
首先我們借助AI工具看一下谷歌瀏覽器的源碼,“此網站無法請求授權,請關閉來著其他應用的所有氣泡和疊加層,然后重試”這個彈窗是什么條件下觸發的,得到以下信息
谷歌瀏覽器(Chromium)中關于 “此網站無法請求授權” 錯誤提示的源碼主要涉及權限請求攔截、覆蓋層檢測和用戶界面通知三個核心模塊。以下從源碼層面解析其實現邏輯:
一、錯誤提示的觸發條件
該錯誤通常在以下情況觸發(基于content/browser/permission_manager
模塊):
- 存在屏幕覆蓋層(如懸浮窗、輸入法面板等)
- 權限請求被系統層攔截
- 渲染進程與瀏覽器進程通信中斷
- 安全策略阻止跨域授權請求
二、核心源碼分析
1. 覆蓋層檢測機制(Overlay Detector)
源碼路徑:content/browser/android/overlay_detector_android.cc
// content/browser/android/overlay_detector_android.cc
bool OverlayDetector::IsOverlayPresent() {// 獲取系統窗口管理器服務WindowManager* wm = WindowManager::GetInstance();// 檢查是否存在非系統級覆蓋窗口std::vector<WindowInfo> windows = wm->GetAllWindows();for (const auto& window : windows) {if (!window.is_system_window && window.overlaps_application_area) {return true; // 存在惡意覆蓋層}}return false;
}
2. 權限請求攔截邏輯
源碼路徑:content/browser/permission_manager/permission_manager_impl.cc
// content/browser/permission_manager/permission_manager_impl.cc
void PermissionManagerImpl::RequestPermission(PermissionType permission,RenderFrameHost* render_frame_host,const GURL& requesting_origin,bool user_gesture,base::OnceCallback<void(PermissionStatus)> callback) {// 檢查是否存在覆蓋層if (overlay_detector_->IsOverlayPresent()) {// 觸發覆蓋層錯誤提示NotifyOverlayError(render_frame_host, requesting_origin);std::move(callback).Run(PERMISSION_STATUS_DENIED);return;}// 其他權限校驗邏輯...
}void PermissionManagerImpl::NotifyOverlayError(RenderFrameHost* render_frame_host,const GURL& origin) {// 創建錯誤消息std::string error_message = "此網站無法請求授權,請關閉來自其他應用的所有氣泡和疊加層,然后重試";// 通過WebUI發送錯誤通知到渲染進程WebUIMessageHandler::Send(render_frame_host,"chrome://overlay-error/",base::Value(error_message));
}
3. 用戶界面通知實現
源碼路徑:chrome/browser/ui/android/overlay_error_dialog.cc
// chrome/browser/ui/android/overlay_error_dialog.java
public class OverlayErrorDialog {private final Activity activity;private final String origin;private AlertDialog dialog;public OverlayErrorDialog(Activity activity, String origin) {this.activity = activity;this.origin = origin;}public void show() {// 構建錯誤提示對話框AlertDialog.Builder builder = new AlertDialog.Builder(activity);builder.setTitle("授權失敗").setMessage("此網站無法請求授權,請關閉來自其他應用的所有氣泡和疊加層,然后重試").setPositiveButton("確定", (dialog, which) -> dialog.dismiss()).setIcon(R.drawable.ic_warning);dialog = builder.create();dialog.show();}public void dismiss() {if (dialog != null && dialog.isShowing()) {dialog.dismiss();}}
}
4. 系統權限交互邏輯
源碼路徑:content/browser/android/permission_request_controller_android.cc
// content/browser/android/permission_request_controller_android.cc
void PermissionRequestController::OnSystemPermissionsResult(int request_code,const std::vector<std::string>& permissions,const std::vector<int>& grant_results) {// 處理系統權限請求結果if (grant_results.empty() || grant_results[0] != PackageManager.PERMISSION_GRANTED) {// 權限被拒絕,檢查是否因覆蓋層導致if (overlay_detector_->IsOverlayPresent()) {ShowOverlayErrorDialog();} else {ShowPermissionDeniedDialog();}}
}
三、錯誤提示的顯示流程
- 檢測階段:當網站請求敏感權限(如攝像頭、位置)時,瀏覽器通過
OverlayDetector
檢查屏幕上是否存在可疑覆蓋層。 - 攔截階段:若檢測到覆蓋層,
PermissionManager
會攔截權限請求,并調用NotifyOverlayError
生成錯誤消息。 - 顯示階段:
OverlayErrorDialog
將錯誤消息包裝為系統對話框,并通過 Android UI 框架顯示給用戶。
四、相關配置與擴展點
- 覆蓋層檢測閾值:可通過
chrome://flags
中的overlay-detection-sensitivity
標志調整檢測靈敏度。 - 錯誤消息本地化:字符串資源定義在
chrome/android/java/res/values/strings.xml
中。 - 白名單機制:在
content/public/common/overlay_whitelist.h
中可配置允許的覆蓋層應用。
五、解決思路與源碼關聯
當遇到 “此網站無法請求授權” 錯誤時,可從以下源碼角度排查:
- 覆蓋層檢測失敗:檢查
OverlayDetector::IsOverlayPresent()
的返回邏輯。 - 權限請求流程中斷:調試
PermissionManagerImpl::RequestPermission()
的執行路徑。 - UI 顯示異常:檢查
OverlayErrorDialog
的生命周期管理。
再通過AI工具檢查一下OverlayDetector::IsOverlayPresent()方法的返回邏輯,得到以下信息
在 Chromium 源碼中,OverlayDetector::IsOverlayPresent()
方法用于檢測屏幕上是否存在可能干擾瀏覽器權限請求的覆蓋層(如懸浮窗、惡意疊加層等)。其返回邏輯涉及系統窗口管理、應用權限檢查和窗口屬性分析,以下是核心實現解析:
一、方法核心邏輯
源碼路徑:content/browser/android/overlay_detector_android.cc
bool OverlayDetector::IsOverlayPresent() {// 1. 獲取系統窗口管理器服務WindowManager* wm = WindowManager::GetInstance();if (!wm)return false;// 2. 獲取所有可見窗口列表std::vector<WindowInfo> windows = wm->GetVisibleWindows();// 3. 檢查每個窗口是否為可疑覆蓋層for (const auto& window : windows) {// 跳過系統信任的窗口類型if (IsSystemTrustedWindow(window))continue;// 檢查窗口是否與瀏覽器內容區域重疊if (window.overlaps_browser_content) {// 檢查窗口是否具有高風險屬性if (IsHighRiskOverlay(window)) {LOG(WARNING) << "檢測到高風險覆蓋層: " << window.title;return true;}// 累積非系統窗口計數non_system_window_count_++;}}// 4. 判斷非系統窗口數量是否超過閾值if (non_system_window_count_ > kMaxAllowedNonSystemWindows) {LOG(WARNING) << "非系統覆蓋層數量過多: " << non_system_window_count_;return true;}return false;
}
二、關鍵輔助方法解析
1. 系統信任窗口判斷
bool OverlayDetector::IsSystemTrustedWindow(const WindowInfo& window) {// 系統狀態欄、導航欄等屬于信任窗口if (window.type == WINDOW_TYPE_STATUS_BAR ||window.type == WINDOW_TYPE_NAVIGATION_BAR) {return true;}// 輸入法窗口屬于信任窗口(但需特殊處理)if (window.type == WINDOW_TYPE_INPUT_METHOD) {return is_keyboard_trusted_;}// 檢查窗口是否由系統簽名應用創建if (window.is_system_signed) {// 部分系統應用仍需進一步驗證if (IsSystemAppWithOverlayPermission(window.package_name)) {return true;}}return false;
}
2. 高風險覆蓋層判斷
bool OverlayDetector::IsHighRiskOverlay(const WindowInfo& window) {// 檢查窗口是否為"TYPE_APPLICATION_OVERLAY"類型(Android 8.0+的懸浮窗類型)if (window.type == WINDOW_TYPE_APPLICATION_OVERLAY) {// 檢查窗口是否請求了危險權限if (HasDangerousPermissions(window.package_name)) {return true;}// 檢查窗口是否來自已知風險應用if (IsKnownRiskyApp(window.package_name)) {return true;}}// 檢查窗口是否具有不透明全屏特性if (window.is_opaque && window.covers_entire_screen) {return true;}return false;
}
3. 窗口與瀏覽器內容區域重疊判斷
bool OverlayDetector::DoesWindowOverlapBrowserContent(const WindowInfo& window) {// 獲取瀏覽器內容區域坐標gfx::Rect content_area = GetBrowserContentArea();// 檢查窗口邊界是否與內容區域相交return window.bounds.Intersects(content_area);
}
從以上的源碼我們可以看到,如果是系統信任的窗口則直接跳過返回false,如果是窗口與瀏覽器窗口疊加并且是被判斷為窗口具有高風險屬性則返回true,彈窗就會彈出。這時候可以判斷是系統的某個應用,覆蓋在了瀏覽器的上層或者和瀏覽器在同一層級,并且具有高風險屬性。通過代碼排查,以下應用會一直顯示在上層。打開谷歌瀏覽器時截屏并未打開,那只可能是側邊欄的問題了,這時候我們去設置菜單里面設置一下把側邊欄給隱藏掉,這時候再去打開zoom會議授權,發現授權成功了,彈窗沒有彈出來,我們可以確定該問題是側邊欄導致的,查看側邊欄源碼發現側邊欄使用TYPE_APPLICATION_OVERLAY
類型并且請求了危險權限,這下就解釋得通了。
接下來要怎么處理該問題,最好的辦法就是看一下能不能把側邊欄設置為系統信任的窗口,再次使用AI我們得到了以下解決辦法。
-
申請系統簽名
- 通過 OEM 合作或設備定制,為應用獲取系統簽名,從而使用
PRIVATE_FLAG_TRUSTED_OVERLAY
標志:// 系統應用才能生效的代碼 if (isSystemApp()) {params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; }
- 通過 OEM 合作或設備定制,為應用獲取系統簽名,從而使用
-
加入設備白名單
-
與設備廠商合作,將應用添加到瀏覽器的信任白名單中:
// 設備特定配置(非公開API) chrome://flags/#overlay-allowed-apps=com.your.app
-
顯然第一種方法是比較行得通的,側邊欄已經是系統應用,我們只需要添加PRIVATE_FLAG_TRUSTED_OVERLAY標志位即可,修改如下,即可解決惡意覆蓋導致授權失敗問題,該問題最后修改的地方比較簡單只有一行,但是問題的處理和排查過程會相對麻煩一些,這也是大部分系統級問題的通病。其他應用(如懸浮球等應用的解決思路也是一樣的),只不過該屬性在安卓frameworks中是隱藏的,在懸浮球等應用中需要通過反射調用,這方面我們就不去講解了。