亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

系統(tǒng)應用根據(jù)Uri授予權限方法詳解

 更新時間:2022年09月03日 09:08:41   作者:SugarTurboS  
這篇文章主要為大家介紹了系統(tǒng)應用根據(jù)Uri授予權限方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

系統(tǒng)應用根據(jù)Uri授予權限的正確姿勢

在我們印象中,Android6.0以后訪問外部的媒體資源文件都是需要申請READ_EXTERNAL_STORAGE才可以正常訪問,思考一個場景,假如我們不申請該權限,使用系統(tǒng)的Intent.ACTION_PICK意圖跳轉(zhuǎn)系統(tǒng)圖庫選取圖片是否可以正常顯示該圖片?

答案是可以的,這是為什么呢?我們都沒有申請權限,或者說是誰給了我們這個權限?帶著這個疑問我們先來了解下UriPermission

UriPermission

Allow access on a per-URI basis

You can also grant permissions on a per-URI basis. When starting an activity or returning a result to an activity, set the Intent.FLAG_GRANT_READ_URI_PERMISSION, intent flag, the Intent.FLAG_GRANT_WRITE_URI_PERMISSION, intent flag, or both flags. This gives another app read, write, and read/write permissions, respectively, for the data URI that's included in the intent. The other app gains these permissions for the specific URI regardless of whether it has permission to access data in the content provider more generally.

上面是Android官網(wǎng)的解釋,大概意思是,你可以進一步對其他應用如何訪問你應用的Contete Provider或者數(shù)據(jù)URI進行精細控制,可以通過讀寫權限來保護自己,根據(jù) URI 授予權限。

在啟動 activity 或?qū)⒔Y(jié)果返回給 activity 時,請設置 Intent.FLAG_GRANT_READ_URI_PERMISSION intent 標志、Intent.FLAG_GRANT_WRITE_URI_PERMISSIONintent 標志或者同時設置兩者。

這樣便可針對 intent 中包含的數(shù)據(jù) URI 分別向另一個應用授予讀取權限、寫入權限和讀寫權限。

理解完UriPermission,上面的問題就可以解釋了,雖然我們沒有申請讀取的權限,但是系統(tǒng)圖庫在選圖后將結(jié)果返回activity時設置了Intent.FLAG_GRANT_READ_URI_PERMISSION,這樣我們就具有了讀取該Uri指定圖片的權限。

理想很豐滿,現(xiàn)實很骨感。

背景

最近在項目中上線一款自研的圖庫應用(systemUid),支持系統(tǒng)默認選圖action跳轉(zhuǎn),給調(diào)用者返回已選的Uri資源地址,因為安全合規(guī)整改的原因,一些第三方應用去掉了讀寫權限的申請,問題就被暴露出來了,第三方應用無法正常通過圖庫獲取到圖片資源。

分析

分析堆棧信息可以定位到,是調(diào)用者沒有訪問Uri的權限導致的異常,而我們自研圖庫在選圖回傳的時候是有設置FLAG_GRANT_READ_URI_PERMISSION,把URI的臨時訪問權限傳遞給調(diào)用者,且報錯的堆棧打印是在第三方應用,所以可以初步判斷問題應該是出自系統(tǒng)的權限授予過程。

我們可以通過context.grantUriPermission()作為切入點,來分析下系統(tǒng)是如何授予Uri權限

最終調(diào)用的是UriGrantsManagerService$checkGrantUriPermission()

checkGrantUriPermission

int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
        final int modeFlags, int lastTargetUid) {
   ....
    // Bail early if system is trying to hand out permissions directly; it
    // must always grant permissions on behalf of someone explicit.
    final int callingAppId = UserHandle.getAppId(callingUid);
    if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
        if ("com.android.settings.files".equals(grantUri.uri.getAuthority())
                || "com.android.settings.module_licenses".equals(grantUri.uri.getAuthority())) {
            // Exempted authority for
            // 1. cropping user photos and sharing a generated license html
            //    file in Settings app
            // 2. sharing a generated license html file in TvSettings app
            // 3. Sharing module license files from Settings app
        } else {
            Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
                    + " grant to " + grantUri + "; use startActivityAsCaller() instead");
            return -1;
        }
    }
    ....
}
復制代碼

問題就是出現(xiàn)在這里,如果你的應用是root用戶,或者是具有系統(tǒng)級權限(systemUid),并且提供的Uri的authority不是指定的,就會拒絕授權 (return -1),也就是說這個Uri的權限并沒有傳遞成功。

這一點在上面的日志也有體現(xiàn)。

/system_process W UriGrantsManagerService: For security reasons, the system cannot issue a Uri permission grant to

系統(tǒng)為什么要這么做?

注釋里面有說,出于安全原因,系統(tǒng)應用不能使用startActivityAsCaller()來直接授予Uri權限,它必須顯式地讓應用自己授予權限。只有以下幾種情況才會豁免授權

  • 裁剪用戶照片并共享生成的許可HTML文件的設置應用程序
  • 在TvSettings app中共享生成的許可html文件
  • 從設置應用程序共享模塊license文件

調(diào)用者端

分析完圖庫端,我們再來看下調(diào)用者端的異常堆棧打印

客戶端遠程調(diào)用服務端打開指定文件,然后服務端把文件描述符跨進程傳遞到客戶端(后面Binder驅(qū)動跨進程傳遞文件描述符就不展開分析)

通過分析時序圖,可以定位到報錯的堆棧信息是發(fā)生在enforceCallingPermissionInternal()

enforceCallingPermissionInternal()

 private void enforceCallingPermissionInternal(Uri uri, boolean forWrite) {
          ... // 省略部分代碼
          // First, check to see if caller has direct write access
          if (forWrite) {
              final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, uri, table, null);
              try (Cursor c = qb.query(db, new String[0], null, null, null, null, null)) {
                  if (c.moveToFirst()) {
                      // Direct write access granted, yay!
                      return;
                  }
              }
          }
          ... // 省略部分代碼
          // Second, check to see if caller has direct read access
          final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_QUERY, uri, table, null);
          try (Cursor c = qb.query(db, new String[0], null, null, null, null, null)) {
              if (c.moveToFirst()) {
                  if (!forWrite) {
                      // Direct read access granted, yay!
                      return;
                  } else if (allowUserGrant) {
                      // Caller has read access, but they wanted to write, and
                      // they'll need to get the user to grant that access
                      final Context context = getContext();
                      final PendingIntent intent = PendingIntent.getActivity(context, 42,
                              new Intent(null, uri, context, PermissionActivity.class),
                              FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
 
                      final Icon icon = getCollectionIcon(uri);
                      final RemoteAction action = new RemoteAction(icon,
                              context.getText(R.string.permission_required_action),
                              context.getText(R.string.permission_required_action),
                              intent);
 
                      throw new RecoverableSecurityException(new SecurityException(
                              getCallingPackageOrSelf() + " has no access to " + uri),
                              context.getText(R.string.permission_required), action);
                  }
              }
          }
 
          throw new SecurityException(getCallingPackageOrSelf() + " has no access to " + uri);
      }

在這個方法里面,它會根據(jù)參數(shù)forWrite判斷當前調(diào)用者是否擁有讀寫權限,如果沒有則會拋出異常提示,和上面報錯的異常堆棧符合。

解決辦法

以下兩種方法都可以

  • 去除應用systemUid配置
  • 修改framework源碼

考慮到修改系統(tǒng)源碼的影響面比較大,所以采用去除systemUid的方式,再次驗證跳轉(zhuǎn)選圖后可以正常加載。查看系統(tǒng)原生圖庫的清單文件配置,也是沒有設置systemUid,原生圖庫也是采用了這種方式。

擴展

系統(tǒng)原生圖庫既不是系統(tǒng)應用,也沒有動態(tài)申請存儲權限,那它是怎么獲取系統(tǒng)的存儲權限的?

如果有了解過系統(tǒng)權限的授予流程,可以知道Android系統(tǒng)在開機后會對一些特殊的應用進行自動授權,而運行時權限的默認授予工作由DefaultPermissionGrantPolicy類的grantDefaultPermissions方法完成。

在這個方法里面可以看到它對圖庫進行默認授予存儲權限的代碼,具體是通過查找清單文件配置的category是Intent.CATEGORY_APP_GALLERY的應用。

以上就是系統(tǒng)應用根據(jù)Uri授予權限方法詳解的詳細內(nèi)容,更多關于系統(tǒng)應用Uri授權的資料請關注腳本之家其它相關文章!

相關文章

最新評論