12

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.

+2

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

+0

@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. –

+2

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

Odpowiedz

2

Po utworzeniu ChooserTarget należy utworzyć obraz ikony z danymi z pliku. To właśnie robi aplikacja Google Messenger.

File file = new File(new File(filesDir, "images"), imageFileName); 
Bitmap b = BitmapFactory.decodeStream(new FileInputStream(file)); 
Icon icon = Icon.createWithBitmap(b); 
return new ChooserTarget(title, icon, score, cn, extras); 

Możesz dodać trochę kompresji do bitmapy, jeśli jesteś tak pochłonięty.

Muszę jednak ostrzec, że należy uważać na ilość i rozmiar tych, które nakłada się na segregatory ... są to obiekty paczkowane, a od 7.0 segregator może wyrzucić TransacationTooLargeException jeśli umieścisz za dużo lub za dużo bitmap w tych ChooserTarget s lub w dowolnej przesyłce przesłanej przez nie.

+0

To nie zadziała. Dodałem informację, dlaczego na końcu nie padnie pytanie. –

+0

Zaktualizowana odpowiedź. –

+0

Skąd się dowiedziałeś, że konkretna aplikacja używa 'createWithBitmap'? –

-2

MODE_WORLD_READABLE jest wadą bezpieczeństwa.
Google najpierw wycofał go, a następnie całkowicie go usunął.
MODE_WORLD_READABLE został wycofany w wersjach do Androida M. A w Androidzie N nie jest już obsługiwany i generuje wyjątek SecurityException. Rozwiązanie: Spróbuj użyć innego trybu. Użyłem Context.MODE_PRIVATE i zadziałało.

+0

lol total no. –

+0

Pytanie OP dotyczy udostępniania plików między procesami ... Kontekst.MODE_PRIVATE nie będzie działał. –

Powiązane problemy