Intent 权限和 JobIntentService
Intent permissions and JobIntentService
我有一个向应用程序 B 发送意图的应用程序 A。应用程序 A 有一个文件提供程序并向 B 共享一个文件。它向意图添加授权并在 B 上启动服务。B 有IntentService 并处理意图。现在我想根据新的 Android O 策略将 B 更改为使用 JobIntentService。但是我不能从 A 调用 startService,所以我修改了我的代码以发送一个显式广播。我在剪辑数据中添加了 uri。但是,当我收到应用程序 B 的意图时,我遇到了安全异常。
应用程序 A:
Uri contentUri = FileProvider.getUriForFile(this, "com.myotherapp.fileprovider",
newFile);
Intent i = new Intent(INTENT_UPDATE);
ClipData clipData = ClipData.newRawUri(at.name(), contentUri);
i.setClipData(clipData);
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
i.setPackage("com.myapp");
sendBroadcast(i);
应用程序 B:
@Override
public void onReceive(Context arg0, Intent arg1) {
if (MyService.INTENT_UPDATE.equals(arg1.getAction())) {
MyService.enqueueWork(arg0, arg1);
}
}
堆栈:
Process: com.myapp, PID: 7690
java.lang.RuntimeException: Unable to start receiver com.myapp.MyReceiver: java.lang.SecurityException: UID 10083 does not have permission to content://com.myotherapp.fileprovider/files/Q3hnMrF8BsOJeznpVLizkTB4H6LkI90T.csv [user 0]
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3259)
at android.app.ActivityThread.-wrap17(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6540)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.SecurityException: UID 10083 does not have permission to content://com.myotherapp.fileprovider/files/Q3hnMrF8BsOJeznpVLizkTB4H6LkI90T.csv [user 0]
at android.os.Parcel.readException(Parcel.java:1948)
at android.os.Parcel.readException(Parcel.java:1894)
at android.app.job.IJobScheduler$Stub$Proxy.enqueue(IJobScheduler.java:211)
at android.app.JobSchedulerImpl.enqueue(JobSchedulerImpl.java:53)
at android.support.v4.app.JobIntentService$JobWorkEnqueuer.enqueueWork(JobIntentService.java:314)
at android.support.v4.app.JobIntentService.enqueueWork(JobIntentService.java:472)
at com.myapp.MyService.enqueueWork(MyService.java:41)
at com.myapp.myrec.onReceive(UpdateReceiver.java:21)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3252)
at android.app.ActivityThread.-wrap17(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6540)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
编辑: 我尝试使用在 JobInfo 上使用 setClipData 的 JobService。反正一点用都没有。
在 FileProvider
的文档中,他们说
A content URI allows you to grant read and write access using temporary access permissions. When you create an Intent containing a content URI, in order to send the content URI to a client app, you can also call Intent.setFlags() to add permissions. These permissions are available to the client app for as long as the stack for a receiving Activity is active. For an Intent going to a Service, the permissions are available as long as the Service is running.
我的猜测是,当您退出广播接收器的 onReceive
方法时,权限就消失了。
解法:
结果发现问题是不支持广播接收器,您只能定位活动和服务,如文档中所述。
好的,我找到问题了。广播接收器没有收到权限,您不能调用 startService
因此在这种情况下您只有一种使用 Job 服务的棘手方法。我创建了一个无显示activity来转发意图:
public class ProxyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyService.enqueueWork(this, getIntent());
finish();
}
}
但是我不完全确定这个解决方案,因为 activity 每次只能发送一个意图。如果你有多个意图是一个问题。您可以将 launchMode 与单个实例一起使用并使用 onHandleIntent(),这是一个好主意,但您不能再使用主题 no display 并且您不知道何时可以调用 finish()。
编辑: 正如 Ian Lake (Google) 所解释的那样“...如您所见,URI 权限确实可以通过传递链接到其他组件通过剪辑数据。”在Api 16+上使用setData()或setDataAndType()时系统会自动创建和剪辑数据,所以这解释了为什么它有效,但是单一意图的问题仍然存在
Edit2: 我找到了一个更好的解决方案,至少在我的情况下,使用广播接收器,但调用者使用 context.grantUriPermissions()
方法。
我有一个向应用程序 B 发送意图的应用程序 A。应用程序 A 有一个文件提供程序并向 B 共享一个文件。它向意图添加授权并在 B 上启动服务。B 有IntentService 并处理意图。现在我想根据新的 Android O 策略将 B 更改为使用 JobIntentService。但是我不能从 A 调用 startService,所以我修改了我的代码以发送一个显式广播。我在剪辑数据中添加了 uri。但是,当我收到应用程序 B 的意图时,我遇到了安全异常。
应用程序 A:
Uri contentUri = FileProvider.getUriForFile(this, "com.myotherapp.fileprovider",
newFile);
Intent i = new Intent(INTENT_UPDATE);
ClipData clipData = ClipData.newRawUri(at.name(), contentUri);
i.setClipData(clipData);
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
i.setPackage("com.myapp");
sendBroadcast(i);
应用程序 B:
@Override
public void onReceive(Context arg0, Intent arg1) {
if (MyService.INTENT_UPDATE.equals(arg1.getAction())) {
MyService.enqueueWork(arg0, arg1);
}
}
堆栈:
Process: com.myapp, PID: 7690
java.lang.RuntimeException: Unable to start receiver com.myapp.MyReceiver: java.lang.SecurityException: UID 10083 does not have permission to content://com.myotherapp.fileprovider/files/Q3hnMrF8BsOJeznpVLizkTB4H6LkI90T.csv [user 0]
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3259)
at android.app.ActivityThread.-wrap17(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6540)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.SecurityException: UID 10083 does not have permission to content://com.myotherapp.fileprovider/files/Q3hnMrF8BsOJeznpVLizkTB4H6LkI90T.csv [user 0]
at android.os.Parcel.readException(Parcel.java:1948)
at android.os.Parcel.readException(Parcel.java:1894)
at android.app.job.IJobScheduler$Stub$Proxy.enqueue(IJobScheduler.java:211)
at android.app.JobSchedulerImpl.enqueue(JobSchedulerImpl.java:53)
at android.support.v4.app.JobIntentService$JobWorkEnqueuer.enqueueWork(JobIntentService.java:314)
at android.support.v4.app.JobIntentService.enqueueWork(JobIntentService.java:472)
at com.myapp.MyService.enqueueWork(MyService.java:41)
at com.myapp.myrec.onReceive(UpdateReceiver.java:21)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3252)
at android.app.ActivityThread.-wrap17(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6540)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
编辑: 我尝试使用在 JobInfo 上使用 setClipData 的 JobService。反正一点用都没有。
在 FileProvider
的文档中,他们说
A content URI allows you to grant read and write access using temporary access permissions. When you create an Intent containing a content URI, in order to send the content URI to a client app, you can also call Intent.setFlags() to add permissions. These permissions are available to the client app for as long as the stack for a receiving Activity is active. For an Intent going to a Service, the permissions are available as long as the Service is running.
我的猜测是,当您退出广播接收器的 onReceive
方法时,权限就消失了。
解法: 结果发现问题是不支持广播接收器,您只能定位活动和服务,如文档中所述。
好的,我找到问题了。广播接收器没有收到权限,您不能调用 startService
因此在这种情况下您只有一种使用 Job 服务的棘手方法。我创建了一个无显示activity来转发意图:
public class ProxyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyService.enqueueWork(this, getIntent());
finish();
}
}
但是我不完全确定这个解决方案,因为 activity 每次只能发送一个意图。如果你有多个意图是一个问题。您可以将 launchMode 与单个实例一起使用并使用 onHandleIntent(),这是一个好主意,但您不能再使用主题 no display 并且您不知道何时可以调用 finish()。
编辑: 正如 Ian Lake (Google) 所解释的那样“...如您所见,URI 权限确实可以通过传递链接到其他组件通过剪辑数据。”在Api 16+上使用setData()或setDataAndType()时系统会自动创建和剪辑数据,所以这解释了为什么它有效,但是单一意图的问题仍然存在
Edit2: 我找到了一个更好的解决方案,至少在我的情况下,使用广播接收器,但调用者使用 context.grantUriPermissions()
方法。