Loading src/com/android/documentsui/roots/ProvidersCache.java +99 −24 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.function.Function; Loading @@ -90,6 +91,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { // ArchivesProvider doesn't support any roots. add(ArchivesProvider.AUTHORITY); }}; private static final int FIRST_LOAD_TIMEOUT_MS = 5000; private final Context mContext; Loading @@ -111,6 +113,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { private Multimap<UserAuthority, RootInfo> mRoots = ArrayListMultimap.create(); @GuardedBy("mLock") private HashSet<UserAuthority> mStoppedAuthorities = new HashSet<>(); private final Semaphore mMultiProviderUpdateTaskSemaphore = new Semaphore(1); @GuardedBy("mObservedAuthoritiesDetails") private final Map<UserAuthority, PackageDetails> mObservedAuthoritiesDetails = new HashMap<>(); Loading Loading @@ -205,13 +208,16 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { assert (recentRoot.availableBytes == -1); } new UpdateTask(forceRefreshAll, null, callback).executeOnExecutor( new MultiProviderUpdateTask(forceRefreshAll, null, callback).executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR); } public void updatePackageAsync(UserId userId, String packageName) { new UpdateTask(false, new UserPackage(userId, packageName), /* callback= */ null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MultiProviderUpdateTask( /* forceRefreshAll= */ false, new UserPackage(userId, packageName), /* callback= */ null) .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } public void updateAuthorityAsync(UserId userId, String authority) { Loading @@ -235,7 +241,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { } /** * Block until the first {@link UpdateTask} pass has finished. * Block until the first {@link MultiProviderUpdateTask} pass has finished. * * @return {@code true} if cached roots is ready to roll, otherwise * {@code false} if we timed out while waiting. Loading @@ -243,7 +249,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { private boolean waitForFirstLoad() { boolean success = false; try { success = mFirstLoad.await(15, TimeUnit.SECONDS); success = mFirstLoad.await(FIRST_LOAD_TIMEOUT_MS, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { } if (!success) { Loading @@ -254,7 +260,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { /** * Load roots from authorities that are in stopped state. Normal * {@link UpdateTask} passes ignore stopped applications. * {@link MultiProviderUpdateTask} passes ignore stopped applications. */ private void loadStoppedAuthorities() { synchronized (mLock) { Loading @@ -266,7 +272,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { } /** * Load roots from a stopped authority. Normal {@link UpdateTask} passes * Load roots from a stopped authority. Normal {@link MultiProviderUpdateTask} passes * ignore stopped applications. */ private void loadStoppedAuthority(UserAuthority userAuthority) { Loading Loading @@ -433,7 +439,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { waitForFirstLoad(); loadStoppedAuthorities(); synchronized (mLock) { return mRoots.values(); return new HashSet<>(mRoots.values()); } } Loading Loading @@ -485,16 +491,13 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { Log.i(TAG, output.toString()); } private class UpdateTask extends AsyncTask<Void, Void, Void> { private class MultiProviderUpdateTask extends AsyncTask<Void, Void, Void> { private final boolean mForceRefreshAll; @Nullable private final UserPackage mForceRefreshUserPackage; @Nullable private final Runnable mCallback; private final Multimap<UserAuthority, RootInfo> mTaskRoots = ArrayListMultimap.create(); private final HashSet<UserAuthority> mTaskStoppedAuthorities = new HashSet<>(); /** * Create task to update roots cache. * Loading @@ -504,7 +507,9 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { * values for this specific user package should be ignored. * @param callback when non-null, it will be invoked after the task is executed. */ UpdateTask(boolean forceRefreshAll, @Nullable UserPackage forceRefreshUserPackage, MultiProviderUpdateTask( boolean forceRefreshAll, @Nullable UserPackage forceRefreshUserPackage, @Nullable Runnable callback) { mForceRefreshAll = forceRefreshAll; mForceRefreshUserPackage = forceRefreshUserPackage; Loading @@ -513,12 +518,30 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { @Override protected Void doInBackground(Void... params) { if (!mMultiProviderUpdateTaskSemaphore.tryAcquire()) { // Abort, since previous update task is still running. return null; } int previousPriority = Thread.currentThread().getPriority(); Thread.currentThread().setPriority(Thread.MAX_PRIORITY); synchronized (mLock) { mRoots.clear(); mStoppedAuthorities.clear(); } final long start = SystemClock.elapsedRealtime(); for (UserId userId : mUserIdManager.getUserIds()) { final RootInfo recents = createOrGetRecentsRoot(userId); mTaskRoots.put(new UserAuthority(recents.userId, recents.authority), recents); synchronized (mLock) { mRoots.put(new UserAuthority(recents.userId, recents.authority), recents); } } List<SingleProviderUpdateTaskInfo> taskInfos = new ArrayList<>(); for (UserId userId : mUserIdManager.getUserIds()) { final PackageManager pm = userId.getPackageManager(mContext); // Pick up provider with action string final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE); Loading @@ -526,25 +549,53 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { for (ResolveInfo info : providers) { ProviderInfo providerInfo = info.providerInfo; if (providerInfo.authority != null) { handleDocumentsProvider(providerInfo, userId); taskInfos.add(new SingleProviderUpdateTaskInfo(providerInfo, userId)); } } } if (!taskInfos.isEmpty()) { CountDownLatch updateTaskInternalCountDown = new CountDownLatch(taskInfos.size()); for (SingleProviderUpdateTaskInfo taskInfo: taskInfos) { new Thread(() -> startSingleProviderUpdateTask( taskInfo.providerInfo, taskInfo.userId, updateTaskInternalCountDown)).start(); } // Block until all SingleProviderUpdateTask threads finish executing. // Use a shorter timeout for first load since it could block picker UI. long timeoutMs = mFirstLoadDone ? 15000 : FIRST_LOAD_TIMEOUT_MS; boolean success = false; try { success = updateTaskInternalCountDown.await(timeoutMs, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { } if (!success) { Log.w(TAG, "Timeout executing update task!"); } } final long delta = SystemClock.elapsedRealtime() - start; if (VERBOSE) Log.v(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms"); int rootsCount = 0; synchronized (mLock) { rootsCount = mRoots.size(); mFirstLoadDone = true; if (mBootCompletedResult != null) { mBootCompletedResult.finish(); mBootCompletedResult = null; } mRoots = mTaskRoots; mStoppedAuthorities = mTaskStoppedAuthorities; } if (VERBOSE) { Log.v(TAG, "Update found " + rootsCount + " roots in " + delta + "ms"); } mFirstLoad.countDown(); LocalBroadcastManager.getInstance(mContext).sendBroadcast(new Intent(BROADCAST_ACTION)); mMultiProviderUpdateTaskSemaphore.release(); Thread.currentThread().setPriority(previousPriority); return null; } Loading @@ -555,6 +606,17 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { } } private void startSingleProviderUpdateTask( ProviderInfo providerInfo, UserId userId, CountDownLatch updateCountDown) { int previousPriority = Thread.currentThread().getPriority(); Thread.currentThread().setPriority(Thread.MAX_PRIORITY); handleDocumentsProvider(providerInfo, userId); updateCountDown.countDown(); Thread.currentThread().setPriority(previousPriority); } private void handleDocumentsProvider(ProviderInfo info, UserId userId) { UserAuthority userAuthority = new UserAuthority(userId, info.authority); // Ignore stopped packages for now; we might query them Loading @@ -563,16 +625,19 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { if (VERBOSE) { Log.v(TAG, "Ignoring stopped authority " + info.authority + ", user " + userId); } mTaskStoppedAuthorities.add(userAuthority); synchronized (mLock) { mStoppedAuthorities.add(userAuthority); } return; } final boolean forceRefresh = mForceRefreshAll || Objects.equals(new UserPackage(userId, info.packageName), mForceRefreshUserPackage); mTaskRoots.putAll(userAuthority, loadRootsForAuthority(userAuthority, forceRefresh)); || Objects.equals( new UserPackage(userId, info.packageName), mForceRefreshUserPackage); synchronized (mLock) { mRoots.putAll(userAuthority, loadRootsForAuthority(userAuthority, forceRefresh)); } } } private static class UserAuthority { Loading Loading @@ -611,6 +676,16 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { } } private static class SingleProviderUpdateTaskInfo { private final ProviderInfo providerInfo; private final UserId userId; SingleProviderUpdateTaskInfo(ProviderInfo providerInfo, UserId userId) { this.providerInfo = providerInfo; this.userId = userId; } } private static class PackageDetails { private String applicationName; private String packageName; Loading Loading
src/com/android/documentsui/roots/ProvidersCache.java +99 −24 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.function.Function; Loading @@ -90,6 +91,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { // ArchivesProvider doesn't support any roots. add(ArchivesProvider.AUTHORITY); }}; private static final int FIRST_LOAD_TIMEOUT_MS = 5000; private final Context mContext; Loading @@ -111,6 +113,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { private Multimap<UserAuthority, RootInfo> mRoots = ArrayListMultimap.create(); @GuardedBy("mLock") private HashSet<UserAuthority> mStoppedAuthorities = new HashSet<>(); private final Semaphore mMultiProviderUpdateTaskSemaphore = new Semaphore(1); @GuardedBy("mObservedAuthoritiesDetails") private final Map<UserAuthority, PackageDetails> mObservedAuthoritiesDetails = new HashMap<>(); Loading Loading @@ -205,13 +208,16 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { assert (recentRoot.availableBytes == -1); } new UpdateTask(forceRefreshAll, null, callback).executeOnExecutor( new MultiProviderUpdateTask(forceRefreshAll, null, callback).executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR); } public void updatePackageAsync(UserId userId, String packageName) { new UpdateTask(false, new UserPackage(userId, packageName), /* callback= */ null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MultiProviderUpdateTask( /* forceRefreshAll= */ false, new UserPackage(userId, packageName), /* callback= */ null) .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } public void updateAuthorityAsync(UserId userId, String authority) { Loading @@ -235,7 +241,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { } /** * Block until the first {@link UpdateTask} pass has finished. * Block until the first {@link MultiProviderUpdateTask} pass has finished. * * @return {@code true} if cached roots is ready to roll, otherwise * {@code false} if we timed out while waiting. Loading @@ -243,7 +249,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { private boolean waitForFirstLoad() { boolean success = false; try { success = mFirstLoad.await(15, TimeUnit.SECONDS); success = mFirstLoad.await(FIRST_LOAD_TIMEOUT_MS, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { } if (!success) { Loading @@ -254,7 +260,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { /** * Load roots from authorities that are in stopped state. Normal * {@link UpdateTask} passes ignore stopped applications. * {@link MultiProviderUpdateTask} passes ignore stopped applications. */ private void loadStoppedAuthorities() { synchronized (mLock) { Loading @@ -266,7 +272,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { } /** * Load roots from a stopped authority. Normal {@link UpdateTask} passes * Load roots from a stopped authority. Normal {@link MultiProviderUpdateTask} passes * ignore stopped applications. */ private void loadStoppedAuthority(UserAuthority userAuthority) { Loading Loading @@ -433,7 +439,7 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { waitForFirstLoad(); loadStoppedAuthorities(); synchronized (mLock) { return mRoots.values(); return new HashSet<>(mRoots.values()); } } Loading Loading @@ -485,16 +491,13 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { Log.i(TAG, output.toString()); } private class UpdateTask extends AsyncTask<Void, Void, Void> { private class MultiProviderUpdateTask extends AsyncTask<Void, Void, Void> { private final boolean mForceRefreshAll; @Nullable private final UserPackage mForceRefreshUserPackage; @Nullable private final Runnable mCallback; private final Multimap<UserAuthority, RootInfo> mTaskRoots = ArrayListMultimap.create(); private final HashSet<UserAuthority> mTaskStoppedAuthorities = new HashSet<>(); /** * Create task to update roots cache. * Loading @@ -504,7 +507,9 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { * values for this specific user package should be ignored. * @param callback when non-null, it will be invoked after the task is executed. */ UpdateTask(boolean forceRefreshAll, @Nullable UserPackage forceRefreshUserPackage, MultiProviderUpdateTask( boolean forceRefreshAll, @Nullable UserPackage forceRefreshUserPackage, @Nullable Runnable callback) { mForceRefreshAll = forceRefreshAll; mForceRefreshUserPackage = forceRefreshUserPackage; Loading @@ -513,12 +518,30 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { @Override protected Void doInBackground(Void... params) { if (!mMultiProviderUpdateTaskSemaphore.tryAcquire()) { // Abort, since previous update task is still running. return null; } int previousPriority = Thread.currentThread().getPriority(); Thread.currentThread().setPriority(Thread.MAX_PRIORITY); synchronized (mLock) { mRoots.clear(); mStoppedAuthorities.clear(); } final long start = SystemClock.elapsedRealtime(); for (UserId userId : mUserIdManager.getUserIds()) { final RootInfo recents = createOrGetRecentsRoot(userId); mTaskRoots.put(new UserAuthority(recents.userId, recents.authority), recents); synchronized (mLock) { mRoots.put(new UserAuthority(recents.userId, recents.authority), recents); } } List<SingleProviderUpdateTaskInfo> taskInfos = new ArrayList<>(); for (UserId userId : mUserIdManager.getUserIds()) { final PackageManager pm = userId.getPackageManager(mContext); // Pick up provider with action string final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE); Loading @@ -526,25 +549,53 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { for (ResolveInfo info : providers) { ProviderInfo providerInfo = info.providerInfo; if (providerInfo.authority != null) { handleDocumentsProvider(providerInfo, userId); taskInfos.add(new SingleProviderUpdateTaskInfo(providerInfo, userId)); } } } if (!taskInfos.isEmpty()) { CountDownLatch updateTaskInternalCountDown = new CountDownLatch(taskInfos.size()); for (SingleProviderUpdateTaskInfo taskInfo: taskInfos) { new Thread(() -> startSingleProviderUpdateTask( taskInfo.providerInfo, taskInfo.userId, updateTaskInternalCountDown)).start(); } // Block until all SingleProviderUpdateTask threads finish executing. // Use a shorter timeout for first load since it could block picker UI. long timeoutMs = mFirstLoadDone ? 15000 : FIRST_LOAD_TIMEOUT_MS; boolean success = false; try { success = updateTaskInternalCountDown.await(timeoutMs, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { } if (!success) { Log.w(TAG, "Timeout executing update task!"); } } final long delta = SystemClock.elapsedRealtime() - start; if (VERBOSE) Log.v(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms"); int rootsCount = 0; synchronized (mLock) { rootsCount = mRoots.size(); mFirstLoadDone = true; if (mBootCompletedResult != null) { mBootCompletedResult.finish(); mBootCompletedResult = null; } mRoots = mTaskRoots; mStoppedAuthorities = mTaskStoppedAuthorities; } if (VERBOSE) { Log.v(TAG, "Update found " + rootsCount + " roots in " + delta + "ms"); } mFirstLoad.countDown(); LocalBroadcastManager.getInstance(mContext).sendBroadcast(new Intent(BROADCAST_ACTION)); mMultiProviderUpdateTaskSemaphore.release(); Thread.currentThread().setPriority(previousPriority); return null; } Loading @@ -555,6 +606,17 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { } } private void startSingleProviderUpdateTask( ProviderInfo providerInfo, UserId userId, CountDownLatch updateCountDown) { int previousPriority = Thread.currentThread().getPriority(); Thread.currentThread().setPriority(Thread.MAX_PRIORITY); handleDocumentsProvider(providerInfo, userId); updateCountDown.countDown(); Thread.currentThread().setPriority(previousPriority); } private void handleDocumentsProvider(ProviderInfo info, UserId userId) { UserAuthority userAuthority = new UserAuthority(userId, info.authority); // Ignore stopped packages for now; we might query them Loading @@ -563,16 +625,19 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { if (VERBOSE) { Log.v(TAG, "Ignoring stopped authority " + info.authority + ", user " + userId); } mTaskStoppedAuthorities.add(userAuthority); synchronized (mLock) { mStoppedAuthorities.add(userAuthority); } return; } final boolean forceRefresh = mForceRefreshAll || Objects.equals(new UserPackage(userId, info.packageName), mForceRefreshUserPackage); mTaskRoots.putAll(userAuthority, loadRootsForAuthority(userAuthority, forceRefresh)); || Objects.equals( new UserPackage(userId, info.packageName), mForceRefreshUserPackage); synchronized (mLock) { mRoots.putAll(userAuthority, loadRootsForAuthority(userAuthority, forceRefresh)); } } } private static class UserAuthority { Loading Loading @@ -611,6 +676,16 @@ public class ProvidersCache implements ProvidersAccess, LookupApplicationName { } } private static class SingleProviderUpdateTaskInfo { private final ProviderInfo providerInfo; private final UserId userId; SingleProviderUpdateTaskInfo(ProviderInfo providerInfo, UserId userId) { this.providerInfo = providerInfo; this.userId = userId; } } private static class PackageDetails { private String applicationName; private String packageName; Loading