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

Commit 355931b8 authored by Todd Kennedy's avatar Todd Kennedy Committed by android-build-merger
Browse files

Merge "Fix deadlock between AMS and EphemeralResolverConnection" into oc-mr1-dev am: a3735dc4

am: 7a8c6a39

Change-Id: I1d9e4b8d4aec4f9a81d5a0a4d0d520327321f79f
parents e98f1e5a 7a8c6a39
Loading
Loading
Loading
Loading
+75 −34
Original line number Diff line number Diff line
@@ -69,8 +69,12 @@ final class EphemeralResolverConnection implements DeathRecipient {
    /** Intent used to bind to the service */
    private final Intent mIntent;

    private static final int STATE_IDLE    = 0; // no bind operation is ongoing
    private static final int STATE_BINDING = 1; // someone is binding and waiting
    private static final int STATE_PENDING = 2; // a bind is pending, but the caller is not waiting

    @GuardedBy("mLock")
    private volatile boolean mIsBinding;
    private int mBindState = STATE_IDLE;
    @GuardedBy("mLock")
    private IInstantAppResolver mRemoteInstance;

@@ -137,23 +141,17 @@ final class EphemeralResolverConnection implements DeathRecipient {

    private IInstantAppResolver getRemoteInstanceLazy(String token)
            throws ConnectionException, TimeoutException, InterruptedException {
        synchronized (mLock) {
            if (mRemoteInstance != null) {
                return mRemoteInstance;
            }
        long binderToken = Binder.clearCallingIdentity();
        try {
                bindLocked(token);
            return bind(token);
        } finally {
            Binder.restoreCallingIdentity(binderToken);
        }
            return mRemoteInstance;
        }
    }

    private void waitForBindLocked(String token) throws TimeoutException, InterruptedException {
        final long startMillis = SystemClock.uptimeMillis();
        while (mIsBinding) {
        while (mBindState != STATE_IDLE) {
            if (mRemoteInstance != null) {
                break;
            }
@@ -166,42 +164,83 @@ final class EphemeralResolverConnection implements DeathRecipient {
        }
    }

    private void bindLocked(String token)
    private IInstantAppResolver bind(String token)
            throws ConnectionException, TimeoutException, InterruptedException {
        if (DEBUG_EPHEMERAL && mIsBinding && mRemoteInstance == null) {
        boolean doUnbind = false;
        synchronized (mLock) {
            if (mRemoteInstance != null) {
                return mRemoteInstance;
            }

            if (mBindState == STATE_PENDING) {
                // there is a pending bind, let's see if we can use it.
                if (DEBUG_EPHEMERAL) {
                    Slog.i(TAG, "[" + token + "] Previous bind timed out; waiting for connection");
                }
                try {
                    waitForBindLocked(token);
                    if (mRemoteInstance != null) {
                        return mRemoteInstance;
                    }
                } catch (TimeoutException e) {
                    // nope, we might have to try a rebind.
                    doUnbind = true;
                }
            }

            if (mBindState == STATE_BINDING) {
                // someone was binding when we called bind(), or they raced ahead while we were
                // waiting in the PENDING case; wait for their result instead. Last chance!
                if (DEBUG_EPHEMERAL) {
                    Slog.i(TAG, "[" + token + "] Another thread is binding; waiting for connection");
                }
                waitForBindLocked(token);
                // if the other thread's bindService() returned false, we could still have null.
                if (mRemoteInstance != null) {
                    return mRemoteInstance;
                }
                throw new ConnectionException(ConnectionException.FAILURE_BIND);
            }
            mBindState = STATE_BINDING; // our time to shine! :)
        }

        // only one thread can be here at a time (the one that set STATE_BINDING)
        boolean wasBound = false;
        IInstantAppResolver instance = null;
        try {
            if (doUnbind) {
                if (DEBUG_EPHEMERAL) {
                    Slog.i(TAG, "[" + token + "] Previous connection never established; rebinding");
                }
                mContext.unbindService(mServiceConnection);
            }
        if (mRemoteInstance != null) {
            return;
        }
        mIsBinding = true;
            if (DEBUG_EPHEMERAL) {
                Slog.v(TAG, "[" + token + "] Binding to instant app resolver");
            }
        boolean wasBound = false;
        try {
            final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
            wasBound = mContext
                    .bindServiceAsUser(mIntent, mServiceConnection, flags, UserHandle.SYSTEM);
            if (wasBound) {
                synchronized (mLock) {
                    waitForBindLocked(token);
                    instance = mRemoteInstance;
                    return instance;
                }
            } else {
                Slog.w(TAG, "[" + token + "] Failed to bind to: " + mIntent);
                throw new ConnectionException(ConnectionException.FAILURE_BIND);
            }
        } finally {
            mIsBinding = wasBound && mRemoteInstance == null;
            synchronized (mLock) {
                if (wasBound && instance == null) {
                    mBindState = STATE_PENDING;
                } else {
                    mBindState = STATE_IDLE;
                }
                mLock.notifyAll();
            }
        }
    }

    private void throwIfCalledOnMainThread() {
        if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
@@ -255,7 +294,9 @@ final class EphemeralResolverConnection implements DeathRecipient {
            }
            synchronized (mLock) {
                mRemoteInstance = IInstantAppResolver.Stub.asInterface(service);
                mIsBinding = false;
                if (mBindState == STATE_PENDING) {
                    mBindState = STATE_IDLE;
                }
                try {
                    service.linkToDeath(EphemeralResolverConnection.this, 0 /*flags*/);
                } catch (RemoteException e) {