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

Commit 4dc70f1c authored by Jing Ji's avatar Jing Ji
Browse files

Move the waiting for the publishing of content provider to client side

...except the external provider client.

Previously the waiting is within the system_server and holds a binder
thread of the system_server. Now the waiting will be in client side
and the system_server will notify the client when the provider is
ready.

Bug: 149935749
Bug: 162450085
Test: atest CtsContentTestCases:android.content.cts
Test: atest FrameworksCoreTests:android.content
Test: atest ActivityManagerPerfTests:ContentProviderPerfTest
Change-Id: Ic1da797b895901f5385476295a14b2bb2d5b3613
parent 7f2044ea
Loading
Loading
Loading
Loading
+58 −16
Original line number Diff line number Diff line
@@ -420,6 +420,8 @@ public final class ActivityThread extends ClientTransactionHandler {
    private static final class ProviderKey {
        final String authority;
        final int userId;
        ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter
        int mWaiters; // Number of threads waiting on the publishing of the provider

        public ProviderKey(String authority, int userId) {
            this.authority = authority;
@@ -437,7 +439,11 @@ public final class ActivityThread extends ClientTransactionHandler {

        @Override
        public int hashCode() {
            return ((authority != null) ? authority.hashCode() : 0) ^ userId;
            return hashCode(authority, userId);
        }

        public static int hashCode(final String auth, final int userIdent) {
            return ((auth != null) ? auth.hashCode() : 0) ^ userIdent;
        }
    }

@@ -458,9 +464,8 @@ public final class ActivityThread extends ClientTransactionHandler {
    // Mitigation for b/74523247: Used to serialize calls to AM.getContentProvider().
    // Note we never removes items from this map but that's okay because there are only so many
    // users and so many authorities.
    // TODO Remove it once we move CPR.wait() from AMS to the client side.
    @GuardedBy("mGetProviderLocks")
    final ArrayMap<ProviderKey, Object> mGetProviderLocks = new ArrayMap<>();
    @GuardedBy("mGetProviderKeys")
    final SparseArray<ProviderKey> mGetProviderKeys = new SparseArray<>();

    final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
        = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
@@ -1751,6 +1756,16 @@ public final class ActivityThread extends ClientTransactionHandler {
                    ActivityThread.this, activityToken, actionId, arguments,
                    cancellationSignal, resultCallback));
        }

        @Override
        public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder,
                @NonNull String auth, int userId, boolean published) {
            final ProviderKey key = getGetProviderKey(auth, userId);
            synchronized (key) {
                key.mHolder = holder;
                key.notifyAll();
            }
        }
    }

    private @NonNull SafeCancellationTransport createSafeCancellationTransport(
@@ -6796,13 +6811,40 @@ public final class ActivityThread extends ClientTransactionHandler {
        // provider since it might take a long time to run and it could also potentially
        // be re-entrant in the case where the provider is in the same process.
        ContentProviderHolder holder = null;
        final ProviderKey key = getGetProviderKey(auth, userId);
        synchronized (key) {
            boolean wasWaiting = false;
            try {
            synchronized (getGetProviderLock(auth, userId)) {
                if (key.mWaiters == 0) {
                    // No other thread is waiting for this provider, let's fetch one by ourselves.
                    // If the returned holder is non-null but its provider is null and it's not
                    // local, we'll need to wait for the publishing of the provider.
                    holder = ActivityManager.getService().getContentProvider(
                            getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
                }
                if ((holder != null && holder.provider == null && !holder.mLocal)
                        || (key.mWaiters > 0 && (holder = key.mHolder) == null)) {
                    try {
                        key.mWaiters++;
                        wasWaiting = true;
                        key.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
                        holder = key.mHolder;
                        if (holder != null && holder.provider == null) {
                            // probably timed out
                            holder = null;
                        }
                    } catch (InterruptedException e) {
                        holder = null;
                    }
                }
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            } finally {
                if (wasWaiting && --key.mWaiters == 0) {
                    // Clear the holder from the key since the key itself is never cleared.
                    key.mHolder = null;
                }
            }
        }
        if (holder == null) {
            if (UserManager.get(c).isUserUnlocked(userId)) {
@@ -6820,13 +6862,13 @@ public final class ActivityThread extends ClientTransactionHandler {
        return holder.provider;
    }

    private Object getGetProviderLock(String auth, int userId) {
        final ProviderKey key = new ProviderKey(auth, userId);
        synchronized (mGetProviderLocks) {
            Object lock = mGetProviderLocks.get(key);
    private ProviderKey getGetProviderKey(String auth, int userId) {
        final int key = ProviderKey.hashCode(auth, userId);
        synchronized (mGetProviderKeys) {
            ProviderKey lock = mGetProviderKeys.get(key);
            if (lock == null) {
                lock = key;
                mGetProviderLocks.put(key, lock);
                lock = new ProviderKey(auth, userId);
                mGetProviderKeys.put(key, lock);
            }
            return lock;
        }
+8 −1
Original line number Diff line number Diff line
@@ -39,6 +39,11 @@ public class ContentProviderHolder implements Parcelable {
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public boolean noReleaseNeeded;

    /**
     * Whether the provider here is a local provider or not.
     */
    public boolean mLocal;

    @UnsupportedAppUsage
    public ContentProviderHolder(ProviderInfo _info) {
        info = _info;
@@ -59,6 +64,7 @@ public class ContentProviderHolder implements Parcelable {
        }
        dest.writeStrongBinder(connection);
        dest.writeInt(noReleaseNeeded ? 1 : 0);
        dest.writeInt(mLocal ? 1 : 0);
    }

    public static final @android.annotation.NonNull Parcelable.Creator<ContentProviderHolder> CREATOR
@@ -81,5 +87,6 @@ public class ContentProviderHolder implements Parcelable {
                source.readStrongBinder());
        connection = source.readStrongBinder();
        noReleaseNeeded = source.readInt() != 0;
        mLocal = source.readInt() != 0;
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app;

import android.app.ContentProviderHolder;
import android.app.IInstrumentationWatcher;
import android.app.IUiAutomationConnection;
import android.app.ProfilerInfo;
@@ -147,4 +148,6 @@ oneway interface IApplicationThread {
    void performDirectAction(IBinder activityToken, String actionId,
            in Bundle arguments, in RemoteCallback cancellationCallback,
            in RemoteCallback resultCallback);
    void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth,
            int userId, boolean published);
}
+6 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.app.servertransaction.TestUtils.resultInfoList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import android.app.ContentProviderHolder;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.IUiAutomationConnection;
@@ -664,5 +665,10 @@ public class TransactionParcelTests {
        public void performDirectAction(IBinder activityToken, String actionId, Bundle arguments,
                RemoteCallback cancellationCallback, RemoteCallback resultCallback) {
        }

        @Override
        public void notifyContentProviderPublishStatus(ContentProviderHolder holder, String auth,
                int userId, boolean published) {
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -1579,6 +1579,7 @@ public class ActivityManagerService extends IActivityManager.Stub
    static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
    static final int KILL_APP_ZYGOTE_MSG = 71;
    static final int BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG = 72;
    static final int WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG = 73;
    static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1915,6 +1916,11 @@ public class ActivityManagerService extends IActivityManager.Stub
                case BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG: {
                    handleBinderHeavyHitterAutoSamplerTimeOut();
                } break;
                case WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG: {
                    synchronized (ActivityManagerService.this) {
                        ((ContentProviderRecord) msg.obj).onProviderPublishStatusLocked(false);
                    }
                } break;
            }
        }
    }
Loading