diff options
3 files changed, 175 insertions, 124 deletions
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 56f973eb9e46..90ccc257b134 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -62,6 +62,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IResultReceiver; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; +import com.android.internal.util.SyncResultReceiver; import org.xmlpull.v1.XmlPullParserException; @@ -75,8 +76,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; + //TODO: use java.lang.ref.Cleaner once Android supports Java 9 import sun.misc.Cleaner; @@ -324,6 +324,11 @@ public final class AutofillManager { public static final int FC_SERVICE_TIMEOUT = 5000; /** + * Timeout for calls to system_server. + */ + private static final int SYNC_CALLS_TIMEOUT_MS = 5000; + + /** * Makes an authentication id from a request id and a dataset id. * * @param requestId The request id. @@ -612,7 +617,8 @@ public final class AutofillManager { final AutofillClient client = getClient(); if (client != null) { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver( + SYNC_CALLS_TIMEOUT_MS); try { mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(), mServiceClient.asBinder(), receiver); @@ -732,9 +738,9 @@ public final class AutofillManager { */ @Nullable public FillEventHistory getFillEventHistory() { try { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.getFillEventHistory(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE); + return receiver.getParcelableResult(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1287,7 +1293,7 @@ public final class AutofillManager { public boolean hasEnabledAutofillServices() { if (mService == null) return false; - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver); return receiver.getIntResult() == 1; @@ -1304,10 +1310,10 @@ public final class AutofillManager { public ComponentName getAutofillServiceComponentName() { if (mService == null) return null; - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.getAutofillServiceComponentName(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE); + return receiver.getParcelableResult(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1330,9 +1336,9 @@ public final class AutofillManager { */ @Nullable public String getUserDataId() { try { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.getUserDataId(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING); + return receiver.getStringResult(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1352,9 +1358,9 @@ public final class AutofillManager { */ @Nullable public UserData getUserData() { try { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.getUserData(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE); + return receiver.getParcelableResult(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1390,7 +1396,7 @@ public final class AutofillManager { * the user. */ public boolean isFieldClassificationEnabled() { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.isFieldClassificationEnabled(receiver); return receiver.getIntResult() == 1; @@ -1413,10 +1419,10 @@ public final class AutofillManager { */ @Nullable public String getDefaultFieldClassificationAlgorithm() { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.getDefaultFieldClassificationAlgorithm(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING); + return receiver.getStringResult(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1433,11 +1439,10 @@ public final class AutofillManager { */ @NonNull public List<String> getAvailableFieldClassificationAlgorithms() { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.getAvailableFieldClassificationAlgorithms(receiver); - final String[] algorithms = receiver - .getObjectResult(SyncResultReceiver.TYPE_STRING_ARRAY); + final String[] algorithms = receiver.getStringArrayResult(); return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList(); } catch (RemoteException e) { e.rethrowFromSystemServer(); @@ -1458,7 +1463,7 @@ public final class AutofillManager { public boolean isAutofillSupported() { if (mService == null) return false; - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.isServiceSupported(mContext.getUserId(), receiver); return receiver.getIntResult() == 1; @@ -1582,7 +1587,7 @@ public final class AutofillManager { final AutofillClient client = getClient(); if (client == null) return; // NOTE: getClient() already logged it.. - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.startSession(client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), mCallback != null, flags, client.autofillClientGetComponentName(), @@ -1665,7 +1670,7 @@ public final class AutofillManager { mServiceClient = new AutofillManagerClient(this); try { final int userId = mContext.getUserId(); - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.addClient(mServiceClient, userId, receiver); final int flags = receiver.getIntResult(); mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0; @@ -2986,104 +2991,4 @@ public final class AutofillManager { } } } - - /** - * @hide - */ - public static final class SyncResultReceiver extends IResultReceiver.Stub { - - private static final String EXTRA = "EXTRA"; - - /** - * How long to block waiting for {@link IResultReceiver} callbacks when calling server. - */ - private static final long BINDER_TIMEOUT_MS = 5000; - - private static final int TYPE_STRING = 0; - private static final int TYPE_STRING_ARRAY = 1; - private static final int TYPE_PARCELABLE = 2; - - private final CountDownLatch mLatch = new CountDownLatch(1); - private int mResult; - private Bundle mBundle; - - private void waitResult() { - try { - if (!mLatch.await(BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { - throw new IllegalStateException("Not called in " + BINDER_TIMEOUT_MS + "ms"); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - /** - * Gets the result from an operation that returns an {@code int}. - */ - int getIntResult() { - waitResult(); - return mResult; - } - - /** - * Gets the result from an operation that returns an {@code Object}. - * - * @param type type of expected object. - */ - @Nullable - @SuppressWarnings("unchecked") - <T> T getObjectResult(int type) { - waitResult(); - if (mBundle == null) { - return null; - } - switch (type) { - case TYPE_STRING: - return (T) mBundle.getString(EXTRA); - case TYPE_STRING_ARRAY: - return (T) mBundle.getStringArray(EXTRA); - case TYPE_PARCELABLE: - return (T) mBundle.getParcelable(EXTRA); - default: - throw new IllegalArgumentException("unsupported type: " + type); - } - } - - @Override - public void send(int resultCode, Bundle resultData) { - mResult = resultCode; - mBundle = resultData; - mLatch.countDown(); - } - - /** - * Creates a bundle for a {@code String} value. - */ - @NonNull - public static Bundle bundleFor(@Nullable String value) { - final Bundle bundle = new Bundle(); - bundle.putString(EXTRA, value); - return bundle; - } - - /** - * Creates a bundle for a {@code String[]} value. - */ - @NonNull - public static Bundle bundleFor(@Nullable String[] value) { - final Bundle bundle = new Bundle(); - bundle.putStringArray(EXTRA, value); - return bundle; - } - - /** - * Creates a bundle for a {@code Parcelable} value. - */ - @NonNull - public static Bundle bundleFor(@Nullable Parcelable value) { - final Bundle bundle = new Bundle(); - bundle.putParcelable(EXTRA, value); - return bundle; - } - } } diff --git a/core/java/com/android/internal/util/SyncResultReceiver.java b/core/java/com/android/internal/util/SyncResultReceiver.java new file mode 100644 index 000000000000..9a346ac93a8d --- /dev/null +++ b/core/java/com/android/internal/util/SyncResultReceiver.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.Parcelable; +import android.os.RemoteException; + +import com.android.internal.os.IResultReceiver; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * A {@code IResultReceiver} implementation that can be used to make "sync" Binder calls by blocking + * until it receives a result + * + * @hide + */ +public final class SyncResultReceiver extends IResultReceiver.Stub { + + private static final String EXTRA = "EXTRA"; + + private final CountDownLatch mLatch = new CountDownLatch(1); + private final int mTimeoutMs; + private int mResult; + private Bundle mBundle; + + /** + * Default constructor. + * + * @param timeoutMs how long to block waiting for {@link IResultReceiver} callbacks. + */ + public SyncResultReceiver(int timeoutMs) { + mTimeoutMs = timeoutMs; + } + + private void waitResult() throws TimeoutException { + try { + if (!mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Not called in " + mTimeoutMs + "ms"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TimeoutException("Interrupted"); + } + } + + /** + * Gets the result from an operation that returns an {@code int}. + */ + public int getIntResult() throws TimeoutException { + waitResult(); + return mResult; + } + + /** + * Gets the result from an operation that returns an {@code String}. + */ + @Nullable + public String getStringResult() throws TimeoutException { + waitResult(); + return mBundle == null ? null : mBundle.getString(EXTRA); + } + + /** + * Gets the result from an operation that returns a {@code String[]}. + */ + @Nullable + public String[] getStringArrayResult() throws TimeoutException { + waitResult(); + return mBundle == null ? null : mBundle.getStringArray(EXTRA); + } + + /** + * Gets the result from an operation that returns a {@code Parcelable}. + */ + @Nullable + public <P extends Parcelable> P getParcelableResult() throws TimeoutException { + waitResult(); + return mBundle == null ? null : mBundle.getParcelable(EXTRA); + } + + @Override + public void send(int resultCode, Bundle resultData) { + mResult = resultCode; + mBundle = resultData; + mLatch.countDown(); + } + + /** + * Creates a bundle for a {@code String} value so it can be retrieved by + * {@link #getStringResult()}. + */ + @NonNull + public static Bundle bundleFor(@Nullable String value) { + final Bundle bundle = new Bundle(); + bundle.putString(EXTRA, value); + return bundle; + } + + /** + * Creates a bundle for a {@code String[]} value so it can be retrieved by + * {@link #getStringArrayResult()}. + */ + @NonNull + public static Bundle bundleFor(@Nullable String[] value) { + final Bundle bundle = new Bundle(); + bundle.putStringArray(EXTRA, value); + return bundle; + } + + /** + * Creates a bundle for a {@code Parcelable} value so it can be retrieved by + * {@link #getParcelableResult()}. + */ + @NonNull + public static Bundle bundleFor(@Nullable Parcelable value) { + final Bundle bundle = new Bundle(); + bundle.putParcelable(EXTRA, value); + return bundle; + } + + /** @hide */ + public static final class TimeoutException extends RemoteException { + private TimeoutException(String msg) { + super(msg); + } + } +} diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 1a6bee9daaf3..6cccd62d563f 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -70,6 +70,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; +import com.android.internal.util.SyncResultReceiver; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.autofill.ui.AutoFillUI; @@ -606,15 +607,15 @@ public final class AutofillManagerService } private void send(@NonNull IResultReceiver receiver, @Nullable String value) { - send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value)); + send(receiver, SyncResultReceiver.bundleFor(value)); } private void send(@NonNull IResultReceiver receiver, @Nullable String[] value) { - send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value)); + send(receiver, SyncResultReceiver.bundleFor(value)); } private void send(@NonNull IResultReceiver receiver, @Nullable Parcelable value) { - send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value)); + send(receiver, SyncResultReceiver.bundleFor(value)); } private void send(@NonNull IResultReceiver receiver, boolean value) { |