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

Commit 4eaeb91e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "fix service binding" into oc-dev

parents 1ab9119f 46b4f2b7
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -91,6 +91,9 @@ public abstract class InstantAppResolverService extends Service {
            @Override
            public void getInstantAppResolveInfoList(
                    int digestPrefix[], String token, int sequence, IRemoteCallback callback) {
                if (DEBUG_EPHEMERAL) {
                    Slog.v(TAG, "[" + token + "] Phase1 called; posting");
                }
                final SomeArgs args = SomeArgs.obtain();
                args.arg1 = callback;
                args.arg2 = digestPrefix;
@@ -103,6 +106,9 @@ public abstract class InstantAppResolverService extends Service {
            @Override
            public void getInstantAppIntentFilterList(
                    int digestPrefix[], String token, String hostName, IRemoteCallback callback) {
                if (DEBUG_EPHEMERAL) {
                    Slog.v(TAG, "[" + token + "] Phase2 called; posting");
                }
                final SomeArgs args = SomeArgs.obtain();
                args.arg1 = callback;
                args.arg2 = digestPrefix;
@@ -140,7 +146,7 @@ public abstract class InstantAppResolverService extends Service {
    void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
            InstantAppResolutionCallback callback) {
        if (DEBUG_EPHEMERAL) {
            Slog.d(TAG, "Instant resolver; getInstantAppResolveInfo;"
            Slog.d(TAG, "[" + token + "] Phase1 request;"
                    + " prefix: " + Arrays.toString(digestPrefix));
        }
        onGetInstantAppResolveInfo(digestPrefix, token, callback);
@@ -149,7 +155,7 @@ public abstract class InstantAppResolverService extends Service {
    void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName,
            InstantAppResolutionCallback callback) {
        if (DEBUG_EPHEMERAL) {
            Slog.d(TAG, "Instant resolver; getInstantAppIntentFilter;"
            Slog.d(TAG, "[" + token + "] Phase2 request;"
                    + " prefix: " + Arrays.toString(digestPrefix));
        }
        onGetInstantAppIntentFilter(digestPrefix, token, callback);
+77 −55
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.os.UserHandle;
import android.util.Slog;
import android.util.TimedRemoteCaller;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.TransferPipe;

import java.io.FileDescriptor;
@@ -53,7 +54,9 @@ final class EphemeralResolverConnection implements DeathRecipient {
    private static final String TAG = "PackageManager";
    // This is running in a critical section and the timeout must be sufficiently low
    private static final long BIND_SERVICE_TIMEOUT_MS =
            ("eng".equals(Build.TYPE)) ? 300 : 200;
            ("eng".equals(Build.TYPE)) ? 500 : 300;
    private static final long CALL_SERVICE_TIMEOUT_MS =
            ("eng".equals(Build.TYPE)) ? 200 : 100;
    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;

    private final Object mLock = new Object();
@@ -64,7 +67,9 @@ final class EphemeralResolverConnection implements DeathRecipient {
    /** Intent used to bind to the service */
    private final Intent mIntent;

    private volatile boolean mBindRequested;
    @GuardedBy("mLock")
    private volatile boolean mIsBinding;
    @GuardedBy("mLock")
    private IInstantAppResolver mRemoteInstance;

    public EphemeralResolverConnection(
@@ -76,11 +81,18 @@ final class EphemeralResolverConnection implements DeathRecipient {
    public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[],
            String token) {
        throwIfCalledOnMainThread();
        IInstantAppResolver target = null;
        try {
            return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList(
                    getRemoteInstanceLazy(), hashPrefix, token);
        } catch (RemoteException re) {
        } catch (TimeoutException te) {
            target = getRemoteInstanceLazy(token);
            return mGetEphemeralResolveInfoCaller
                    .getEphemeralResolveInfoList(target, hashPrefix, token);
        } catch (RemoteException e) {
        } catch (InterruptedException | TimeoutException e) {
            if (target == null) {
                Slog.w(TAG, "[" + token + "] Timeout! Phase1 binding to instant app resolver");
            } else {
                Slog.w(TAG, "[" + token + "] Timeout! Phase1 resolving instant app");
            }
        } finally {
            synchronized (mLock) {
                mLock.notifyAll();
@@ -107,71 +119,74 @@ final class EphemeralResolverConnection implements DeathRecipient {
            }
        };
        try {
            getRemoteInstanceLazy()
            getRemoteInstanceLazy(token)
                    .getInstantAppIntentFilterList(hashPrefix, token, hostName, remoteCallback);
        } catch (RemoteException re) {
        } catch (TimeoutException te) {
        }
    }

    public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
        synchronized (mLock) {
            pw.append(prefix).append("bound=")
                    .append((mRemoteInstance != null) ? "true" : "false").println();

            pw.flush();
            try {
                TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), fd,
                        new String[] { prefix });
            } catch (IOException | TimeoutException | RemoteException e) {
                pw.println("Failed to dump remote instance: " + e);
            }
        } catch (RemoteException e) {
        } catch (InterruptedException | TimeoutException e) {
            Slog.w(TAG, "[" + token + "] Timeout! Phase2 binding to instant app resolver");
        }
    }

    private IInstantAppResolver getRemoteInstanceLazy() throws TimeoutException {
    private IInstantAppResolver getRemoteInstanceLazy(String token)
            throws TimeoutException, InterruptedException {
        synchronized (mLock) {
            if (mRemoteInstance != null) {
                return mRemoteInstance;
            }
            bindLocked();
            bindLocked(token);
            return mRemoteInstance;
        }
    }

    private void bindLocked() throws TimeoutException {
        if (mRemoteInstance != null) {
            return;
        }

        if (!mBindRequested) {
            mBindRequested = true;
            if (DEBUG_EPHEMERAL) {
                Slog.d(TAG, "Binding to resolver service");
            }
            mContext.bindServiceAsUser(mIntent, mServiceConnection,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, UserHandle.SYSTEM);
        }

    private void waitForBind(String token) throws TimeoutException, InterruptedException {
        final long startMillis = SystemClock.uptimeMillis();
        while (true) {
        while (mIsBinding) {
            if (mRemoteInstance != null) {
                break;
            }
            final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
            final long remainingMillis = BIND_SERVICE_TIMEOUT_MS - elapsedMillis;
            if (remainingMillis <= 0) {
                throw new TimeoutException("Didn't bind to resolver in time.");
                throw new TimeoutException("[" + token + "] Didn't bind to resolver in time!");
            }
            try {
            mLock.wait(remainingMillis);
            } catch (InterruptedException ie) {
                /* ignore */
        }
    }

    private void bindLocked(String token) throws TimeoutException, InterruptedException {
        if (DEBUG_EPHEMERAL && mIsBinding && mRemoteInstance == null) {
            Slog.i(TAG, "[" + token + "] Previous bind timed out; waiting for connection");
        }
        try {
            waitForBind(token);
        } catch (TimeoutException e) {
            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) {
                waitForBind(token);
            } else {
                Slog.w(TAG, "[" + token + "] Failed to bind to: " + mIntent);
            }
        } finally {
            mIsBinding = wasBound && mRemoteInstance == null;
            mLock.notifyAll();
        }
    }

    private void throwIfCalledOnMainThread() {
        if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
@@ -182,13 +197,18 @@ final class EphemeralResolverConnection implements DeathRecipient {
    @Override
    public void binderDied() {
        if (DEBUG_EPHEMERAL) {
            Slog.d(TAG, "Binder died");
            Slog.d(TAG, "Binder to instant app resolver died");
        }
        synchronized (mLock) {
            handleBinderDiedLocked();
        }
    }

    private void handleBinderDiedLocked() {
        if (mRemoteInstance != null) {
            mRemoteInstance.asBinder().unlinkToDeath(this, 0 /*flags*/);
        }
        mRemoteInstance = null;
        mBindRequested = false;
    }

    /**
@@ -203,13 +223,15 @@ final class EphemeralResolverConnection implements DeathRecipient {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (DEBUG_EPHEMERAL) {
                Slog.d(TAG, "Service connected");
                Slog.d(TAG, "Connected to instant app resolver");
            }
            synchronized (mLock) {
                mRemoteInstance = IInstantAppResolver.Stub.asInterface(service);
                mIsBinding = false;
                try {
                    service.linkToDeath(EphemeralResolverConnection.this, 0 /*flags*/);
                    mRemoteInstance = IInstantAppResolver.Stub.asInterface(service);
                } catch (RemoteException e) {
                    handleBinderDiedLocked();
                }
                mLock.notifyAll();
            }
@@ -218,10 +240,10 @@ final class EphemeralResolverConnection implements DeathRecipient {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG_EPHEMERAL) {
                Slog.d(TAG, "Service disconnected");
                Slog.d(TAG, "Disconnected from instant app resolver");
            }
            synchronized (mLock) {
                mRemoteInstance = null;
                handleBinderDiedLocked();
            }
        }
    }
@@ -231,7 +253,7 @@ final class EphemeralResolverConnection implements DeathRecipient {
        private final IRemoteCallback mCallback;

        public GetEphemeralResolveInfoCaller() {
            super(BIND_SERVICE_TIMEOUT_MS);
            super(CALL_SERVICE_TIMEOUT_MS);
            mCallback = new IRemoteCallback.Stub() {
                    @Override
                    public void sendResult(Bundle data) throws RemoteException {
+15 −8
Original line number Diff line number Diff line
@@ -74,11 +74,11 @@ public abstract class InstantAppResolver {

    public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(Context context,
            EphemeralResolverConnection connection, InstantAppRequest requestObj) {
        if (DEBUG_EPHEMERAL) {
            Log.d(TAG, "Resolving phase 1");
        }
        final long startTime = System.currentTimeMillis();
        final String token = UUID.randomUUID().toString();
        if (DEBUG_EPHEMERAL) {
            Log.d(TAG, "[" + token + "] Resolving phase 1");
        }
        final Intent intent = requestObj.origIntent;
        final InstantAppDigest digest =
                new InstantAppDigest(intent.getData().getHost(), 5 /*maxDigests*/);
@@ -89,7 +89,7 @@ public abstract class InstantAppResolver {
        if (instantAppResolveInfoList == null || instantAppResolveInfoList.size() == 0) {
            // No hash prefix match; there are no instant apps for this domain.
            if (DEBUG_EPHEMERAL) {
                Log.d(TAG, "No results returned");
                Log.d(TAG, "[" + token + "] No results returned");
            }
            return null;
        }
@@ -98,21 +98,24 @@ public abstract class InstantAppResolver {
                intent.getPackage(), digest, token);
        logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token,
                RESOLUTION_SUCCESS);
        if (DEBUG_EPHEMERAL && resolveInfo == null) {
            Log.d(TAG, "[" + token + "] No results matched");
        }
        return resolveInfo;
    }

    public static void doInstantAppResolutionPhaseTwo(Context context,
            EphemeralResolverConnection connection, InstantAppRequest requestObj,
            ActivityInfo instantAppInstaller, Handler callbackHandler) {
        final long startTime = System.currentTimeMillis();
        final String token = requestObj.responseObj.token;
        if (DEBUG_EPHEMERAL) {
            Log.d(TAG, "Resolving phase 2");
            Log.d(TAG, "[" + token + "] Resolving phase 2");
        }
        final long startTime = System.currentTimeMillis();
        final Intent intent = requestObj.origIntent;
        final String hostName = intent.getData().getHost();
        final InstantAppDigest digest = new InstantAppDigest(hostName, 5 /*maxDigests*/);
        final int[] shaPrefix = digest.getDigestPrefix();
        final String token = requestObj.responseObj.token;

        final PhaseTwoCallback callback = new PhaseTwoCallback() {
            @Override
@@ -285,12 +288,16 @@ public abstract class InstantAppResolver {
                if (!matchedResolveInfoList.isEmpty()) {
                    if (DEBUG_EPHEMERAL) {
                        final AuxiliaryResolveInfo info = matchedResolveInfoList.get(0);
                        Log.d(TAG, "Found match;"
                        Log.d(TAG, "[" + token + "] Found match;"
                                + " package: " + info.packageName
                                + ", split: " + info.splitName
                                + ", versionCode: " + info.versionCode);
                    }
                    return matchedResolveInfoList.get(0);
                } else if (DEBUG_EPHEMERAL) {
                    Log.d(TAG, "[" + token + "] No matches found"
                            + " package: " + instantAppInfo.getPackageName()
                            + ", versionCode: " + instantAppInfo.getVersionCode());
                }
            }
        }