由于URI缺少权限,重新发送意图失败

4
在我的应用中,所有的活动都是需要登录保护的:
每个活动在其onCreate/onShow回调中检查用户是否已登录。如果已登录,则不会发生任何特别的事情。但如果用户没有登录,则这些回调将重定向用户到一个LoginActivity并结束当前活动:
Intent loginIntent = new Intent(this, LoginActivity.class);
loginIntent.putExtra(EXTRA_ORIGINAL_INTENT, getIntent());
finish();
startActivity(loginIntent);

如上面的行所示,原始的Intent用于启动当前活动已添加到loginIntent的额外信息中。因此,在成功登录后,LoginActivity可以理论上重新发送Intent。但是,这并不总是有效。

从启动器启动时可以使用:

Intent with ACTION_MAIN --> MainActivity --> Redirect to LoginActivity -->
Redirect back to MainActivity after successful login

在Chrome浏览器上使用分享按钮时无法正常工作:
Intent with ACTION_SEND --> SendActivity --> Redirect to LoginActivity -->
Redirect back to SendActivity after successful login

问题在于最后的重定向,它试图重新发送最初用于启动“SendActivity”的原始“Intent”:
// Resend original intent
startActivity((Intent) getIntent().getExtra(EXTRA_ORIGINAL_INTENT));

它失败了并出现了异常:

Caused by: java.lang.SecurityException: Uid 10075 does not have permission to uri 0 @ content://com.android.chrome.FileProvider/images/screenshot/6471932623902346234.jpg

现在我的问题是:
如何重新发送Intent,而不会引起权限异常?
如何传递所需的权限?


注意:
我真的希望以完成当前活动并重新发送Intent的方式来实现重定向行为。由于各种原因,简单地保留活动(MainActivity, SendActivity)在堆栈中并从LoginActivity返回它们不是选项。

2个回答

3
重新发送原始的IntentLoginActivity中是有效的,如果权限沿着Activity/Intent链传递。这可以通过以下方式实现:
  1. 重定向-Intents是原始Intent的副本。
  2. 在重新发送之前,使Intent明确。
例如,SomeActivity使用启动SomeActivityIntent的副本重定向到LoginActivity
Intent loginIntent = new Intent(getIntent()); // copy ...
loginIntent.setClass(this, LoginActivity.class); // ... explicit now
// maybe loginIntent.setAction(...) etc.
loginIntent.putExtra(EXTRA_ORIGINAL_INTENT, getIntent());
finish();
startActivity(loginIntent);

现在,在成功登录后,LoginActivity 可以重定向回 SomeActivity:
Intent origIntent = getIntent().getParcelableExtra(EXTRA_ORIGINAL_INTENT);
Intent redirectIntent = new Intent(origIntent); // copy ...
redirectIntent.setClass(this, SomeActivity.class); // ... explicit now
finish();
startActivity(redirectIntent);

0
通常,您缺少的是FLAG_GRANT_READ_URI_PERMISSION
当您从外部方(例如,在ACTION_SEND上的EXTRA_STREAM中)获取一个Uri时,您应该被授予至少读取由该Uri标识的内容的权限。这本身有点棘手设置
然而,这种权限授予仅针对接收Intent的组件(例如,响应ACTION_SEND的活动)。如果您尝试将工作委派给其他任何东西(另一个活动、服务等),那个组件将没有权限。
因此,您有几个选择:
其一是将身份验证/授权内容转换为可以在原地使用的片段或其他UI部件,而不是启动另一个活动。这样可以让用户全部停留在一个地方,并且该位置具有内容的读取访问权限。
另一个选项是在将用户重定向到LoginActivity之前消耗内容。

另一种方法是不要调用finish()来结束原始活动,而是使用FLAG_ACTIVITY_REORDER_TO_FRONT来将LoginActivity的现有实例带回前台。理论上,它仍然可以读取内容。你的问题来自于你销毁了原始实例并创建了一个新的实例,而新的实例没有读取权限。

另一种方法是尝试在LoginActivityIntent和稍后通过EXTRA_ORIGINAL_INTENT额外传递的Intent上添加FLAG_GRANT_READ_URI_PERMISSION。我猜这不会起作用。

以上方法的变体是不仅将原始Intent放入EXTRA_ORIGINAL_INTENT中,还要提取其Uri并将其放入LoginActivity Intent的数据方面:

Intent loginIntent = new Intent(this, LoginActivity.class);
loginIntent.putExtra(EXTRA_ORIGINAL_INTENT, getIntent());
loginIntent.setData(getIntent().getData());
loginIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

LoginActivity 可能仍需要在来自 EXTRA_ORIGINAL_INTENTIntent 上调用 addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

基本上,在后一种情况下,您希望原始活动授予 LoginActivity 读取访问权限,仅以便 LoginActivity 可以将读取访问权限授予原始活动的新实例。

最终,您使用的任何原始活动实例都需要具有读取内容的访问权限,由于您最初被授予了访问权限,因此由您来确保该实例具有该访问权限。


谢谢你的想法。我已经尝试了使用grant-flags和显式地设置数据/附加信息,但没有成功。然而,我认为我已经弄清楚了如何重新发送intent。现在,在重定向时,我通过设置正确的目标activity class(例如SendActivity)将intent转换为显式intent。似乎我不允许重新发送这个隐式intent,但是显式intent按预期工作。这正是我想做的事情;-) - Biggie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接