Mam kilka obrazów zapisanych w lokalnej aplikacji połączonej z określonymi kontekstami (takimi jak kontakty). Używam bezpośredniego udziału (API 23+) przez ChooserTargetService
, aby pokazać je do wyboru i chcę ChooserTarget
instancje, aby Icon
wypełnione tymi obrazami.Podanie ikony do selektora systemu za pomocą opcji ChooserTargetService, FileProvider i grantUriPermission
Pomyślałem więc mogę używać android.support.v4.content.FileProvider
tego (wewnątrz ChooserTargetService::onGetChooserTargets
):
val file = File(File(filesDir, "images"), imageFileName)
val contentUri = FileProvider.getUriForFile(this, "com.company.fileprovider", file)
val icon = Icon.createWithContentUri(contentUri)
oraz w manifeście:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mycompany.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths"/>
</provider>
ale problem jest uzyskać wyjątek
05-10 16:06:09.100 32444-32444/android:ui W/Icon: Unable to load image from URI: content://com.mycompany.fileprovider/images/icon_dice.png
java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.mycompany.fileprovider/images/icon_dice.png from pid=32444, uid=1000 requires the provider be exported, or grantUriPermission()
at android.os.Parcel.readException(Parcel.java:1684)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1147)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984)
at android.content.ContentResolver.openInputStream(ContentResolver.java:704)
at android.graphics.drawable.Icon.loadDrawableInner(Icon.java:335)
at android.graphics.drawable.Icon.loadDrawable(Icon.java:272)
at com.android.internal.app.ChooserActivity$ChooserTargetInfo.<init>(ChooserActivity.java:645)
at com.android.internal.app.ChooserActivity$ChooserListAdapter.addServiceResults(ChooserActivity.java:1003)
at com.android.internal.app.ChooserActivity$1.handleMessage(ChooserActivity.java:126)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Nie można zastosować sugestii "wyeksportowanej", ponieważ FileProvider
niestety ma on ustalony, aby nie pozwolić (od FileProvider.java
android wsparcia kodu źródłowego biblioteki):
// Sanity check our security
if (info.exported) {
throw new SecurityException("Provider must not be exported");
}
if (!info.grantUriPermissions) {
throw new SecurityException("Provider must grant uri permissions");
}
więc próbowałem zadzwonić
grantUriPermission("<something goes here>", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
ale to nie jest oczywiste, co należy umieścić w nazwie pakietu pierwszy parametr. Szczegóły wyjątku można odliczyć, że kod jest w com.android.internal.app.ChooserActivity
i jest wywoływany przez system.
Edit:
Korzystanie Icon.createWithFilePath
nie jest możliwe, ponieważ nie można uzyskać dostępu do pliku z innego procesu:
W/Icon: Unable to load image from path: /data/user/0/com.mycompany.app/files/images/image.png
java.io.FileNotFoundException: /data/user/0/com.mycompany.app/files/images/image.png (Permission denied)
i jeśli spróbujesz ustawić plik przestarzałe Context.MODE_WORLD_READABLE
dostaniesz:
java.lang.SecurityException: MODE_WORLD_READABLE no longer supported
na Andorid 7.
IMHO, lepiej byłoby użyć innego "ContentProvider", jednego z obsługą tylko do odczytu dla plików i można je wyeksportować. Twój proponowany hack uderza mnie jako jedną z rzeczy, które mogą działać na niektórych urządzeniach, ale nie na innych, na podstawie zmian Androida, zmian producenta urządzenia, niestandardowych zmian ROM itp. Ostatecznie planuję dodać tę funkcję do [mojego "StreamProvider"] (https://github.com/commonsguy/cwac-provider/issues/22). IMHO, 'ChooserTargetService' powinno obsługiwać twoje uprawnienia. – CommonsWare
@CommonsWare Dzięki. Prawdopodobnie wykorzystam twój "StreamProvider". Na początku próbowałem "android: exported =" true "' + 'android: readPermission =" android.permission.BIND_CHOOSER_TARGET_SERVICE "' (to samo uprawnienie jest wymagane dla klientów mojej implementacji 'ChooserTargetService'), co powinno być czymś, co po prostu działa i jest bezpieczny, jeśli nie jest to hardcoded ifs w 'FileProvider'. Czy to prawda, że ten hack może zostać złamany? Moduł obsługi plików/bezpośredniego udostępniania jest częścią systemu i nie można go zastąpić tak łatwo jak np. domyślna aplikacja uruchamiająca. –
Nie mogę wykluczyć, że ktoś zmienia identyfikator aplikacji dla części systemu, która zużywa 'Icon' z' ChooserTargetService'. Możesz wypróbować 'com.android.systemui' dla pakietu, ponieważ to działa [dla powiadomień dźwiękowych] (https://commonsware.com/blog/2016/09/07/notifications-sounds-android-7p0- aggravation.html). Lub, jeśli pliki znajdują się w pamięci zewnętrznej, użyj 'Uri.fromFile()' i 'StrictMode.setVmPolicy (new StrictMode.VmPolicy.Builder(). Build());', aby cofnąć kontrolę 'FileUriExposedException'. – CommonsWare