@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;
@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;
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);
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);
import android.util.Slog;
import android.util.TimedRemoteCaller;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.TransferPipe;
import java.io.FileDescriptor;
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();
/** 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(
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();
}
};
try {
- getRemoteInstanceLazy()
+ getRemoteInstanceLazy(token)
.getInstantAppIntentFilterList(hashPrefix, token, hostName, remoteCallback);
- } catch (RemoteException re) {
- } catch (TimeoutException te) {
+ } catch (RemoteException e) {
+ } catch (InterruptedException | TimeoutException e) {
+ Slog.w(TAG, "[" + token + "] Timeout! Phase2 binding to instant app resolver");
}
}
- 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);
- }
- }
- }
-
- 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.");
- }
- try {
- mLock.wait(remainingMillis);
- } catch (InterruptedException ie) {
- /* ignore */
+ throw new TimeoutException("[" + token + "] Didn't bind to resolver in time!");
}
+ mLock.wait(remainingMillis);
}
+ }
- mLock.notifyAll();
+ 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() {
@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;
}
/**
@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();
}
@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();
}
}
}
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 {
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*/);
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;
}
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
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());
}
}
}