欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

系統(tǒng)應(yīng)用根據(jù)Uri授予權(quán)限的正確姿勢(shì)

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

答案是可以的,這是為什么呢?我們都沒(méi)有申請(qǐng)權(quán)限,或者說(shuō)是誰(shuí)給了我們這個(gè)權(quán)限?帶著這個(gè)疑問(wèn)我們先來(lái)了解下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)的解釋?zhuān)蟾乓馑际?,你可以進(jìn)一步對(duì)其他應(yīng)用如何訪問(wèn)你應(yīng)用的Contete Provider或者數(shù)據(jù)URI進(jìn)行精細(xì)控制,可以通過(guò)讀寫(xiě)權(quán)限來(lái)保護(hù)自己,根據(jù) URI 授予權(quán)限。

在啟動(dòng) activity 或?qū)⒔Y(jié)果返回給 activity 時(shí),請(qǐng)?jiān)O(shè)置 Intent.FLAG_GRANT_READ_URI_PERMISSION intent 標(biāo)志、Intent.FLAG_GRANT_WRITE_URI_PERMISSIONintent 標(biāo)志或者同時(shí)設(shè)置兩者。

這樣便可針對(duì) intent 中包含的數(shù)據(jù) URI 分別向另一個(gè)應(yīng)用授予讀取權(quán)限、寫(xiě)入權(quán)限和讀寫(xiě)權(quán)限。

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

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

背景

最近在項(xiàng)目中上線一款自研的圖庫(kù)應(yīng)用(systemUid),支持系統(tǒng)默認(rèn)選圖action跳轉(zhuǎn),給調(diào)用者返回已選的Uri資源地址,因?yàn)榘踩弦?guī)整改的原因,一些第三方應(yīng)用去掉了讀寫(xiě)權(quán)限的申請(qǐng),問(wèn)題就被暴露出來(lái)了,第三方應(yīng)用無(wú)法正常通過(guò)圖庫(kù)獲取到圖片資源。

分析

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

我們可以通過(guò)context.grantUriPermission()作為切入點(diǎn),來(lái)分析下系統(tǒng)是如何授予Uri權(quán)限

最終調(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;
        }
    }
    ....
}
復(fù)制代碼

問(wèn)題就是出現(xiàn)在這里,如果你的應(yīng)用是root用戶,或者是具有系統(tǒng)級(jí)權(quán)限(systemUid),并且提供的Uri的authority不是指定的,就會(huì)拒絕授權(quán) (return -1),也就是說(shuō)這個(gè)Uri的權(quán)限并沒(méi)有傳遞成功。

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

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

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

注釋里面有說(shuō),出于安全原因,系統(tǒng)應(yīng)用不能使用startActivityAsCaller()來(lái)直接授予Uri權(quán)限,它必須顯式地讓?xiě)?yīng)用自己授予權(quán)限。只有以下幾種情況才會(huì)豁免授權(quán)

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

調(diào)用者端

分析完圖庫(kù)端,我們?cè)賮?lái)看下調(diào)用者端的異常堆棧打印

客戶端遠(yuǎn)程調(diào)用服務(wù)端打開(kāi)指定文件,然后服務(wù)端把文件描述符跨進(jìn)程傳遞到客戶端(后面Binder驅(qū)動(dòng)跨進(jìn)程傳遞文件描述符就不展開(kāi)分析)

通過(guò)分析時(shí)序圖,可以定位到報(bào)錯(cuò)的堆棧信息是發(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);
      }

在這個(gè)方法里面,它會(huì)根據(jù)參數(shù)forWrite判斷當(dāng)前調(diào)用者是否擁有讀寫(xiě)權(quán)限,如果沒(méi)有則會(huì)拋出異常提示,和上面報(bào)錯(cuò)的異常堆棧符合。

解決辦法

以下兩種方法都可以

  • 去除應(yīng)用systemUid配置
  • 修改framework源碼

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

擴(kuò)展

系統(tǒng)原生圖庫(kù)既不是系統(tǒng)應(yīng)用,也沒(méi)有動(dòng)態(tài)申請(qǐng)存儲(chǔ)權(quán)限,那它是怎么獲取系統(tǒng)的存儲(chǔ)權(quán)限的?

如果有了解過(guò)系統(tǒng)權(quán)限的授予流程,可以知道Android系統(tǒng)在開(kāi)機(jī)后會(huì)對(duì)一些特殊的應(yīng)用進(jìn)行自動(dòng)授權(quán),而運(yùn)行時(shí)權(quán)限的默認(rèn)授予工作由DefaultPermissionGrantPolicy類(lèi)的grantDefaultPermissions方法完成。

在這個(gè)方法里面可以看到它對(duì)圖庫(kù)進(jìn)行默認(rèn)授予存儲(chǔ)權(quán)限的代碼,具體是通過(guò)查找清單文件配置的category是Intent.CATEGORY_APP_GALLERY的應(yīng)用。

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

相關(guān)文章

最新評(píng)論