diff options
author | 2020-01-28 23:16:50 +0000 | |
---|---|---|
committer | 2020-02-06 14:52:14 +0000 | |
commit | 2788bf12a94a9c7131bd16b83964abab8f5435e8 (patch) | |
tree | e92b6215e0090234cd792f938d780352144c150e | |
parent | fa8ca4b617143c7ab8a12b85bee3815b723ac570 (diff) |
Loads UserId into DocumentInfo
Test: atest DocumentsUIGoogleTests
Bug: 148264822
Change-Id: Iccf754ec55fc0bc2f187768e97ef63c8f1743933
39 files changed, 278 insertions, 137 deletions
diff --git a/res/values/inspector_strings.xml b/res/values/inspector_strings.xml index b83967196..dc8ca7f1d 100644 --- a/res/values/inspector_strings.xml +++ b/res/values/inspector_strings.xml @@ -90,6 +90,8 @@ <string name="debug_stream_types" translatable="false">Stream types</string> <!-- String label for developer/debug file details, specifying the size of the file in bytes. --> <string name="debug_raw_size" translatable="false">Raw size (bytes)</string> + <!-- String label for developer/debug file details, specifying a user id --> + <string name="debug_user_id" translatable="false">User id</string> <!-- String label for developer/debug file details, specifying a file's uri/content address. --> <string name="debug_content_uri" translatable="false">Uri</string> <!-- String label for developer/debug file details, specifying document id. --> diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java index 322b3898a..ea4af7e73 100644 --- a/src/com/android/documentsui/AbstractActionHandler.java +++ b/src/com/android/documentsui/AbstractActionHandler.java @@ -350,6 +350,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA if (mSearchMgr.isSearching()) { loadDocument( doc.derivedUri, + doc.userId, (@Nullable DocumentStack stack) -> openFolderInSearchResult(stack, doc)); } else { openChildContainer(doc); @@ -381,7 +382,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA if (top.isArchive()) { // Swap the zip file in original provider and the one provided by ArchiveProvider. stack.pop(); - stack.push(mDocs.getArchiveDocument(top.derivedUri)); + stack.push(mDocs.getArchiveDocument(top.derivedUri, top.userId)); } mState.stack.reset(); @@ -409,7 +410,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA currentDoc = doc; } else if (doc.isArchive()) { // Archive. - currentDoc = mDocs.getArchiveDocument(doc.derivedUri); + currentDoc = mDocs.getArchiveDocument(doc.derivedUri, doc.userId); } assert(currentDoc != null); @@ -499,11 +500,12 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA throw new UnsupportedOperationException("Share not supported!"); } - protected final void loadDocument(Uri uri, LoadDocStackCallback callback) { + protected final void loadDocument(Uri uri, UserId userId, LoadDocStackCallback callback) { new LoadDocStackTask( mActivity, mProviders, mDocs, + userId, callback ).executeOnExecutor(mExecutors.lookup(uri.getAuthority()), uri); } @@ -539,7 +541,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA protected final boolean launchToDocument(Uri uri) { // We don't support launching to a document in an archive. if (!Providers.isArchiveUri(uri)) { - loadDocument(uri, this::onStackLoaded); + loadDocument(uri, UserId.DEFAULT_USER, this::onStackLoaded); return true; } diff --git a/src/com/android/documentsui/CreateDirectoryFragment.java b/src/com/android/documentsui/CreateDirectoryFragment.java index 1a35cbdc4..3bd344ff4 100644 --- a/src/com/android/documentsui/CreateDirectoryFragment.java +++ b/src/com/android/documentsui/CreateDirectoryFragment.java @@ -150,7 +150,7 @@ public class CreateDirectoryFragment extends DialogFragment { resolver, mCwd.derivedUri.getAuthority()); final Uri childUri = DocumentsContract.createDocument( wrap(client), mCwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName); - DocumentInfo doc = DocumentInfo.fromUri(resolver, childUri); + DocumentInfo doc = DocumentInfo.fromUri(resolver, childUri, mCwd.userId); return doc.isDirectory() ? doc : null; } catch (Exception e) { Log.w(TAG, "Failed to create directory", e); diff --git a/src/com/android/documentsui/DirectoryLoader.java b/src/com/android/documentsui/DirectoryLoader.java index 35ac16223..04928b1ca 100644 --- a/src/com/android/documentsui/DirectoryLoader.java +++ b/src/com/android/documentsui/DirectoryLoader.java @@ -142,7 +142,8 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { cursor.registerContentObserver(mObserver); - cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1); + cursor = new RootCursorWrapper(mDoc.userId, mUri.getAuthority(), mRoot.rootId, cursor, + -1); if (mSearchMode && !mFeatures.isFoldersInSearchResultsEnabled()) { // There is no findDocumentPath API. Enable filtering on folders in search mode. diff --git a/src/com/android/documentsui/DocumentsAccess.java b/src/com/android/documentsui/DocumentsAccess.java index 3028f1b67..6445a7c97 100644 --- a/src/com/android/documentsui/DocumentsAccess.java +++ b/src/com/android/documentsui/DocumentsAccess.java @@ -16,8 +16,6 @@ package com.android.documentsui; -import androidx.annotation.Nullable; - import static android.content.ContentResolver.wrap; import android.content.ContentProviderClient; @@ -31,9 +29,12 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Path; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.documentsui.archives.ArchivesProvider; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.RootInfo; +import com.android.documentsui.base.UserId; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -46,13 +47,16 @@ import java.util.List; public interface DocumentsAccess { @Nullable DocumentInfo getRootDocument(RootInfo root); - @Nullable DocumentInfo getDocument(Uri uri); - @Nullable DocumentInfo getArchiveDocument(Uri uri); + @Nullable DocumentInfo getDocument(Uri uri, UserId userId); + @Nullable DocumentInfo getArchiveDocument(Uri uri, UserId userId); boolean isDocumentUri(Uri uri); - @Nullable Path findDocumentPath(Uri uri) throws RemoteException, FileNotFoundException; - List<DocumentInfo> getDocuments(String authority, List<String> docIds) throws RemoteException; + @Nullable + Path findDocumentPath(Uri uri, UserId userId) throws RemoteException, FileNotFoundException; + + List<DocumentInfo> getDocuments(UserId userId, String authority, List<String> docIds) + throws RemoteException; @Nullable Uri createDocument(DocumentInfo parentDoc, String mimeType, String displayName); @@ -71,15 +75,16 @@ public interface DocumentsAccess { } @Override - public @Nullable DocumentInfo getRootDocument(RootInfo root) { - return getDocument( - DocumentsContract.buildDocumentUri(root.authority, root.documentId)); + @Nullable + public DocumentInfo getRootDocument(RootInfo root) { + return getDocument(DocumentsContract.buildDocumentUri(root.authority, root.documentId), + root.userId); } @Override - public @Nullable DocumentInfo getDocument(Uri uri) { + public @Nullable DocumentInfo getDocument(Uri uri, UserId userId) { try { - return DocumentInfo.fromUri(mContext.getContentResolver(), uri); + return DocumentInfo.fromUri(userId.getContentResolver(mContext), uri, userId); } catch (FileNotFoundException e) { Log.w(TAG, "Couldn't create DocumentInfo for uri: " + uri); } @@ -88,11 +93,11 @@ public interface DocumentsAccess { } @Override - public List<DocumentInfo> getDocuments(String authority, List<String> docIds) + public List<DocumentInfo> getDocuments(UserId userId, String authority, List<String> docIds) throws RemoteException { - try(final ContentProviderClient client = DocumentsApplication - .acquireUnstableProviderOrThrow(mContext.getContentResolver(), authority)) { + try (ContentProviderClient client = DocumentsApplication.acquireUnstableProviderOrThrow( + userId.getContentResolver(mContext), authority)) { List<DocumentInfo> result = new ArrayList<>(docIds.size()); for (String docId : docIds) { @@ -103,7 +108,7 @@ public interface DocumentsAccess { throw new RemoteException("Failed to move cursor."); } - result.add(DocumentInfo.fromCursor(cursor, authority)); + result.add(DocumentInfo.fromCursor(cursor, userId, authority)); } } @@ -112,9 +117,10 @@ public interface DocumentsAccess { } @Override - public DocumentInfo getArchiveDocument(Uri uri) { - return getDocument(ArchivesProvider.buildUriForArchive(uri, - ParcelFileDescriptor.MODE_READ_ONLY)); + public DocumentInfo getArchiveDocument(Uri uri, UserId userId) { + return getDocument( + ArchivesProvider.buildUriForArchive(uri, ParcelFileDescriptor.MODE_READ_ONLY), + userId); } @Override @@ -123,8 +129,9 @@ public interface DocumentsAccess { } @Override - public Path findDocumentPath(Uri docUri) throws RemoteException, FileNotFoundException { - final ContentResolver resolver = mContext.getContentResolver(); + public Path findDocumentPath(Uri docUri, UserId userId) + throws RemoteException, FileNotFoundException { + final ContentResolver resolver = userId.getContentResolver(mContext); try (final ContentProviderClient client = DocumentsApplication .acquireUnstableProviderOrThrow(resolver, docUri.getAuthority())) { return DocumentsContract.findDocumentPath(wrap(client), docUri); diff --git a/src/com/android/documentsui/GlobalSearchLoader.java b/src/com/android/documentsui/GlobalSearchLoader.java index 073b3d0f3..2f603c4e8 100644 --- a/src/com/android/documentsui/GlobalSearchLoader.java +++ b/src/com/android/documentsui/GlobalSearchLoader.java @@ -105,7 +105,8 @@ public class GlobalSearchLoader extends MultiRootDocumentsLoader { @Override protected RootCursorWrapper generateResultCursor(RootInfo rootInfo, Cursor oriCursor) { - return new RootCursorWrapper(authority, rootInfo.rootId, oriCursor, -1 /* maxCount */); + return new RootCursorWrapper(rootInfo.userId, authority, rootInfo.rootId, oriCursor, + -1 /* maxCount */); } } } diff --git a/src/com/android/documentsui/LoadDocStackTask.java b/src/com/android/documentsui/LoadDocStackTask.java index df6db769f..cb00c035f 100644 --- a/src/com/android/documentsui/LoadDocStackTask.java +++ b/src/com/android/documentsui/LoadDocStackTask.java @@ -47,16 +47,19 @@ public class LoadDocStackTask extends PairedTask<Activity, Uri, DocumentStack> { private final ProvidersAccess mProviders; private final DocumentsAccess mDocs; + private final UserId mUserId; private final LoadDocStackCallback mCallback; public LoadDocStackTask( Activity activity, ProvidersAccess providers, DocumentsAccess docs, + UserId userId, LoadDocStackCallback callback) { super(activity); mProviders = providers; mDocs = docs; + mUserId = userId; mCallback = callback; } @@ -74,7 +77,7 @@ public class LoadDocStackTask extends PairedTask<Activity, Uri, DocumentStack> { } try { - final Path path = mDocs.findDocumentPath(docUri); + final Path path = mDocs.findDocumentPath(docUri, mUserId); if (path != null) { return buildStack(docUri.getAuthority(), path); } else { @@ -99,14 +102,14 @@ public class LoadDocStackTask extends PairedTask<Activity, Uri, DocumentStack> { throw new IllegalStateException("Provider doesn't provider root id."); } - RootInfo root = mProviders.getRootOneshot(UserId.DEFAULT_USER, authority, path.getRootId()); + RootInfo root = mProviders.getRootOneshot(mUserId, authority, path.getRootId()); if (root == null) { throw new IllegalStateException( "Failed to load root on user " + root.userId + " for authority: " + authority + " and root ID: " + path.getRootId() + "."); } - List<DocumentInfo> docs = mDocs.getDocuments(authority, path.getPath()); + List<DocumentInfo> docs = mDocs.getDocuments(root.userId, authority, path.getPath()); return new DocumentStack(root, docs); } diff --git a/src/com/android/documentsui/RecentsLoader.java b/src/com/android/documentsui/RecentsLoader.java index f9eacaa0a..45864ce98 100644 --- a/src/com/android/documentsui/RecentsLoader.java +++ b/src/com/android/documentsui/RecentsLoader.java @@ -82,7 +82,8 @@ public class RecentsLoader extends MultiRootDocumentsLoader { @Override protected RootCursorWrapper generateResultCursor(RootInfo rootInfo, Cursor oriCursor) { - return new RootCursorWrapper(authority, rootInfo.rootId, oriCursor, MAX_DOCS_FROM_ROOT); + return new RootCursorWrapper(rootInfo.userId, authority, rootInfo.rootId, oriCursor, + MAX_DOCS_FROM_ROOT); } } } diff --git a/src/com/android/documentsui/base/DocumentInfo.java b/src/com/android/documentsui/base/DocumentInfo.java index e836b7057..db11933d8 100644 --- a/src/com/android/documentsui/base/DocumentInfo.java +++ b/src/com/android/documentsui/base/DocumentInfo.java @@ -54,7 +54,9 @@ public class DocumentInfo implements Durable, Parcelable { private static final String TAG = "DocumentInfo"; private static final int VERSION_INIT = 1; private static final int VERSION_SPLIT_URI = 2; + private static final int VERSION_USER_ID = 3; + public UserId userId; public String authority; public String documentId; public String mimeType; @@ -74,6 +76,7 @@ public class DocumentInfo implements Durable, Parcelable { @Override public void reset() { + userId = UserId.UNSPECIFIED_USER; authority = null; documentId = null; mimeType = null; @@ -90,9 +93,12 @@ public class DocumentInfo implements Durable, Parcelable { public void read(DataInputStream in) throws IOException { final int version = in.readInt(); switch (version) { - case VERSION_INIT: - throw new ProtocolException("Ignored upgrade"); + case VERSION_USER_ID: + userId = UserId.read(in); case VERSION_SPLIT_URI: + if (version < VERSION_USER_ID) { + userId = UserId.CURRENT_USER; + } authority = DurableUtils.readNullableString(in); documentId = DurableUtils.readNullableString(in); mimeType = DurableUtils.readNullableString(in); @@ -104,6 +110,8 @@ public class DocumentInfo implements Durable, Parcelable { icon = in.readInt(); deriveFields(); break; + case VERSION_INIT: + throw new ProtocolException("Ignored upgrade"); default: throw new ProtocolException("Unknown version " + version); } @@ -111,7 +119,8 @@ public class DocumentInfo implements Durable, Parcelable { @Override public void write(DataOutputStream out) throws IOException { - out.writeInt(VERSION_SPLIT_URI); + out.writeInt(VERSION_USER_ID); + UserId.write(out, userId); DurableUtils.writeNullableString(out, authority); DurableUtils.writeNullableString(out, documentId); DurableUtils.writeNullableString(out, mimeType); @@ -148,19 +157,22 @@ public class DocumentInfo implements Durable, Parcelable { }; public static DocumentInfo fromDirectoryCursor(Cursor cursor) { - assert(cursor != null); + assert (cursor != null); + assert (cursor.getColumnIndex(RootCursorWrapper.COLUMN_USER_ID) >= 0); + final UserId userId = UserId.of(getCursorInt(cursor, RootCursorWrapper.COLUMN_USER_ID)); final String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY); - return fromCursor(cursor, authority); + return fromCursor(cursor, userId, authority); } - public static DocumentInfo fromCursor(Cursor cursor, String authority) { + public static DocumentInfo fromCursor(Cursor cursor, UserId userId, String authority) { assert(cursor != null); final DocumentInfo info = new DocumentInfo(); - info.updateFromCursor(cursor, authority); + info.updateFromCursor(cursor, userId, authority); return info; } - public void updateFromCursor(Cursor cursor, String authority) { + public void updateFromCursor(Cursor cursor, UserId userId, String authority) { + this.userId = userId; this.authority = authority; this.documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID); this.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); @@ -173,22 +185,27 @@ public class DocumentInfo implements Durable, Parcelable { this.deriveFields(); } - public static DocumentInfo fromUri(ContentResolver resolver, Uri uri) + /** + * Resolves a document info from the uri. The caller should specify the user of the resolver + * by providing a {@link UserId}. + */ + public static DocumentInfo fromUri(ContentResolver resolver, Uri uri, UserId userId) throws FileNotFoundException { final DocumentInfo info = new DocumentInfo(); - info.updateFromUri(resolver, uri); + info.updateFromUri(resolver, uri, userId); return info; } /** - * Update a possibly stale restored document against a live - * {@link DocumentsProvider}. + * Update a possibly stale restored document against a live {@link DocumentsProvider}. The + * caller should specify the user of the resolver by providing a {@link UserId}. */ - public void updateSelf(ContentResolver resolver) throws FileNotFoundException { - updateFromUri(resolver, derivedUri); + public void updateSelf(ContentResolver resolver, UserId userId) throws FileNotFoundException { + updateFromUri(resolver, derivedUri, userId); } - public void updateFromUri(ContentResolver resolver, Uri uri) throws FileNotFoundException { + private void updateFromUri(ContentResolver resolver, Uri uri, UserId userId) + throws FileNotFoundException { ContentProviderClient client = null; Cursor cursor = null; try { @@ -198,7 +215,7 @@ public class DocumentInfo implements Durable, Parcelable { if (!cursor.moveToFirst()) { throw new FileNotFoundException("Missing details for " + uri); } - updateFromCursor(cursor, uri.getAuthority()); + updateFromCursor(cursor, userId, uri.getAuthority()); } catch (Throwable t) { throw asFileNotFoundException(t); } finally { @@ -216,6 +233,7 @@ public class DocumentInfo implements Durable, Parcelable { public String toString() { return "DocumentInfo{" + "docId=" + documentId + + ", userId=" + userId + ", name=" + displayName + ", mimeType=" + mimeType + ", isContainer=" + isContainer() @@ -309,7 +327,7 @@ public class DocumentInfo implements Durable, Parcelable { @Override public int hashCode() { - return derivedUri.hashCode() + mimeType.hashCode(); + return userId.hashCode() + derivedUri.hashCode() + mimeType.hashCode(); } @Override @@ -325,7 +343,8 @@ public class DocumentInfo implements Durable, Parcelable { if (o instanceof DocumentInfo) { DocumentInfo other = (DocumentInfo) o; // Uri + mime type should be totally unique. - return Objects.equals(derivedUri, other.derivedUri) + return Objects.equals(userId, other.userId) + && Objects.equals(derivedUri, other.derivedUri) && Objects.equals(mimeType, other.mimeType); } diff --git a/src/com/android/documentsui/base/DocumentStack.java b/src/com/android/documentsui/base/DocumentStack.java index 06e00f16c..14740e1eb 100644 --- a/src/com/android/documentsui/base/DocumentStack.java +++ b/src/com/android/documentsui/base/DocumentStack.java @@ -20,7 +20,7 @@ import static androidx.core.util.Preconditions.checkArgument; import static com.android.documentsui.base.SharedMinimal.DEBUG; -import android.content.ContentResolver; +import android.content.Context; import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; @@ -234,14 +234,14 @@ public class DocumentStack implements Durable, Parcelable { * Update a possibly stale restored stack against a live * {@link DocumentsProvider}. */ - private void updateDocuments(ContentResolver resolver) throws FileNotFoundException { + private void updateDocuments(Context context) throws FileNotFoundException { for (DocumentInfo info : mList) { - info.updateSelf(resolver); + info.updateSelf(info.userId.getContentResolver(context), info.userId); } } public static @Nullable DocumentStack fromLastAccessedCursor( - Cursor cursor, Collection<RootInfo> matchingRoots, ContentResolver resolver) + Cursor cursor, Collection<RootInfo> matchingRoots, Context context) throws IOException { if (cursor.moveToFirst()) { @@ -251,7 +251,7 @@ public class DocumentStack implements Durable, Parcelable { DurableUtils.readFromArray(rawStack, stack); stack.updateRoot(matchingRoots); - stack.updateDocuments(resolver); + stack.updateDocuments(context); return stack; } diff --git a/src/com/android/documentsui/base/UserId.java b/src/com/android/documentsui/base/UserId.java index b4177478e..f1d0cdc2c 100644 --- a/src/com/android/documentsui/base/UserId.java +++ b/src/com/android/documentsui/base/UserId.java @@ -21,10 +21,12 @@ import static androidx.core.util.Preconditions.checkNotNull; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Process; import android.os.UserHandle; import androidx.annotation.VisibleForTesting; +import androidx.loader.content.CursorLoader; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -61,6 +63,16 @@ public final class UserId { return new UserId(userHandle); } + + /** + * Returns a {@link UserId} for the given user id identifier. + * + * @see UserHandle#getIdentifier + */ + public static UserId of(int userIdentifier) { + return of(UserHandle.of(userIdentifier)); + } + /** * Returns the given context if the user is the current user or unspecified. Otherwise, returns * an "android" package context as the user. @@ -93,15 +105,21 @@ public final class UserId { return asContext(context).getContentResolver(); } + /** + * Returns an identifier stored in this user id. This can be used to recreate the {@link UserId} + * by {@link UserId#of(int)}. + */ + public int getIdentifier() { + return mUserHandle.getIdentifier(); + } + private boolean isUnspecified() { return UNSPECIFIED_USER.equals(this); } @Override public String toString() { - return "UserId{" - + (isUnspecified() ? "UNSPECIFIED" : mUserHandle.getIdentifier()) - + "}"; + return isUnspecified() ? "UNSPECIFIED" : String.valueOf(mUserHandle.getIdentifier()); } @Override @@ -148,4 +166,11 @@ public final class UserId { out.writeInt(VERSION_INIT); out.writeInt(userId.mUserHandle.getIdentifier()); } + + /** + * Create a cursor loader of the user for the given uri. + */ + public static CursorLoader createCursorLoader(Context context, Uri uri, UserId userId) { + return new CursorLoader(userId.asContext(context), uri, null, null, null, null); + } } diff --git a/src/com/android/documentsui/dirlist/GridDocumentHolder.java b/src/com/android/documentsui/dirlist/GridDocumentHolder.java index e27d21097..dbc665a8a 100644 --- a/src/com/android/documentsui/dirlist/GridDocumentHolder.java +++ b/src/com/android/documentsui/dirlist/GridDocumentHolder.java @@ -16,12 +16,11 @@ package com.android.documentsui.dirlist; +import static com.android.documentsui.base.DocumentInfo.getCursorInt; import static com.android.documentsui.base.DocumentInfo.getCursorLong; import static com.android.documentsui.base.DocumentInfo.getCursorString; -import androidx.annotation.ColorInt; import android.content.Context; -import android.content.res.TypedArray; import android.database.Cursor; import android.provider.DocumentsContract.Document; import android.text.format.Formatter; @@ -34,6 +33,7 @@ import android.widget.TextView; import com.android.documentsui.R; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.Shared; +import com.android.documentsui.base.UserId; import com.android.documentsui.roots.RootCursorWrapper; import com.android.documentsui.ui.Views; @@ -147,7 +147,9 @@ final class GridDocumentHolder extends DocumentHolder { mModelId = modelId; - mDoc.updateFromCursor(cursor, getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY)); + mDoc.updateFromCursor(cursor, + UserId.of(getCursorInt(cursor, RootCursorWrapper.COLUMN_USER_ID)), + getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY)); mIconHelper.stopLoading(mIconThumb); diff --git a/src/com/android/documentsui/dirlist/GridPhotoHolder.java b/src/com/android/documentsui/dirlist/GridPhotoHolder.java index 5a5feecc5..23890872f 100644 --- a/src/com/android/documentsui/dirlist/GridPhotoHolder.java +++ b/src/com/android/documentsui/dirlist/GridPhotoHolder.java @@ -16,6 +16,7 @@ package com.android.documentsui.dirlist; +import static com.android.documentsui.base.DocumentInfo.getCursorInt; import static com.android.documentsui.base.DocumentInfo.getCursorLong; import static com.android.documentsui.base.DocumentInfo.getCursorString; @@ -31,6 +32,7 @@ import android.widget.ImageView; import com.android.documentsui.R; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.Shared; +import com.android.documentsui.base.UserId; import com.android.documentsui.roots.RootCursorWrapper; import com.android.documentsui.ui.Views; @@ -127,7 +129,9 @@ final class GridPhotoHolder extends DocumentHolder { mModelId = modelId; - mDoc.updateFromCursor(cursor, getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY)); + mDoc.updateFromCursor(cursor, + UserId.of(getCursorInt(cursor, RootCursorWrapper.COLUMN_USER_ID)), + getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY)); mIconHelper.stopLoading(mIconThumb); diff --git a/src/com/android/documentsui/dirlist/ListDocumentHolder.java b/src/com/android/documentsui/dirlist/ListDocumentHolder.java index 55b4ec050..71b590c3e 100644 --- a/src/com/android/documentsui/dirlist/ListDocumentHolder.java +++ b/src/com/android/documentsui/dirlist/ListDocumentHolder.java @@ -16,9 +16,9 @@ package com.android.documentsui.dirlist; +import static com.android.documentsui.base.DocumentInfo.getCursorInt; import static com.android.documentsui.base.DocumentInfo.getCursorString; -import androidx.annotation.Nullable; import android.content.Context; import android.database.Cursor; import android.graphics.Rect; @@ -30,11 +30,14 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.documentsui.R; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.Lookup; import com.android.documentsui.base.Shared; import com.android.documentsui.base.State; +import com.android.documentsui.base.UserId; import com.android.documentsui.roots.RootCursorWrapper; import com.android.documentsui.ui.Views; @@ -179,7 +182,9 @@ final class ListDocumentHolder extends DocumentHolder { mModelId = modelId; - mDoc.updateFromCursor(cursor, getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY)); + mDoc.updateFromCursor(cursor, + UserId.of(getCursorInt(cursor, RootCursorWrapper.COLUMN_USER_ID)), + getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY)); mIconHelper.stopLoading(mIconThumb); diff --git a/src/com/android/documentsui/files/ActionHandler.java b/src/com/android/documentsui/files/ActionHandler.java index 5f753e62f..9849a4e60 100644 --- a/src/com/android/documentsui/files/ActionHandler.java +++ b/src/com/android/documentsui/files/ActionHandler.java @@ -186,7 +186,7 @@ public class ActionHandler<T extends FragmentActivity & AbstractActionHandler.Co resolver, document.derivedUri.getAuthority()); Uri newUri = DocumentsContract.renameDocument( wrap(client), document.derivedUri, name); - return DocumentInfo.fromUri(resolver, newUri); + return DocumentInfo.fromUri(resolver, newUri, document.userId); } catch (Exception e) { Log.w(TAG, "Failed to rename file", e); return null; @@ -745,8 +745,7 @@ public class ActionHandler<T extends FragmentActivity & AbstractActionHandler.Co @Override public void showInspector(DocumentInfo doc) { Metrics.logUserAction(MetricConsts.USER_ACTION_INSPECTOR); - Intent intent = new Intent(mActivity, InspectorActivity.class); - intent.setData(doc.derivedUri); + Intent intent = InspectorActivity.createIntent(mActivity, doc.derivedUri, doc.userId); // permit the display of debug info about the file. intent.putExtra( diff --git a/src/com/android/documentsui/inspector/DebugView.java b/src/com/android/documentsui/inspector/DebugView.java index 72dedbd0e..ffd4b7e05 100644 --- a/src/com/android/documentsui/inspector/DebugView.java +++ b/src/com/android/documentsui/inspector/DebugView.java @@ -72,6 +72,7 @@ public class DebugView extends TableView implements DebugDisplay { public void accept(DocumentInfo info) { setTitle(R.string.inspector_debug_section, true); + put(R.string.debug_user_id, info.userId.toString()); put(R.string.debug_content_uri, info.derivedUri.toString()); put(R.string.debug_document_id, info.documentId); put(R.string.debug_raw_mimetype, info.mimeType); diff --git a/src/com/android/documentsui/inspector/InspectorActivity.java b/src/com/android/documentsui/inspector/InspectorActivity.java index f7c2c8de5..948d66fb2 100644 --- a/src/com/android/documentsui/inspector/InspectorActivity.java +++ b/src/com/android/documentsui/inspector/InspectorActivity.java @@ -17,6 +17,7 @@ package com.android.documentsui.inspector; import static androidx.core.util.Preconditions.checkArgument; +import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.net.Uri; @@ -31,14 +32,27 @@ import androidx.loader.app.LoaderManager; import com.android.documentsui.R; import com.android.documentsui.base.Shared; +import com.android.documentsui.base.UserId; import com.android.documentsui.inspector.InspectorController.DataSupplier; public class InspectorActivity extends AppCompatActivity { + private static final String EXTRA_USER_ID = "user_id"; + private InspectorController mController; private View mView; private Toolbar mToolbar; + /** + * Returns an intent for inspector activity with a uri and user of the uri. + */ + public static Intent createIntent(Context context, Uri uri, UserId userId) { + Intent intent = new Intent(context, InspectorActivity.class); + intent.setData(uri); + intent.putExtra(EXTRA_USER_ID, userId.getIdentifier()); + return intent; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -67,8 +81,11 @@ public class InspectorActivity extends AppCompatActivity { protected void onStart() { super.onStart(); Uri uri = getIntent().getData(); + checkArgument(getIntent().hasExtra(EXTRA_USER_ID)); checkArgument(uri.getScheme().equals("content")); - mController.loadInfo(uri); + UserId userId = UserId.of( + getIntent().getIntExtra(EXTRA_USER_ID, UserId.UNSPECIFIED_USER.getIdentifier())); + mController.loadInfo(uri, userId); } @Override diff --git a/src/com/android/documentsui/inspector/InspectorController.java b/src/com/android/documentsui/inspector/InspectorController.java index aad7426db..3a9255b6f 100644 --- a/src/com/android/documentsui/inspector/InspectorController.java +++ b/src/com/android/documentsui/inspector/InspectorController.java @@ -149,8 +149,8 @@ public final class InspectorController { mLoader.reset(); } - public void loadInfo(Uri uri) { - mLoader.loadDocInfo(uri, this::updateView); + public void loadInfo(Uri uri, UserId userId) { + mLoader.loadDocInfo(uri, userId, this::updateView); } /** @@ -281,10 +281,11 @@ public final class InspectorController { * Starts the Asynchronous process of loading file data. * * @param uri - A content uri to query metadata from. + * @param userId - A user to load the uri from. * @param callback - Function to be called when the loader has finished loading metadata. A * DocumentInfo will be sent to this method. DocumentInfo may be null. */ - void loadDocInfo(Uri uri, Consumer<DocumentInfo> callback); + void loadDocInfo(Uri uri, UserId userId, Consumer<DocumentInfo> callback); /** * Loads a folders item count. diff --git a/src/com/android/documentsui/inspector/RuntimeDataSupplier.java b/src/com/android/documentsui/inspector/RuntimeDataSupplier.java index a7bac8140..01672ca92 100644 --- a/src/com/android/documentsui/inspector/RuntimeDataSupplier.java +++ b/src/com/android/documentsui/inspector/RuntimeDataSupplier.java @@ -25,13 +25,14 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.provider.DocumentsContract; + import androidx.annotation.Nullable; import androidx.loader.app.LoaderManager; import androidx.loader.app.LoaderManager.LoaderCallbacks; -import androidx.loader.content.CursorLoader; import androidx.loader.content.Loader; import com.android.documentsui.base.DocumentInfo; +import com.android.documentsui.base.UserId; import com.android.documentsui.inspector.InspectorController.DataSupplier; import java.util.ArrayList; @@ -63,7 +64,7 @@ public class RuntimeDataSupplier implements DataSupplier { * Loads documents metadata. */ @Override - public void loadDocInfo(Uri uri, Consumer<DocumentInfo> updateView) { + public void loadDocInfo(Uri uri, UserId userId, Consumer<DocumentInfo> updateView) { //Check that we have correct Uri type and that the loader is not already created. checkArgument(uri.getScheme().equals("content")); @@ -74,13 +75,14 @@ public class RuntimeDataSupplier implements DataSupplier { if (cursor == null || !cursor.moveToFirst()) { updateView.accept(null); } else { - DocumentInfo docInfo = DocumentInfo.fromCursor(cursor, uri.getAuthority()); + DocumentInfo docInfo = DocumentInfo.fromCursor(cursor, userId, + uri.getAuthority()); updateView.accept(docInfo); } } }; - mDocCallbacks = new Callbacks(mContext, uri, callback); + mDocCallbacks = new Callbacks(mContext, uri, userId, callback); mLoaderMgr.restartLoader(getNextLoaderId(), null, mDocCallbacks); } @@ -102,7 +104,7 @@ public class RuntimeDataSupplier implements DataSupplier { } }; - mDirCallbacks = new Callbacks(mContext, children, callback); + mDirCallbacks = new Callbacks(mContext, children, directory.userId, callback); mLoaderMgr.restartLoader(getNextLoaderId(), null, mDirCallbacks); } @@ -161,21 +163,24 @@ public class RuntimeDataSupplier implements DataSupplier { private final Context mContext; private final Uri mUri; + private final UserId mUserId; private final Consumer<Cursor> mCallback; private ContentObserver mObserver; - Callbacks(Context context, Uri uri, Consumer<Cursor> callback) { + Callbacks(Context context, Uri uri, UserId userId, Consumer<Cursor> callback) { checkArgument(context != null); checkArgument(uri != null); + checkArgument(userId != null); checkArgument(callback != null); mContext = context; mUri = uri; + mUserId = userId; mCallback = callback; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { - return new CursorLoader(mContext, mUri, null, null, null, null); + return UserId.createCursorLoader(mContext, mUri, mUserId); } @Override @@ -192,7 +197,7 @@ public class RuntimeDataSupplier implements DataSupplier { @Override public void onLoaderReset(Loader<Cursor> loader) { if (mObserver != null) { - mContext.getContentResolver().unregisterContentObserver(mObserver); + mUserId.getContentResolver(mContext).unregisterContentObserver(mObserver); } } diff --git a/src/com/android/documentsui/picker/LastAccessedStorage.java b/src/com/android/documentsui/picker/LastAccessedStorage.java index b2b849b2e..e0d6723a7 100644 --- a/src/com/android/documentsui/picker/LastAccessedStorage.java +++ b/src/com/android/documentsui/picker/LastAccessedStorage.java @@ -21,6 +21,7 @@ import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; +import android.os.FileUtils; import android.util.Log; import com.android.documentsui.base.DocumentStack; @@ -28,8 +29,6 @@ import com.android.documentsui.base.Shared; import com.android.documentsui.base.State; import com.android.documentsui.roots.ProvidersAccess; -import android.os.FileUtils; - import java.io.IOException; import javax.annotation.Nullable; @@ -64,8 +63,8 @@ public interface LastAccessedStorage { final ContentResolver resolver = activity.getContentResolver(); Cursor cursor = resolver.query(resumeUri, null, null, null, null); try { - return DocumentStack.fromLastAccessedCursor( - cursor, providers.getMatchingRootsBlocking(state), resolver); + return DocumentStack.fromLastAccessedCursor(cursor, + providers.getMatchingRootsBlocking(state), activity); } catch (IOException e) { Log.w(TAG, "Failed to resume: ", e); } finally { diff --git a/src/com/android/documentsui/roots/RootCursorWrapper.java b/src/com/android/documentsui/roots/RootCursorWrapper.java index d220f688b..f11452d7e 100644 --- a/src/com/android/documentsui/roots/RootCursorWrapper.java +++ b/src/com/android/documentsui/roots/RootCursorWrapper.java @@ -23,10 +23,13 @@ import android.util.Log; import static com.android.documentsui.base.SharedMinimal.VERBOSE; +import com.android.documentsui.base.UserId; + /** * Cursor wrapper that adds columns to identify which root a document came from. */ public class RootCursorWrapper extends AbstractCursor { + private final UserId mUserId; private final String mAuthority; private final String mRootId; @@ -37,12 +40,16 @@ public class RootCursorWrapper extends AbstractCursor { private final int mAuthorityIndex; private final int mRootIdIndex; + private final int mUserIdIndex; + public static final String COLUMN_USER_ID = "android:userId"; public static final String COLUMN_AUTHORITY = "android:authority"; public static final String COLUMN_ROOT_ID = "android:rootId"; private static final String TAG = "RootCursorWrapper"; - public RootCursorWrapper(String authority, String rootId, Cursor cursor, int maxCount) { + public RootCursorWrapper(UserId userId, String authority, String rootId, Cursor cursor, + int maxCount) { + mUserId = userId; mAuthority = authority; mRootId = rootId; mCursor = cursor; @@ -54,17 +61,22 @@ public class RootCursorWrapper extends AbstractCursor { mCount = count; } - if (cursor.getColumnIndex(COLUMN_AUTHORITY) != -1 + if (cursor.getColumnIndex(COLUMN_USER_ID) != -1 + || cursor.getColumnIndex(COLUMN_AUTHORITY) != -1 || cursor.getColumnIndex(COLUMN_ROOT_ID) != -1) { throw new IllegalArgumentException("Cursor contains internal columns!"); } final String[] before = cursor.getColumnNames(); - mColumnNames = new String[before.length + 2]; + // Create a new columnNames and copy the existing one to it. + // Add the internal column names to the end of the array. + mColumnNames = new String[before.length + 3]; System.arraycopy(before, 0, mColumnNames, 0, before.length); mAuthorityIndex = before.length; mRootIdIndex = before.length + 1; + mUserIdIndex = before.length + 2; mColumnNames[mAuthorityIndex] = COLUMN_AUTHORITY; mColumnNames[mRootIdIndex] = COLUMN_ROOT_ID; + mColumnNames[mUserIdIndex] = COLUMN_USER_ID; } @Override @@ -112,7 +124,11 @@ public class RootCursorWrapper extends AbstractCursor { @Override public int getInt(int column) { - return mCursor.getInt(column); + if (column == mUserIdIndex) { + return mUserId.getIdentifier(); + } else { + return mCursor.getInt(column); + } } @Override diff --git a/src/com/android/documentsui/services/CompressJob.java b/src/com/android/documentsui/services/CompressJob.java index 555149bf6..1f2ea4436 100644 --- a/src/com/android/documentsui/services/CompressJob.java +++ b/src/com/android/documentsui/services/CompressJob.java @@ -16,7 +16,6 @@ package com.android.documentsui.services; -import static com.android.documentsui.base.SharedMinimal.DEBUG; import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE; import android.app.Notification; @@ -28,21 +27,18 @@ import android.os.Messenger; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.provider.DocumentsContract; -import android.provider.DocumentsContract.Document; import android.util.Log; -import com.android.documentsui.Metrics; import com.android.documentsui.R; import com.android.documentsui.archives.ArchivesProvider; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.DocumentStack; import com.android.documentsui.base.Features; +import com.android.documentsui.base.UserId; import com.android.documentsui.clipping.UrisSupplier; import java.io.FileNotFoundException; -import javax.annotation.Nullable; - // TODO: Stop extending CopyJob. final class CompressJob extends CopyJob { @@ -113,7 +109,7 @@ final class CompressJob extends CopyJob { try { mDstInfo = DocumentInfo.fromUri(resolver, ArchivesProvider.buildUriForArchive( - archiveUri, ParcelFileDescriptor.MODE_WRITE_ONLY)); + archiveUri, ParcelFileDescriptor.MODE_WRITE_ONLY), UserId.DEFAULT_USER); ArchivesProvider.acquireArchive(getClient(mDstInfo), mDstInfo.derivedUri); } catch (FileNotFoundException e) { Log.e(TAG, "Failed to create dstInfo.", e); diff --git a/src/com/android/documentsui/services/CopyJob.java b/src/com/android/documentsui/services/CopyJob.java index c8bb8b163..c0ef0c971 100644 --- a/src/com/android/documentsui/services/CopyJob.java +++ b/src/com/android/documentsui/services/CopyJob.java @@ -443,7 +443,7 @@ class CopyJob extends ResolvedResourcesJob { DocumentInfo dstInfo = null; try { - dstInfo = DocumentInfo.fromUri(getContentResolver(), dstUri); + dstInfo = DocumentInfo.fromUri(getContentResolver(), dstUri, dest.userId); } catch (FileNotFoundException | RuntimeException e) { Metrics.logFileOperationFailure( appContext, MetricConsts.SUBFILEOP_QUERY_DOCUMENT, dstUri); @@ -493,7 +493,7 @@ class CopyJob extends ResolvedResourcesJob { DocumentInfo src; while (cursor.moveToNext() && !isCanceled()) { try { - src = DocumentInfo.fromCursor(cursor, srcDir.authority); + src = DocumentInfo.fromCursor(cursor, srcDir.userId, srcDir.authority); processDocument(src, srcDir, destDir); } catch (RuntimeException e) { Log.e(TAG, String.format( diff --git a/src/com/android/documentsui/services/DeleteJob.java b/src/com/android/documentsui/services/DeleteJob.java index 38b8288cd..ede46a937 100644 --- a/src/com/android/documentsui/services/DeleteJob.java +++ b/src/com/android/documentsui/services/DeleteJob.java @@ -32,6 +32,7 @@ import com.android.documentsui.R; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.DocumentStack; import com.android.documentsui.base.Features; +import com.android.documentsui.base.UserId; import com.android.documentsui.clipping.UrisSupplier; import java.io.FileNotFoundException; @@ -103,7 +104,7 @@ final class DeleteJob extends ResolvedResourcesJob { DocumentInfo parentDoc; try { parentDoc = mParentUri != null - ? DocumentInfo.fromUri(resolver, mParentUri) + ? DocumentInfo.fromUri(resolver, mParentUri, UserId.DEFAULT_USER) : null; } catch (FileNotFoundException e) { Log.e(TAG, "Failed to resolve parent from Uri: " + mParentUri + ". Cannot continue.", e); diff --git a/src/com/android/documentsui/services/FileOperation.java b/src/com/android/documentsui/services/FileOperation.java index 29393a81e..89525bee1 100644 --- a/src/com/android/documentsui/services/FileOperation.java +++ b/src/com/android/documentsui/services/FileOperation.java @@ -16,13 +16,14 @@ package com.android.documentsui.services; +import static androidx.core.util.Preconditions.checkArgument; + import static com.android.documentsui.services.FileOperationService.OPERATION_COMPRESS; import static com.android.documentsui.services.FileOperationService.OPERATION_COPY; import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE; import static com.android.documentsui.services.FileOperationService.OPERATION_EXTRACT; import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE; import static com.android.documentsui.services.FileOperationService.OPERATION_UNKNOWN; -import static androidx.core.util.Preconditions.checkArgument; import android.content.Context; import android.net.Uri; @@ -32,6 +33,7 @@ import android.os.Message; import android.os.Messenger; import android.os.Parcel; import android.os.Parcelable; + import androidx.annotation.VisibleForTesting; import com.android.documentsui.base.DocumentStack; @@ -45,7 +47,8 @@ import java.util.List; import javax.annotation.Nullable; /** - * FileOperation describes a file operation, such as move/copy/delete etc. + * FileOperation describes a file operation, such as move/copy/delete etc. File operation currently + * supports and assumes on current user only. */ public abstract class FileOperation implements Parcelable { private final @OpType int mOpType; diff --git a/src/com/android/documentsui/services/MoveJob.java b/src/com/android/documentsui/services/MoveJob.java index cc1601bfe..184bfc8f7 100644 --- a/src/com/android/documentsui/services/MoveJob.java +++ b/src/com/android/documentsui/services/MoveJob.java @@ -23,7 +23,6 @@ import static com.android.documentsui.services.FileOperationService.OPERATION_MO import android.app.Notification; import android.app.Notification.Builder; -import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.os.Messenger; @@ -38,6 +37,7 @@ import com.android.documentsui.R; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.DocumentStack; import com.android.documentsui.base.Features; +import com.android.documentsui.base.UserId; import com.android.documentsui.clipping.UrisSupplier; import java.io.FileNotFoundException; @@ -95,9 +95,9 @@ final class MoveJob extends CopyJob { @Override public boolean setUp() { if (mSrcParentUri != null) { - final ContentResolver resolver = appContext.getContentResolver(); try { - mSrcParent = DocumentInfo.fromUri(resolver, mSrcParentUri); + mSrcParent = DocumentInfo.fromUri(appContext.getContentResolver(), mSrcParentUri, + UserId.DEFAULT_USER); } catch (FileNotFoundException e) { Log.e(TAG, "Failed to create srcParent.", e); failureCount = mResourceUris.getItemCount(); diff --git a/src/com/android/documentsui/services/ResolvedResourcesJob.java b/src/com/android/documentsui/services/ResolvedResourcesJob.java index 182a9ed35..500958978 100644 --- a/src/com/android/documentsui/services/ResolvedResourcesJob.java +++ b/src/com/android/documentsui/services/ResolvedResourcesJob.java @@ -27,6 +27,7 @@ import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.DocumentStack; import com.android.documentsui.base.Features; import com.android.documentsui.base.RootInfo; +import com.android.documentsui.base.UserId; import com.android.documentsui.clipping.UrisSupplier; import com.android.documentsui.services.FileOperationService.OpType; @@ -132,7 +133,7 @@ public abstract class ResolvedResourcesJob extends Job { DocumentInfo doc; try { - doc = DocumentInfo.fromUri(resolver, uri); + doc = DocumentInfo.fromUri(resolver, uri, UserId.DEFAULT_USER); } catch (FileNotFoundException e) { Log.e(TAG, "Failed to resolve content from Uri: " + uri + ". Skipping to next resource.", e); diff --git a/tests/common/com/android/documentsui/DocumentsProviderHelper.java b/tests/common/com/android/documentsui/DocumentsProviderHelper.java index 7111508c3..f68c9258c 100644 --- a/tests/common/com/android/documentsui/DocumentsProviderHelper.java +++ b/tests/common/com/android/documentsui/DocumentsProviderHelper.java @@ -295,7 +295,8 @@ public class DocumentsProviderHelper { Uri uri = buildChildDocumentsUri(mAuthority, documentId); List<DocumentInfo> children = new ArrayList<>(); try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) { - Cursor wrapper = new RootCursorWrapper(mAuthority, "totally-fake", cursor, maxCount); + Cursor wrapper = new RootCursorWrapper(mUserId, mAuthority, "totally-fake", cursor, + maxCount); while (wrapper.moveToNext()) { children.add(DocumentInfo.fromDirectoryCursor(wrapper)); } diff --git a/tests/common/com/android/documentsui/testing/TestDocumentsAccess.java b/tests/common/com/android/documentsui/testing/TestDocumentsAccess.java index 266a06b69..088819a05 100644 --- a/tests/common/com/android/documentsui/testing/TestDocumentsAccess.java +++ b/tests/common/com/android/documentsui/testing/TestDocumentsAccess.java @@ -26,6 +26,7 @@ import android.util.Pair; import com.android.documentsui.DocumentsAccess; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.RootInfo; +import com.android.documentsui.base.UserId; import java.util.List; @@ -50,12 +51,12 @@ public class TestDocumentsAccess implements DocumentsAccess { } @Override - public DocumentInfo getDocument(Uri uri) { + public DocumentInfo getDocument(Uri uri, UserId userId) { return nextDocument; } @Override - public List<DocumentInfo> getDocuments(String authority, List<String> docIds) { + public List<DocumentInfo> getDocuments(UserId userId, String authority, List<String> docIds) { return nextDocuments; } @@ -74,7 +75,7 @@ public class TestDocumentsAccess implements DocumentsAccess { } @Override - public DocumentInfo getArchiveDocument(Uri uri) { + public DocumentInfo getArchiveDocument(Uri uri, UserId userId) { return nextDocument; } @@ -84,7 +85,7 @@ public class TestDocumentsAccess implements DocumentsAccess { } @Override - public Path findDocumentPath(Uri docUri) throws RemoteException { + public Path findDocumentPath(Uri docUri, UserId userId) throws RemoteException { lastUri.accept(docUri); return nextPath; } diff --git a/tests/common/com/android/documentsui/testing/TestEnv.java b/tests/common/com/android/documentsui/testing/TestEnv.java index 06f9dd109..a75c0034c 100644 --- a/tests/common/com/android/documentsui/testing/TestEnv.java +++ b/tests/common/com/android/documentsui/testing/TestEnv.java @@ -31,6 +31,7 @@ import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.Features; import com.android.documentsui.base.RootInfo; import com.android.documentsui.base.State; +import com.android.documentsui.base.UserId; import com.android.documentsui.dirlist.TestFocusHandler; import com.android.documentsui.sorting.SortModel; import com.android.documentsui.ui.TestDialogController; @@ -79,8 +80,8 @@ public class TestEnv { this.features = features; state.sortModel = SortModel.createModel(); mExecutor = new TestScheduledExecutorService(); - model = new TestModel(authority, features); - archiveModel = new TestModel(ArchivesProvider.AUTHORITY, features); + model = new TestModel(UserId.DEFAULT_USER, authority, features); + archiveModel = new TestModel(UserId.DEFAULT_USER, ArchivesProvider.AUTHORITY, features); selectionMgr = SelectionHelpers.createTestInstance(); searchViewManager = new TestSearchViewManager(); injector = new Injector( diff --git a/tests/common/com/android/documentsui/testing/TestModel.java b/tests/common/com/android/documentsui/testing/TestModel.java index 7df7b2746..70a836923 100644 --- a/tests/common/com/android/documentsui/testing/TestModel.java +++ b/tests/common/com/android/documentsui/testing/TestModel.java @@ -25,6 +25,7 @@ import com.android.documentsui.DirectoryResult; import com.android.documentsui.Model; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.Features; +import com.android.documentsui.base.UserId; import com.android.documentsui.roots.RootCursorWrapper; import libcore.content.type.MimeMap; @@ -42,13 +43,15 @@ public class TestModel extends Model { Document.COLUMN_MIME_TYPE }; + private final UserId mUserId; private final String mAuthority; private int mLastId = 0; private Random mRand = new Random(); private MatrixCursor mCursor; - public TestModel(String authority, Features features) { + public TestModel(UserId userId, String authority, Features features) { super(features); + mUserId = userId; mAuthority = authority; reset(); } @@ -102,6 +105,7 @@ public class TestModel extends Model { public DocumentInfo createDocument(String name, String mimeType, int flags) { DocumentInfo doc = new DocumentInfo(); + doc.userId = mUserId; doc.authority = mAuthority; doc.documentId = Integer.toString(++mLastId); doc.derivedUri = DocumentsContract.buildDocumentUri(doc.authority, doc.documentId); diff --git a/tests/functional/com/android/documentsui/FileCopyUiTest.java b/tests/functional/com/android/documentsui/FileCopyUiTest.java index c616eda1f..5ca212f59 100644 --- a/tests/functional/com/android/documentsui/FileCopyUiTest.java +++ b/tests/functional/com/android/documentsui/FileCopyUiTest.java @@ -457,7 +457,7 @@ public class FileCopyUiTest extends ActivityTest<FilesActivity> { if (info == null) { ContentResolver cr = context.getContentResolver(); Uri uri = mStorageDocsHelper.createFolder(mPrimaryRoot.documentId, "Download"); - info = DocumentInfo.fromUri(cr, uri); + info = DocumentInfo.fromUri(cr, uri, userId); } assertTrue(info != null && info.isDirectory()); diff --git a/tests/functional/com/android/documentsui/InspectorUiTest.java b/tests/functional/com/android/documentsui/InspectorUiTest.java index de52250aa..0068a0aea 100644 --- a/tests/functional/com/android/documentsui/InspectorUiTest.java +++ b/tests/functional/com/android/documentsui/InspectorUiTest.java @@ -21,7 +21,6 @@ import android.provider.DocumentsContract; import androidx.test.filters.LargeTest; -import com.android.documentsui.files.FilesActivity; import com.android.documentsui.inspector.InspectorActivity; @LargeTest @@ -43,10 +42,9 @@ public class InspectorUiTest extends ActivityTest<InspectorActivity> { if (!features.isInspectorEnabled()) { return; } - final Intent intent = new Intent(context, FilesActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); Uri uri = DocumentsContract.buildDocumentUri(InspectorProvider.AUTHORITY, TEST_DOC_NAME); - intent.setData(uri); + final Intent intent = InspectorActivity.createIntent(context, uri, userId); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); setActivityIntent(intent); getActivity(); } diff --git a/tests/unit/com/android/documentsui/FocusManagerTest.java b/tests/unit/com/android/documentsui/FocusManagerTest.java index 15f703d54..67e70005e 100644 --- a/tests/unit/com/android/documentsui/FocusManagerTest.java +++ b/tests/unit/com/android/documentsui/FocusManagerTest.java @@ -22,6 +22,7 @@ import android.test.suitebuilder.annotation.SmallTest; import androidx.recyclerview.selection.SelectionTracker; import androidx.recyclerview.widget.RecyclerView; +import com.android.documentsui.base.UserId; import com.android.documentsui.dirlist.TestData; import com.android.documentsui.testing.TestFeatures; import com.android.documentsui.testing.TestGridLayoutManager; @@ -53,8 +54,8 @@ public class FocusManagerTest extends AndroidTestCase { mSelectionMgr = SelectionHelpers.createTestInstance(ITEMS); mFeatures = new TestFeatures(); - mManager = new FocusManager(mFeatures, mSelectionMgr, null, null, 0) - .reset(mView, new TestModel(TEST_AUTHORITY, mFeatures)); + mManager = new FocusManager(mFeatures, mSelectionMgr, null, null, 0).reset(mView, + new TestModel(UserId.DEFAULT_USER, TEST_AUTHORITY, mFeatures)); } public void testFocus() { @@ -75,7 +76,7 @@ public class FocusManagerTest extends AndroidTestCase { mView = TestRecyclerView.create(new ArrayList<>()); mManager = new FocusManager( mFeatures, SelectionHelpers.createTestInstance(), null, null, 0) - .reset(mView, new TestModel(TEST_AUTHORITY, mFeatures)); + .reset(mView, new TestModel(UserId.DEFAULT_USER, TEST_AUTHORITY, mFeatures)); assertFalse(mManager.focusDirectoryList()); } diff --git a/tests/unit/com/android/documentsui/base/DocumentInfoTest.java b/tests/unit/com/android/documentsui/base/DocumentInfoTest.java index 6faa58ae3..e6c2e07e8 100644 --- a/tests/unit/com/android/documentsui/base/DocumentInfoTest.java +++ b/tests/unit/com/android/documentsui/base/DocumentInfoTest.java @@ -64,6 +64,7 @@ public class DocumentInfoTest extends AndroidTestCase { private static DocumentInfo createDocInfo(String authority, String docId, String mimeType) { DocumentInfo doc = new DocumentInfo(); + doc.userId = UserId.DEFAULT_USER; doc.authority = authority; doc.documentId = docId; doc.mimeType = mimeType; @@ -97,6 +98,14 @@ public class DocumentInfoTest extends AndroidTestCase { } @Test + public void testNotEquals_differentUser() throws Exception { + DocumentInfo documentInfo1 = createDocInfo("authority.a", "doc.1", "text/plain"); + DocumentInfo documentInfo2 = createDocInfo("authority.a", "doc.1", "text/plain"); + documentInfo1.userId = UserId.of(documentInfo2.userId.getIdentifier() + 1); + assertFalse(documentInfo1.equals(documentInfo2)); + } + + @Test public void testNotEquals_differentAuthority() throws Exception { assertFalse(TEST_DOC.equals(createDocInfo("authority.b", "doc.1", "text/plain"))); } diff --git a/tests/unit/com/android/documentsui/base/UserIdTest.java b/tests/unit/com/android/documentsui/base/UserIdTest.java index 0f5763760..2f9f7b24a 100644 --- a/tests/unit/com/android/documentsui/base/UserIdTest.java +++ b/tests/unit/com/android/documentsui/base/UserIdTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,6 +96,18 @@ public class UserIdTest extends AndroidTestCase { } @Test + public void testOf_UserIdUserHandleIdentifier() { + UserHandle userHandle = UserHandle.of(10); + assertEquals(UserId.of(userHandle), UserId.of(userHandle.getIdentifier())); + } + + @Test + public void testOf_UserIdRecreatingShouldEqual() { + UserId userId = UserId.of(3); + assertEquals(userId, UserId.of(userId.getIdentifier())); + } + + @Test public void testRead_success() throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); DataOutputStream o = new DataOutputStream(output); diff --git a/tests/unit/com/android/documentsui/inspector/DocumentLoaderTest.java b/tests/unit/com/android/documentsui/inspector/DocumentLoaderTest.java index 9d9d63b2d..c453ed2b6 100644 --- a/tests/unit/com/android/documentsui/inspector/DocumentLoaderTest.java +++ b/tests/unit/com/android/documentsui/inspector/DocumentLoaderTest.java @@ -31,6 +31,7 @@ import androidx.test.rule.provider.ProviderTestRule; import com.android.documentsui.InspectorProvider; import com.android.documentsui.base.DocumentInfo; +import com.android.documentsui.base.UserId; import com.android.documentsui.inspector.InspectorController.DataSupplier; import com.android.documentsui.testing.LatchedConsumer; import com.android.documentsui.testing.TestSupportLoaderManager; @@ -54,6 +55,7 @@ public class DocumentLoaderTest extends TestCase { private static final String NOT_DIRECTORY = "OpenInProviderTest"; private Context mContext; + private UserId mUserId; private TestSupportLoaderManager mLoaderManager; private DataSupplier mLoader; private ContentResolver mResolver; @@ -67,6 +69,7 @@ public class DocumentLoaderTest extends TestCase { super.setUp(); mContext = prepareContentResolverSource(); + mUserId = UserId.DEFAULT_USER; mResolver = mContext.getContentResolver(); mLoaderManager = new TestSupportLoaderManager(); mLoader = new RuntimeDataSupplier(mContext, mLoaderManager); @@ -99,7 +102,7 @@ public class DocumentLoaderTest extends TestCase { Uri validUri = DocumentsContract.buildDocumentUri( InspectorProvider.AUTHORITY, TEST_DOC_NAME); LatchedConsumer<DocumentInfo> consumer = new LatchedConsumer<>(1); - mLoader.loadDocInfo(validUri, consumer); + mLoader.loadDocInfo(validUri, mUserId, consumer); // this is a test double that requires explicitly loading. @see TestLoaderManager mLoaderManager.getLoader(0).startLoading(); @@ -120,7 +123,7 @@ public class DocumentLoaderTest extends TestCase { public void testInvalidInput() throws Exception { Uri invalidUri = Uri.parse("content://poodles/chuckleberry/ham"); LatchedConsumer<DocumentInfo> consumer = new LatchedConsumer<>(1); - mLoader.loadDocInfo(invalidUri, consumer); + mLoader.loadDocInfo(invalidUri, mUserId, consumer); // this is a test double that requires explicitly loading. @see TestLoaderManager mLoaderManager.getLoader(0).startLoading(); @@ -136,7 +139,7 @@ public class DocumentLoaderTest extends TestCase { LatchedConsumer<DocumentInfo> consumer = new LatchedConsumer<>(1); try { - mLoader.loadDocInfo(invalidUri, consumer); + mLoader.loadDocInfo(invalidUri, mUserId, consumer); // this is a test double that requires explicitly loading. @see TestLoaderManager mLoaderManager.getLoader(0).startLoading(); @@ -149,7 +152,7 @@ public class DocumentLoaderTest extends TestCase { Uri dirUri = DocumentsContract.buildDocumentUri( InspectorProvider.AUTHORITY, DIR_TOP); - DocumentInfo info = DocumentInfo.fromUri(mResolver, dirUri); + DocumentInfo info = DocumentInfo.fromUri(mResolver, dirUri, mUserId); LatchedConsumer<Integer> consumer = new LatchedConsumer<>(1); mLoader.loadDirCount(info, consumer); @@ -164,7 +167,7 @@ public class DocumentLoaderTest extends TestCase { Uri uri = DocumentsContract.buildDocumentUri( InspectorProvider.AUTHORITY, NOT_DIRECTORY); - DocumentInfo info = DocumentInfo.fromUri(mResolver, uri); + DocumentInfo info = DocumentInfo.fromUri(mResolver, uri, mUserId); LatchedConsumer<Integer> consumer = new LatchedConsumer<>(1); try { diff --git a/tests/unit/com/android/documentsui/inspector/InspectorControllerTest.java b/tests/unit/com/android/documentsui/inspector/InspectorControllerTest.java index 3d1aaceb2..d98f955ef 100644 --- a/tests/unit/com/android/documentsui/inspector/InspectorControllerTest.java +++ b/tests/unit/com/android/documentsui/inspector/InspectorControllerTest.java @@ -134,7 +134,7 @@ public class InspectorControllerTest { */ @Test public void testHideDebugByDefault() throws Exception { - mController.loadInfo(TestEnv.FILE_JPG.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(TestEnv.FILE_JPG.derivedUri, mUserId); // actual URI doesn't matter :) mDebugTestDouble.assertVisible(false); mDebugTestDouble.assertEmpty(); } @@ -146,7 +146,7 @@ public class InspectorControllerTest { public void testShowDebugUpdatesView() throws Exception { mShowDebug = true; recreateController(); - mController.loadInfo(TestEnv.FILE_JPG.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(TestEnv.FILE_JPG.derivedUri, mUserId); // actual URI doesn't matter :) mDebugTestDouble.assertVisible(true); mDebugTestDouble.assertNotEmpty(); } @@ -158,7 +158,7 @@ public class InspectorControllerTest { public void testExtraTitleOverridesDisplayName() throws Exception { mTitle = "hammy!"; recreateController(); - mController.loadInfo(TestEnv.FILE_JPG.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(TestEnv.FILE_JPG.derivedUri, mUserId); // actual URI doesn't matter :) mDetailsTestDouble.assertTitle("hammy!"); } @@ -191,7 +191,7 @@ public class InspectorControllerTest { */ @Test public void testUpdateViewWithValidInput() throws Exception { - mController.loadInfo(TestEnv.FILE_JPG.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(TestEnv.FILE_JPG.derivedUri, mUserId); // actual URI doesn't matter :) mHeaderTestDouble.assertCalled(); mDetailsTestDouble.assertCalled(); } @@ -209,7 +209,7 @@ public class InspectorControllerTest { DocumentsContract.buildDocumentUri(InspectorProvider.AUTHORITY, OPEN_IN_PROVIDER_DOC); doc.flags = doc.flags | Document.FLAG_SUPPORTS_SETTINGS; mDataSupplier.mDoc = doc; - mController.loadInfo(doc.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(doc.derivedUri, mUserId); // actual URI doesn't matter :) assertTrue(mShowInProvider.becameVisible); } @@ -220,7 +220,7 @@ public class InspectorControllerTest { */ @Test public void testShowInProvider_invisible() throws Exception { - mController.loadInfo(TestEnv.FILE_JPG.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(TestEnv.FILE_JPG.derivedUri, mUserId); // actual URI doesn't matter :) assertFalse(mShowInProvider.becameVisible); } @@ -233,7 +233,7 @@ public class InspectorControllerTest { @Test public void testUpdateView_withNullValue() throws Exception { mDataSupplier.mDoc = null; - mController.loadInfo(TestEnv.FILE_JPG.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(TestEnv.FILE_JPG.derivedUri, mUserId); // actual URI doesn't matter :) mErrCallback.assertCalled(); mHeaderTestDouble.assertNotCalled(); @@ -245,7 +245,7 @@ public class InspectorControllerTest { DocumentInfo doc = TestEnv.clone(TestEnv.FILE_JPG); doc.flags |= DocumentsContract.Document.FLAG_SUPPORTS_METADATA; mDataSupplier.mDoc = doc; - mController.loadInfo(doc.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(doc.derivedUri, mUserId); // actual URI doesn't matter :) mMedia.assertGeoCallbackInstalled(); } @@ -255,14 +255,14 @@ public class InspectorControllerTest { doc.flags |= DocumentsContract.Document.FLAG_SUPPORTS_METADATA; mDataSupplier.mDoc = doc; mDataSupplier.mMetadata = null; // sorry, no results sucka! - mController.loadInfo(doc.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(doc.derivedUri, mUserId); // actual URI doesn't matter :) } @Test public void testMetadata_notDisplayedDocWithoutSupportFlag() { assert !TestEnv.FILE_JPG.isMetadataSupported(); mDataSupplier.mDoc = TestEnv.FILE_JPG; // this is the default value. For "good measure". - mController.loadInfo(TestEnv.FILE_JPG.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(TestEnv.FILE_JPG.derivedUri, mUserId); // actual URI doesn't matter :) mMedia.assertVisible(false); mMedia.assertEmpty(); } @@ -272,7 +272,7 @@ public class InspectorControllerTest { DocumentInfo doc = TestEnv.clone(TestEnv.FILE_JPG); doc.flags |= DocumentsContract.Document.FLAG_SUPPORTS_METADATA; mDataSupplier.mDoc = doc; - mController.loadInfo(doc.derivedUri); // actual URI doesn't matter :) + mController.loadInfo(doc.derivedUri, mUserId); // actual URI doesn't matter :) mMedia.mGeoClickCallback.run(); Intent geoIntent = mActivity.started; assertEquals(Intent.ACTION_VIEW, geoIntent.getAction()); @@ -415,7 +415,7 @@ public class InspectorControllerTest { private @Nullable Bundle mMetadata; @Override - public void loadDocInfo(Uri uri, Consumer<DocumentInfo> callback) { + public void loadDocInfo(Uri uri, UserId userId, Consumer<DocumentInfo> callback) { callback.accept(mDoc); } diff --git a/tests/unit/com/android/documentsui/services/AbstractJobTest.java b/tests/unit/com/android/documentsui/services/AbstractJobTest.java index db77c9ca3..b651a8777 100644 --- a/tests/unit/com/android/documentsui/services/AbstractJobTest.java +++ b/tests/unit/com/android/documentsui/services/AbstractJobTest.java @@ -98,7 +98,7 @@ public abstract class AbstractJobTest<T extends Job> extends AndroidTestCase { FileOperation createOperation(@OpType int opType, List<Uri> srcs, Uri srcParent, Uri destination) throws Exception { DocumentStack stack = - new DocumentStack(mSrcRoot, DocumentInfo.fromUri(mResolver, destination)); + new DocumentStack(mSrcRoot, DocumentInfo.fromUri(mResolver, destination, mUserId)); UrisSupplier urisSupplier = DocsProviders.createDocsProvider(srcs); FileOperation operation = new FileOperation.Builder() @@ -122,7 +122,7 @@ public abstract class AbstractJobTest<T extends Job> extends AndroidTestCase { final T createJob(@OpType int opType, List<Uri> srcs, Uri srcParent, Uri destination) throws Exception { DocumentStack stack = - new DocumentStack(mSrcRoot, DocumentInfo.fromUri(mResolver, destination)); + new DocumentStack(mSrcRoot, DocumentInfo.fromUri(mResolver, destination, mUserId)); UrisSupplier urisSupplier = DocsProviders.createDocsProvider(srcs); FileOperation operation = new FileOperation.Builder() |