blob: 6856129cb530e2d5cfd8e6932c6097625a51e111 [file] [log] [blame]
Jeff Sharkey76112212013-08-06 11:26:10 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.documentsui;
18
Steve McKay459bc2b2015-09-16 15:07:31 -070019import static com.android.documentsui.Shared.DEBUG;
Steve McKayf8a5e082015-09-23 17:21:40 -070020import static com.android.documentsui.Shared.TAG;
Jeff Sharkey76112212013-08-06 11:26:10 -070021
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -070022import android.content.ContentProviderClient;
23import android.content.ContentResolver;
Jeff Sharkey76112212013-08-06 11:26:10 -070024import android.content.Context;
Jeff Sharkey85c273d2013-10-07 10:16:12 -070025import android.content.Intent;
Jeff Sharkey8b997042013-09-19 15:25:56 -070026import android.content.pm.ApplicationInfo;
Jeff Sharkey76112212013-08-06 11:26:10 -070027import android.content.pm.PackageManager;
28import android.content.pm.ProviderInfo;
Jeff Sharkey85c273d2013-10-07 10:16:12 -070029import android.content.pm.ResolveInfo;
Jeff Sharkey8b997042013-09-19 15:25:56 -070030import android.database.ContentObserver;
Jeff Sharkey724deeb2013-08-31 15:02:20 -070031import android.database.Cursor;
Jeff Sharkey76112212013-08-06 11:26:10 -070032import android.net.Uri;
Jeff Sharkey8b997042013-09-19 15:25:56 -070033import android.os.AsyncTask;
34import android.os.Handler;
35import android.os.SystemClock;
Jeff Sharkey76112212013-08-06 11:26:10 -070036import android.provider.DocumentsContract;
Jeff Sharkey724deeb2013-08-31 15:02:20 -070037import android.provider.DocumentsContract.Root;
Steve McKayf8a5e082015-09-23 17:21:40 -070038import android.support.annotation.VisibleForTesting;
Jeff Sharkey76112212013-08-06 11:26:10 -070039import android.util.Log;
Jeff Sharkey76112212013-08-06 11:26:10 -070040
Jeff Sharkey724deeb2013-08-31 15:02:20 -070041import com.android.documentsui.model.RootInfo;
Jeff Sharkey76112212013-08-06 11:26:10 -070042import com.android.internal.annotations.GuardedBy;
Steve McKayfefcd702015-08-20 16:19:38 +000043
Jeff Sharkey8b997042013-09-19 15:25:56 -070044import com.google.common.collect.ArrayListMultimap;
45import com.google.common.collect.Multimap;
Jeff Sharkey76112212013-08-06 11:26:10 -070046
Jeff Sharkey724deeb2013-08-31 15:02:20 -070047import libcore.io.IoUtils;
48
Steve McKayfefcd702015-08-20 16:19:38 +000049import java.util.ArrayList;
Jeff Sharkey8b997042013-09-19 15:25:56 -070050import java.util.Collection;
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +090051import java.util.Collections;
Jeff Sharkey8b997042013-09-19 15:25:56 -070052import java.util.HashSet;
Jeff Sharkey76112212013-08-06 11:26:10 -070053import java.util.List;
Kenny Root16d85982013-12-13 12:00:26 -080054import java.util.Objects;
Jeff Sharkey8b997042013-09-19 15:25:56 -070055import java.util.concurrent.CountDownLatch;
56import java.util.concurrent.TimeUnit;
Jeff Sharkey76112212013-08-06 11:26:10 -070057
58/**
59 * Cache of known storage backends and their roots.
60 */
61public class RootsCache {
Jeff Sharkey59168772013-10-23 09:59:06 -070062 public static final Uri sNotificationUri = Uri.parse(
63 "content://com.android.documentsui.roots/");
Jeff Sharkey76112212013-08-06 11:26:10 -070064
Jeff Sharkey873daa32013-08-18 17:38:20 -070065 private final Context mContext;
Jeff Sharkey8b997042013-09-19 15:25:56 -070066 private final ContentObserver mObserver;
Daichi Hirono3067d0d2015-12-25 11:08:42 +090067 private OnCacheUpdateListener mCacheUpdateListener;
Jeff Sharkey76112212013-08-06 11:26:10 -070068
Steve McKay323dffb2016-02-17 18:25:47 -080069 private final RootInfo mRecentsRoot;
Jeff Sharkey76112212013-08-06 11:26:10 -070070
Jeff Sharkey8b997042013-09-19 15:25:56 -070071 private final Object mLock = new Object();
72 private final CountDownLatch mFirstLoad = new CountDownLatch(1);
73
74 @GuardedBy("mLock")
75 private Multimap<String, RootInfo> mRoots = ArrayListMultimap.create();
76 @GuardedBy("mLock")
Steve McKayfefcd702015-08-20 16:19:38 +000077 private HashSet<String> mStoppedAuthorities = new HashSet<>();
Jeff Sharkey8b997042013-09-19 15:25:56 -070078
79 @GuardedBy("mObservedAuthorities")
Steve McKayfefcd702015-08-20 16:19:38 +000080 private final HashSet<String> mObservedAuthorities = new HashSet<>();
Jeff Sharkey873daa32013-08-18 17:38:20 -070081
82 public RootsCache(Context context) {
83 mContext = context;
Jeff Sharkey8b997042013-09-19 15:25:56 -070084 mObserver = new RootsChangedObserver();
Steve McKay323dffb2016-02-17 18:25:47 -080085
86 // Create a new anonymous "Recents" RootInfo. It's a faker.
87 mRecentsRoot = new RootInfo() {{
Steve McKay55c00e72016-02-18 15:32:16 -080088 // Special root for recents
89 derivedIcon = R.drawable.ic_root_recent;
90 derivedType = RootInfo.TYPE_RECENTS;
Steve McKay62d33a72016-02-26 15:30:19 -080091 flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD
92 | Root.FLAG_SUPPORTS_CREATE;
Steve McKay55c00e72016-02-18 15:32:16 -080093 title = mContext.getString(R.string.root_recent);
94 availableBytes = -1;
95 }};
Jeff Sharkey8b997042013-09-19 15:25:56 -070096 }
97
98 private class RootsChangedObserver extends ContentObserver {
99 public RootsChangedObserver() {
100 super(new Handler());
101 }
102
103 @Override
104 public void onChange(boolean selfChange, Uri uri) {
Steve McKay459bc2b2015-09-16 15:07:31 -0700105 if (DEBUG) Log.d(TAG, "Updating roots due to change at " + uri);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700106 updateAuthorityAsync(uri.getAuthority());
107 }
Jeff Sharkey873daa32013-08-18 17:38:20 -0700108 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700109
Daichi Hirono3067d0d2015-12-25 11:08:42 +0900110 static interface OnCacheUpdateListener {
111 void onCacheUpdate();
112 }
113
Jeff Sharkey76112212013-08-06 11:26:10 -0700114 /**
115 * Gather roots from all known storage providers.
116 */
Jeff Sharkey8b997042013-09-19 15:25:56 -0700117 public void updateAsync() {
Steve McKay8d8a0a52016-03-01 12:57:44 -0800118
119 // NOTE: This method is called when the UI language changes.
120 // For that reason we upadte our RecentsRoot to reflect
121 // the current language.
122 mRecentsRoot.title = mContext.getString(R.string.root_recent);
123
124 // Nothing else about the root should ever change.
Steve McKay0af8afd2016-02-25 13:34:03 -0800125 assert(mRecentsRoot.authority == null);
126 assert(mRecentsRoot.rootId == null);
127 assert(mRecentsRoot.derivedIcon == R.drawable.ic_root_recent);
128 assert(mRecentsRoot.derivedType == RootInfo.TYPE_RECENTS);
129 assert(mRecentsRoot.flags == (Root.FLAG_LOCAL_ONLY
130 | Root.FLAG_SUPPORTS_IS_CHILD
131 | Root.FLAG_SUPPORTS_CREATE));
Steve McKay0af8afd2016-02-25 13:34:03 -0800132 assert(mRecentsRoot.availableBytes == -1);
133
Jeff Sharkey8b997042013-09-19 15:25:56 -0700134 new UpdateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
135 }
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700136
Jeff Sharkey8b997042013-09-19 15:25:56 -0700137 /**
138 * Gather roots from storage providers belonging to given package name.
139 */
140 public void updatePackageAsync(String packageName) {
Jeff Sharkey8b997042013-09-19 15:25:56 -0700141 new UpdateTask(packageName).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
142 }
143
144 /**
145 * Gather roots from storage providers belonging to given authority.
146 */
147 public void updateAuthorityAsync(String authority) {
148 final ProviderInfo info = mContext.getPackageManager().resolveContentProvider(authority, 0);
149 if (info != null) {
150 updatePackageAsync(info.packageName);
151 }
152 }
153
154 private void waitForFirstLoad() {
155 boolean success = false;
156 try {
157 success = mFirstLoad.await(15, TimeUnit.SECONDS);
158 } catch (InterruptedException e) {
159 }
160 if (!success) {
161 Log.w(TAG, "Timeout waiting for first update");
162 }
163 }
164
165 /**
166 * Load roots from authorities that are in stopped state. Normal
167 * {@link UpdateTask} passes ignore stopped applications.
168 */
169 private void loadStoppedAuthorities() {
170 final ContentResolver resolver = mContext.getContentResolver();
171 synchronized (mLock) {
172 for (String authority : mStoppedAuthorities) {
Steve McKay459bc2b2015-09-16 15:07:31 -0700173 if (DEBUG) Log.d(TAG, "Loading stopped authority " + authority);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700174 mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
175 }
176 mStoppedAuthorities.clear();
177 }
178 }
179
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900180 /**
181 * Load roots from a stopped authority. Normal {@link UpdateTask} passes
182 * ignore stopped applications.
183 */
184 private void loadStoppedAuthority(String authority) {
185 final ContentResolver resolver = mContext.getContentResolver();
186 synchronized (mLock) {
Tomasz Mikolajewski9daa59f2016-02-03 15:18:22 +0900187 if (!mStoppedAuthorities.contains(authority)) {
188 return;
189 }
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900190 if (DEBUG) {
191 Log.d(TAG, "Loading stopped authority " + authority);
192 }
193 mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
194 mStoppedAuthorities.remove(authority);
195 }
196 }
197
Jeff Sharkey8b997042013-09-19 15:25:56 -0700198 private class UpdateTask extends AsyncTask<Void, Void, Void> {
199 private final String mFilterPackage;
200
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700201 private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create();
Steve McKayfefcd702015-08-20 16:19:38 +0000202 private final HashSet<String> mTaskStoppedAuthorities = new HashSet<>();
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700203
Jeff Sharkey8b997042013-09-19 15:25:56 -0700204 /**
205 * Update all roots.
206 */
207 public UpdateTask() {
208 this(null);
Jeff Sharkey76112212013-08-06 11:26:10 -0700209 }
210
Jeff Sharkey8b997042013-09-19 15:25:56 -0700211 /**
212 * Only update roots belonging to given package name. Other roots will
213 * be copied from cached {@link #mRoots} values.
214 */
215 public UpdateTask(String filterPackage) {
216 mFilterPackage = filterPackage;
217 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700218
Jeff Sharkey8b997042013-09-19 15:25:56 -0700219 @Override
220 protected Void doInBackground(Void... params) {
221 final long start = SystemClock.elapsedRealtime();
222
Jeff Sharkeyca494a72014-09-10 11:23:15 -0700223 if (mFilterPackage != null) {
224 // Need at least first load, since we're going to be using
225 // previously cached values for non-matching packages.
226 waitForFirstLoad();
227 }
228
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700229 mTaskRoots.put(mRecentsRoot.authority, mRecentsRoot);
Jeff Sharkey40457802013-09-21 13:57:33 -0700230
Jeff Sharkey8b997042013-09-19 15:25:56 -0700231 final ContentResolver resolver = mContext.getContentResolver();
232 final PackageManager pm = mContext.getPackageManager();
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700233
234 // Pick up provider with action string
235 final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
236 final List<ResolveInfo> providers = pm.queryIntentContentProviders(intent, 0);
237 for (ResolveInfo info : providers) {
238 handleDocumentsProvider(info.providerInfo);
239 }
240
Jeff Sharkey8b997042013-09-19 15:25:56 -0700241 final long delta = SystemClock.elapsedRealtime() - start;
Steve McKay459bc2b2015-09-16 15:07:31 -0700242 if (DEBUG)
243 Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
Jeff Sharkey8b997042013-09-19 15:25:56 -0700244 synchronized (mLock) {
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700245 mRoots = mTaskRoots;
246 mStoppedAuthorities = mTaskStoppedAuthorities;
Jeff Sharkey8b997042013-09-19 15:25:56 -0700247 }
248 mFirstLoad.countDown();
Jeff Sharkey59168772013-10-23 09:59:06 -0700249 resolver.notifyChange(sNotificationUri, null, false);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700250 return null;
251 }
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700252
Daichi Hirono3067d0d2015-12-25 11:08:42 +0900253 @Override
254 protected void onPostExecute(Void result) {
255 if (mCacheUpdateListener != null) {
256 mCacheUpdateListener.onCacheUpdate();
257 }
258 }
259
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700260 private void handleDocumentsProvider(ProviderInfo info) {
261 // Ignore stopped packages for now; we might query them
262 // later during UI interaction.
263 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
Steve McKay459bc2b2015-09-16 15:07:31 -0700264 if (DEBUG) Log.d(TAG, "Ignoring stopped authority " + info.authority);
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700265 mTaskStoppedAuthorities.add(info.authority);
266 return;
267 }
268
269 // Try using cached roots if filtering
270 boolean cacheHit = false;
271 if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) {
272 synchronized (mLock) {
273 if (mTaskRoots.putAll(info.authority, mRoots.get(info.authority))) {
Steve McKay459bc2b2015-09-16 15:07:31 -0700274 if (DEBUG) Log.d(TAG, "Used cached roots for " + info.authority);
Jeff Sharkey85c273d2013-10-07 10:16:12 -0700275 cacheHit = true;
276 }
277 }
278 }
279
280 // Cache miss, or loading everything
281 if (!cacheHit) {
282 mTaskRoots.putAll(info.authority,
283 loadRootsForAuthority(mContext.getContentResolver(), info.authority));
284 }
285 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700286 }
287
Jeff Sharkey8b997042013-09-19 15:25:56 -0700288 /**
289 * Bring up requested provider and query for all active roots.
290 */
291 private Collection<RootInfo> loadRootsForAuthority(ContentResolver resolver, String authority) {
Steve McKay459bc2b2015-09-16 15:07:31 -0700292 if (DEBUG) Log.d(TAG, "Loading roots for " + authority);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700293
294 synchronized (mObservedAuthorities) {
295 if (mObservedAuthorities.add(authority)) {
296 // Watch for any future updates
297 final Uri rootsUri = DocumentsContract.buildRootsUri(authority);
298 mContext.getContentResolver().registerContentObserver(rootsUri, true, mObserver);
299 }
300 }
301
Steve McKayfefcd702015-08-20 16:19:38 +0000302 final List<RootInfo> roots = new ArrayList<>();
Jeff Sharkey8b997042013-09-19 15:25:56 -0700303 final Uri rootsUri = DocumentsContract.buildRootsUri(authority);
Jeff Sharkey3fd11772013-09-30 14:26:27 -0700304
305 ContentProviderClient client = null;
Jeff Sharkey8b997042013-09-19 15:25:56 -0700306 Cursor cursor = null;
307 try {
Jeff Sharkey3fd11772013-09-30 14:26:27 -0700308 client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700309 cursor = client.query(rootsUri, null, null, null, null);
310 while (cursor.moveToNext()) {
311 final RootInfo root = RootInfo.fromRootsCursor(authority, cursor);
312 roots.add(root);
313 }
314 } catch (Exception e) {
315 Log.w(TAG, "Failed to load some roots from " + authority + ": " + e);
316 } finally {
317 IoUtils.closeQuietly(cursor);
Jeff Sharkey3fd11772013-09-30 14:26:27 -0700318 ContentProviderClient.releaseQuietly(client);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700319 }
320 return roots;
321 }
322
323 /**
324 * Return the requested {@link RootInfo}, but only loading the roots for the
325 * requested authority. This is useful when we want to load fast without
326 * waiting for all the other roots to come back.
327 */
328 public RootInfo getRootOneshot(String authority, String rootId) {
329 synchronized (mLock) {
330 RootInfo root = getRootLocked(authority, rootId);
331 if (root == null) {
332 mRoots.putAll(
333 authority, loadRootsForAuthority(mContext.getContentResolver(), authority));
334 root = getRootLocked(authority, rootId);
335 }
336 return root;
337 }
338 }
339
340 public RootInfo getRootBlocking(String authority, String rootId) {
341 waitForFirstLoad();
342 loadStoppedAuthorities();
343 synchronized (mLock) {
344 return getRootLocked(authority, rootId);
345 }
346 }
347
348 private RootInfo getRootLocked(String authority, String rootId) {
349 for (RootInfo root : mRoots.get(authority)) {
Kenny Root16d85982013-12-13 12:00:26 -0800350 if (Objects.equals(root.rootId, rootId)) {
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700351 return root;
352 }
353 }
354 return null;
Jeff Sharkey76112212013-08-06 11:26:10 -0700355 }
356
Jeff Sharkey8b997042013-09-19 15:25:56 -0700357 public boolean isIconUniqueBlocking(RootInfo root) {
358 waitForFirstLoad();
359 loadStoppedAuthorities();
360 synchronized (mLock) {
361 final int rootIcon = root.derivedIcon != 0 ? root.derivedIcon : root.icon;
362 for (RootInfo test : mRoots.get(root.authority)) {
Kenny Root16d85982013-12-13 12:00:26 -0800363 if (Objects.equals(test.rootId, root.rootId)) {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700364 continue;
365 }
Jeff Sharkey9656a532013-09-13 13:42:19 -0700366 final int testIcon = test.derivedIcon != 0 ? test.derivedIcon : test.icon;
367 if (testIcon == rootIcon) {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700368 return false;
369 }
370 }
Jeff Sharkey8b997042013-09-19 15:25:56 -0700371 return true;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700372 }
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700373 }
374
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700375 public RootInfo getRecentsRoot() {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700376 return mRecentsRoot;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700377 }
378
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700379 public boolean isRecentsRoot(RootInfo root) {
Steve McKay323dffb2016-02-17 18:25:47 -0800380 return mRecentsRoot.equals(root);
Jeff Sharkey76112212013-08-06 11:26:10 -0700381 }
382
Jeff Sharkey8b997042013-09-19 15:25:56 -0700383 public Collection<RootInfo> getRootsBlocking() {
384 waitForFirstLoad();
385 loadStoppedAuthorities();
386 synchronized (mLock) {
387 return mRoots.values();
388 }
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700389 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700390
Jeff Sharkey8b997042013-09-19 15:25:56 -0700391 public Collection<RootInfo> getMatchingRootsBlocking(State state) {
392 waitForFirstLoad();
393 loadStoppedAuthorities();
394 synchronized (mLock) {
395 return getMatchingRoots(mRoots.values(), state);
396 }
Jeff Sharkey06c41872013-09-06 10:43:45 -0700397 }
398
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900399 /**
400 * Returns a list of roots for the specified authority. If not found, then
401 * an empty list is returned.
402 */
403 public Collection<RootInfo> getRootsForAuthorityBlocking(String authority) {
404 waitForFirstLoad();
405 loadStoppedAuthority(authority);
406 synchronized (mLock) {
407 final Collection<RootInfo> roots = mRoots.get(authority);
408 return roots != null ? roots : Collections.<RootInfo>emptyList();
409 }
410 }
411
Daichi Hirono3067d0d2015-12-25 11:08:42 +0900412 public void setOnCacheUpdateListener(OnCacheUpdateListener cacheUpdateListener) {
413 mCacheUpdateListener = cacheUpdateListener;
414 }
415
Jeff Sharkey8b997042013-09-19 15:25:56 -0700416 @VisibleForTesting
417 static List<RootInfo> getMatchingRoots(Collection<RootInfo> roots, State state) {
Steve McKayfefcd702015-08-20 16:19:38 +0000418 final List<RootInfo> matching = new ArrayList<>();
Jeff Sharkey06c41872013-09-06 10:43:45 -0700419 for (RootInfo root : roots) {
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700420 // Exclude read-only devices when creating
Steve McKay323dffb2016-02-17 18:25:47 -0800421 if (state.action == State.ACTION_CREATE && !root.supportsCreate()) continue;
422 if (state.action == State.ACTION_PICK_COPY_DESTINATION
423 && !root.supportsCreate()) continue;
Jeff Sharkey6e565ff2014-04-05 19:05:24 -0700424 // Exclude roots that don't support directory picking
Steve McKay323dffb2016-02-17 18:25:47 -0800425 if (state.action == State.ACTION_OPEN_TREE && !root.supportsChildren()) continue;
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700426 // Exclude advanced devices when not requested
Steve McKay323dffb2016-02-17 18:25:47 -0800427 if (!state.showAdvanced && root.isAdvanced()) continue;
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700428 // Exclude non-local devices when local only
Steve McKay323dffb2016-02-17 18:25:47 -0800429 if (state.localOnly && !root.isLocalOnly()) continue;
Steve McKay55c00e72016-02-18 15:32:16 -0800430 // Exclude downloads roots as it doesn't support directory creation (actually
431 // we just don't show them).
432 // TODO: Add flag to check the root supports directory creation.
433 if (state.directoryCopy && !root.isDownloads()) continue;
Steve McKay459bc2b2015-09-16 15:07:31 -0700434
435 // Only show empty roots when creating, or in browse mode.
Steve McKay323dffb2016-02-17 18:25:47 -0800436 if (root.isEmpty() && (state.action == State.ACTION_OPEN
Steve McKay7e8885b2015-09-23 16:16:32 -0700437 || state.action == State.ACTION_GET_CONTENT)) {
Steve McKay459bc2b2015-09-16 15:07:31 -0700438 if (DEBUG) Log.i(TAG, "Skipping empty root: " + root);
439 continue;
440 }
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700441
Jeff Sharkey62434fc2013-09-05 13:55:35 -0700442 // Only include roots that serve requested content
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700443 final boolean overlap =
444 MimePredicate.mimeMatches(root.derivedMimeTypes, state.acceptMimes) ||
445 MimePredicate.mimeMatches(state.acceptMimes, root.derivedMimeTypes);
Jeff Sharkey06c41872013-09-06 10:43:45 -0700446 if (!overlap) {
Jeff Sharkey62434fc2013-09-05 13:55:35 -0700447 continue;
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700448 }
449
Ben Kwa0bcdec32015-05-29 15:40:31 -0700450 // Exclude roots from the calling package.
451 if (state.excludedAuthorities.contains(root.authority)) {
Steve McKay323dffb2016-02-17 18:25:47 -0800452 if (DEBUG) Log.d(
453 TAG, "Excluding root " + root.authority + " from calling package.");
Ben Kwa0bcdec32015-05-29 15:40:31 -0700454 continue;
455 }
456
Steve McKay323dffb2016-02-17 18:25:47 -0800457 if (DEBUG) Log.d(
458 TAG, "Including root " + root + " in roots list.");
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700459 matching.add(root);
460 }
461 return matching;
462 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700463}