Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7732e1e5 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Fix RootsCache invalidation bugs.

When RootsCache is still running and it detects a provider/package
change, it needs to force reload roots.  Also fix cache invalidation
bug in ContentService to allow for Uri prefix matches.

Mark internal storage broadcasts with flag to bypass new background
checks.

Bug: 27759529
Change-Id: I9d9c6fe15d8640ff56ae7214afb3bac673682c28
parent 297017d1
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package android.util;

import libcore.util.Objects;
import java.util.Objects;

/**
 * Container to ease passing around a tuple of two objects. This object provides a sensible
@@ -52,7 +52,7 @@ public class Pair<F, S> {
            return false;
        }
        Pair<?, ?> p = (Pair<?, ?>) o;
        return Objects.equal(p.first, first) && Objects.equal(p.second, second);
        return Objects.equals(p.first, first) && Objects.equals(p.second, second);
    }

    /**
@@ -65,6 +65,11 @@ public class Pair<F, S> {
        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
    }

    @Override
    public String toString() {
        return "Pair{" + String.valueOf(first) + " " + String.valueOf(second) + "}";
    }

    /**
     * Convenience method for creating an appropriately typed pair.
     * @param a the first object in the Pair
+21 −37
Original line number Diff line number Diff line
@@ -65,8 +65,6 @@ public class RootsCache {

    private static final String TAG = "RootsCache";

    private static final boolean ENABLE_SYSTEM_CACHE = true;

    private final Context mContext;
    private final ContentObserver mObserver;
    private OnCacheUpdateListener mCacheUpdateListener;
@@ -200,7 +198,7 @@ public class RootsCache {
        synchronized (mLock) {
            for (String authority : mStoppedAuthorities) {
                if (DEBUG) Log.d(TAG, "Loading stopped authority " + authority);
                mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
                mRoots.putAll(authority, loadRootsForAuthority(resolver, authority, true));
            }
            mStoppedAuthorities.clear();
        }
@@ -219,13 +217,13 @@ public class RootsCache {
            if (DEBUG) {
                Log.d(TAG, "Loading stopped authority " + authority);
            }
            mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
            mRoots.putAll(authority, loadRootsForAuthority(resolver, authority, true));
            mStoppedAuthorities.remove(authority);
        }
    }

    private class UpdateTask extends AsyncTask<Void, Void, Void> {
        private final String mFilterPackage;
        private final String mForceRefreshPackage;

        private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create();
        private final HashSet<String> mTaskStoppedAuthorities = new HashSet<>();
@@ -238,18 +236,18 @@ public class RootsCache {
        }

        /**
         * Only update roots belonging to given package name. Other roots will
         * Force update roots belonging to given package name. Other roots will
         * be copied from cached {@link #mRoots} values.
         */
        public UpdateTask(String filterPackage) {
            mFilterPackage = filterPackage;
        public UpdateTask(String forceRefreshPackage) {
            mForceRefreshPackage = forceRefreshPackage;
        }

        @Override
        protected Void doInBackground(Void... params) {
            final long start = SystemClock.elapsedRealtime();

            if (mFilterPackage != null) {
            if (mForceRefreshPackage != null) {
                // We must have previously cached values to fill in non-matching
                // packages, so wait around for successful first load.
                if (!waitForFirstLoad()) {
@@ -302,29 +300,17 @@ public class RootsCache {
                return;
            }

            // Try using cached roots if filtering
            boolean cacheHit = false;
            if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) {
                synchronized (mLock) {
                    if (mTaskRoots.putAll(info.authority, mRoots.get(info.authority))) {
                        if (DEBUG) Log.d(TAG, "Used cached roots for " + info.authority);
                        cacheHit = true;
                    }
                }
            }

            // Cache miss, or loading everything
            if (!cacheHit) {
                mTaskRoots.putAll(info.authority,
                        loadRootsForAuthority(mContext.getContentResolver(), info.authority));
            }
            final boolean forceRefresh = Objects.equals(mForceRefreshPackage, info.packageName);
            mTaskRoots.putAll(info.authority, loadRootsForAuthority(mContext.getContentResolver(),
                    info.authority, forceRefresh));
        }
    }

    /**
     * Bring up requested provider and query for all active roots.
     */
    private Collection<RootInfo> loadRootsForAuthority(ContentResolver resolver, String authority) {
    private Collection<RootInfo> loadRootsForAuthority(ContentResolver resolver, String authority,
            boolean forceRefresh) {
        if (DEBUG) Log.d(TAG, "Loading roots for " + authority);

        synchronized (mObservedAuthorities) {
@@ -336,7 +322,7 @@ public class RootsCache {
        }

        final Uri rootsUri = DocumentsContract.buildRootsUri(authority);
        if (ENABLE_SYSTEM_CACHE) {
        if (!forceRefresh) {
            // Look for roots data that we might have cached for ourselves in the
            // long-lived system process.
            final Bundle systemCache = resolver.getCache(rootsUri);
@@ -363,14 +349,12 @@ public class RootsCache {
            ContentProviderClient.releaseQuietly(client);
        }

        if (ENABLE_SYSTEM_CACHE) {
        // Cache these freshly parsed roots over in the long-lived system
        // process, in case our process goes away. The system takes care of
        // invalidating the cache if the package or Uri changes.
        final Bundle systemCache = new Bundle();
        systemCache.putParcelableArrayList(TAG, roots);
        resolver.putCache(rootsUri, systemCache);
        }

        return roots;
    }
@@ -384,8 +368,8 @@ public class RootsCache {
        synchronized (mLock) {
            RootInfo root = getRootLocked(authority, rootId);
            if (root == null) {
                mRoots.putAll(
                        authority, loadRootsForAuthority(mContext.getContentResolver(), authority));
                mRoots.putAll(authority,
                        loadRootsForAuthority(mContext.getContentResolver(), authority, false));
                root = getRootLocked(authority, rootId);
            }
            return root;
+4 −2
Original line number Diff line number Diff line
@@ -1231,7 +1231,8 @@ class MountService extends IMountService.Stub
        }

        final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);
        intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);
        mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
@@ -1347,7 +1348,8 @@ class MountService extends IMountService.Stub
            intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
            intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
            intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
            mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
        }

+5 −6
Original line number Diff line number Diff line
@@ -26,9 +26,9 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentService;
import android.content.ISyncStatusObserver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ISyncStatusObserver;
import android.content.PeriodicSync;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
@@ -58,18 +58,15 @@ import android.util.SparseIntArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
 * {@hide}
@@ -1030,14 +1027,16 @@ public final class ContentService extends IContentService.Stub {

        if (uri != null) {
            for (int i = 0; i < packageCache.size();) {
                final Uri key = packageCache.keyAt(i).second;
                if (Objects.equals(key, uri)) {
                final Pair<String, Uri> key = packageCache.keyAt(i);
                if (key.second != null && key.second.toString().startsWith(uri.toString())) {
                    Slog.d(TAG, "Invalidating cache for key " + key);
                    packageCache.removeAt(i);
                } else {
                    i++;
                }
            }
        } else {
            Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
            packageCache.clear();
        }
    }