zhanghai / MaterialFiles

Material Design file manager for Android
https://play.google.com/store/apps/details?id=me.zhanghai.android.files
GNU General Public License v3.0
6.07k stars 410 forks source link

Cannot add/remove file and folders in work profile #1207

Closed tleydxdy closed 6 months ago

tleydxdy commented 6 months ago

I wanted to move files between my work profile (created using island) and the main profile. So I created a folder called "Main" in the work profile, then in the main profile's material files I select add storage>external storage>"work">"Main">use this folder . As expected I can now see the content in the Main folder of the work profile. However, if I try to move files into the folder or create new file/folders, I get this error Screenshot_20240421-001350 It seems that the user part "10" there was missing in some API calls or the API used does not understand work profiles and content url?

edit: adding the error text for search java.lang.SecurityException: Requested authority 10@com.android.externalstorage.documents doesn't match provider com.android.externalstorage.documents

zhanghai commented 6 months ago

The error is thrown at this line in Android framework.

        if (!mAuthority.equals(authority)) {
            throw new SecurityException(
                    "Requested authority " + authority + " doesn't match provider " + mAuthority);
        }

So the message does mean that the app requested the correct authority (10@ is the Android way of specifying a different user for a content URI), but the system itself didn't handle this multi-user correctly.

This doesn't impact reads because query() and openFile() isn't using this problematic callUnchecked(), but other methods are using it and indeed not working for cross-user cases. I think a fix will need to be made in the Android framework itself.

tleydxdy commented 6 months ago

would this mean the request was routed to the wrong documentsprovider? presumably there should be a instance of documentsprovider with mAuthority = 10?

zhanghai commented 6 months ago

I don't think so. The ExternalStroageProvider running inside any user should always be named the same and be for its current user. It's the user ID prefix that should be dropped at receiver side once cross-user routing is done but isn't.

To be specific, ContextImpl resolves the user-specific URI to a provider in the correct user here:

        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }

And most ContentProvider methods removes the user ID from the URI like here:

        public Cursor query(@NonNull AttributionSource attributionSource, Uri uri,
                @Nullable String[] projection, @Nullable Bundle queryArgs,
                @Nullable ICancellationSignal cancellationSignal) {
            uri = validateIncomingUri(uri);
            uri = maybeGetUriWithoutUserId(uri);
        }

But the same thing isn't happening in DocumentsProvider.call() ContentProvider.Transport.call(), hence the problem, and it needs to be fixed in the platform.

tleydxdy commented 6 months ago

thanks for the analysis! I opened https://issuetracker.google.com/issues/335997046

zhanghai commented 6 months ago

Correction: The fix should actually probably be in ContentProvider.Transport.call() instead of DocumentsProvider, since all user IDs are removed in the Transport.