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

Commit e0c12903 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Additional notification performance improvements." into rvc-dev

parents 2de79fb6 5fae035a
Loading
Loading
Loading
Loading
+86 −63
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -398,40 +399,32 @@ public final class ContentService extends IContentService.Stub {
     */
    @Override
    public void notifyChange(Uri[] uris, IContentObserver observer,
            boolean observerWantsSelfNotifications, int flags, int userHandle,
            boolean observerWantsSelfNotifications, int flags, int userId,
            int targetSdkVersion, String callingPackage) {
        final ObserverCollector collector = new ObserverCollector();
        for (Uri uri : uris) {
            notifyChange(uri, observer, observerWantsSelfNotifications, flags, userHandle,
                    targetSdkVersion, callingPackage, collector);
        }
        final long token = clearCallingIdentity();
        try {
            collector.dispatch();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    public void notifyChange(Uri uri, IContentObserver observer,
            boolean observerWantsSelfNotifications, int flags, int userHandle,
            int targetSdkVersion, String callingPackage, ObserverCollector collector) {
        if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
                + " from observer " + observer + ", flags " + Integer.toHexString(flags));

        if (uri == null) {
            throw new NullPointerException("Uri must not be null");
        if (DEBUG) {
            Slog.d(TAG, "Notifying update of " + Arrays.toString(uris) + " for user " + userId
                    + ", observer " + observer + ", flags " + Integer.toHexString(flags));
        }

        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        final int callingUserHandle = UserHandle.getCallingUserId();
        final int callingUserId = UserHandle.getCallingUserId();

        // Set of notification events that we need to dispatch
        final ObserverCollector collector = new ObserverCollector();

        userHandle = handleIncomingUser(uri, callingPid, callingUid,
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userHandle);
        // Set of content provider authorities that we've validated the caller
        // has access to, mapped to the package name hosting that provider
        final ArrayMap<Pair<String, Integer>, String> validatedProviders = new ArrayMap<>();

        for (Uri uri : uris) {
            // Validate that calling app has access to this provider
            final int resolvedUserId = handleIncomingUser(uri, callingPid, callingUid,
                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userId);
            final Pair<String, Integer> provider = Pair.create(uri.getAuthority(), resolvedUserId);
            if (!validatedProviders.containsKey(provider)) {
                final String msg = LocalServices.getService(ActivityManagerInternal.class)
                .checkContentProviderAccess(uri.getAuthority(), userHandle);
                        .checkContentProviderAccess(uri.getAuthority(), resolvedUserId);
                if (msg != null) {
                    if (targetSdkVersion >= Build.VERSION_CODES.O) {
                        throw new SecurityException(msg);
@@ -440,36 +433,59 @@ public final class ContentService extends IContentService.Stub {
                            // Sigh, we need to quietly let apps targeting older API
                            // levels notify on non-existent providers.
                        } else {
                    Log.w(TAG, "Ignoring notify for " + uri + " from " + callingUid + ": " + msg);
                    return;
                            Log.w(TAG, "Ignoring notify for " + uri + " from "
                                    + callingUid + ": " + msg);
                            continue;
                        }
                    }
                }

        // This makes it so that future permission checks will be in the context of this
        // process rather than the caller's process. We will restore this before returning.
        long identityToken = clearCallingIdentity();
        try {
                // Remember that we've validated this access
                final String packageName = getProviderPackageName(uri, resolvedUserId);
                validatedProviders.put(provider, packageName);
            }

            // No concerns raised above, so caller has access; let's collect the
            // notifications that should be dispatched
            synchronized (mRootNode) {
                mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
                        flags, userHandle, collector);
                final int segmentCount = ObserverNode.countUriSegments(uri);
                mRootNode.collectObserversLocked(uri, segmentCount, 0, observer,
                        observerWantsSelfNotifications, flags, resolvedUserId, collector);
            }
        }

        final long token = clearCallingIdentity();
        try {
            // Actually dispatch all the notifications we collected
            collector.dispatch();

            for (int i = 0; i < validatedProviders.size(); i++) {
                final String authority = validatedProviders.keyAt(i).first;
                final int resolvedUserId = validatedProviders.keyAt(i).second;
                final String packageName = validatedProviders.valueAt(i);

                // Kick off sync adapters for any authorities we touched
                if ((flags & ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
                    SyncManager syncManager = getSyncManager();
                    if (syncManager != null) {
                    syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle,
                        syncManager.scheduleLocalSync(null /* all accounts */, callingUserId,
                                callingUid,
                            uri.getAuthority(), getSyncExemptionForCaller(callingUid),
                                authority, getSyncExemptionForCaller(callingUid),
                                callingUid, callingPid, callingPackage);
                    }
                }

                // Invalidate caches for any authorities we touched
                synchronized (mCache) {
                final String providerPackageName = getProviderPackageName(uri, userHandle);
                invalidateCacheLocked(userHandle, providerPackageName, uri);
                    for (Uri uri : uris) {
                        if (Objects.equals(uri.getAuthority(), authority)) {
                            invalidateCacheLocked(resolvedUserId, packageName, uri);
                        }
                    }
                }
            }
        } finally {
            restoreCallingIdentity(identityToken);
            Binder.restoreCallingIdentity(token);
        }
    }

@@ -1533,7 +1549,7 @@ public final class ContentService extends IContentService.Stub {
            }
        }

        private String getUriSegment(Uri uri, int index) {
        public static String getUriSegment(Uri uri, int index) {
            if (uri != null) {
                if (index == 0) {
                    return uri.getAuthority();
@@ -1545,7 +1561,7 @@ public final class ContentService extends IContentService.Stub {
            }
        }

        private int countUriSegments(Uri uri) {
        public static int countUriSegments(Uri uri) {
            if (uri == null) {
                return 0;
            }
@@ -1669,14 +1685,21 @@ public final class ContentService extends IContentService.Stub {
            }
        }

        @VisibleForTesting
        public void collectObserversLocked(Uri uri, int index,
                IContentObserver observer, boolean observerWantsSelfNotifications, int flags,
                int targetUserHandle, ObserverCollector collector) {
            collectObserversLocked(uri, countUriSegments(uri), index, observer,
                    observerWantsSelfNotifications, flags, targetUserHandle, collector);
        }

        /**
         * targetUserHandle is either a hard user handle or is USER_ALL
         */
        public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
                                           boolean observerWantsSelfNotifications, int flags,
        public void collectObserversLocked(Uri uri, int segmentCount, int index,
                IContentObserver observer, boolean observerWantsSelfNotifications, int flags,
                int targetUserHandle, ObserverCollector collector) {
            String segment = null;
            int segmentCount = countUriSegments(uri);
            if (index >= segmentCount) {
                // This is the leaf node, notify all observers
                if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
@@ -1696,7 +1719,7 @@ public final class ContentService extends IContentService.Stub {
                ObserverNode node = mChildren.get(i);
                if (segment == null || node.mName.equals(segment)) {
                    // We found the child,
                    node.collectObserversLocked(uri, index + 1, observer,
                    node.collectObserversLocked(uri, segmentCount, index + 1, observer,
                            observerWantsSelfNotifications, flags, targetUserHandle, collector);
                    if (segment != null) {
                        break;