Make IAutofillManager fully oneway.

The critical methods on this interface - like updateSession() - were already
void, so all we had to do were to "onewaywize" the other methods. We could
either refactor them to be truly async, or implement a blocking mechanism that
let them still be sync *and* oneway - because these methods are not in the
critical path, we opted for the latter, which is simpler and less risky.

Fixes: 73536867

Test: mmma -j ./frameworks/base/apct-tests/perftests/autofill/ && \
      adb install -r $OUT/data/app/AutofillPerfTests/AutofillPerfTests.apk && \
      adb shell am instrument -w -e class android.view.autofill.LoginTest \
      com.android.perftests.autofill/android.support.test.runner.AndroidJUnitRunner
Test: CtsAutoFillServiceTestCases

Change-Id: I380430aa2a7805aed6f629afb360566fc5402abb
This commit is contained in:
Felipe Leme
2018-06-18 13:56:38 -07:00
parent d955be72b7
commit d4e52285ed
3 changed files with 254 additions and 84 deletions

View File

@@ -57,6 +57,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
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;
@@ -72,6 +73,8 @@ 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;
@@ -572,10 +575,11 @@ public final class AutofillManager {
final AutofillClient client = getClient();
if (client != null) {
final SyncResultReceiver receiver = new SyncResultReceiver();
try {
final boolean sessionWasRestored = mService.restoreSession(mSessionId,
client.autofillClientGetActivityToken(),
mServiceClient.asBinder());
mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), receiver);
final boolean sessionWasRestored = receiver.getIntResult() == 1;
if (!sessionWasRestored) {
Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -691,7 +695,9 @@ public final class AutofillManager {
*/
@Nullable public FillEventHistory getFillEventHistory() {
try {
return mService.getFillEventHistory();
final SyncResultReceiver receiver = new SyncResultReceiver();
mService.getFillEventHistory(receiver);
return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1242,8 +1248,10 @@ public final class AutofillManager {
public boolean hasEnabledAutofillServices() {
if (mService == null) return false;
final SyncResultReceiver receiver = new SyncResultReceiver();
try {
return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
return receiver.getIntResult() == 1;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1257,8 +1265,10 @@ public final class AutofillManager {
public ComponentName getAutofillServiceComponentName() {
if (mService == null) return null;
final SyncResultReceiver receiver = new SyncResultReceiver();
try {
return mService.getAutofillServiceComponentName();
mService.getAutofillServiceComponentName(receiver);
return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1281,7 +1291,9 @@ public final class AutofillManager {
*/
@Nullable public String getUserDataId() {
try {
return mService.getUserDataId();
final SyncResultReceiver receiver = new SyncResultReceiver();
mService.getUserDataId(receiver);
return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1301,7 +1313,9 @@ public final class AutofillManager {
*/
@Nullable public UserData getUserData() {
try {
return mService.getUserData();
final SyncResultReceiver receiver = new SyncResultReceiver();
mService.getUserData(receiver);
return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1337,8 +1351,10 @@ public final class AutofillManager {
* the user.
*/
public boolean isFieldClassificationEnabled() {
final SyncResultReceiver receiver = new SyncResultReceiver();
try {
return mService.isFieldClassificationEnabled();
mService.isFieldClassificationEnabled(receiver);
return receiver.getIntResult() == 1;
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return false;
@@ -1358,8 +1374,10 @@ public final class AutofillManager {
*/
@Nullable
public String getDefaultFieldClassificationAlgorithm() {
final SyncResultReceiver receiver = new SyncResultReceiver();
try {
return mService.getDefaultFieldClassificationAlgorithm();
mService.getDefaultFieldClassificationAlgorithm(receiver);
return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1376,9 +1394,10 @@ public final class AutofillManager {
*/
@NonNull
public List<String> getAvailableFieldClassificationAlgorithms() {
final String[] algorithms;
final SyncResultReceiver receiver = new SyncResultReceiver();
try {
algorithms = mService.getAvailableFieldClassificationAlgorithms();
mService.getAvailableFieldClassificationAlgorithms(receiver);
final String[] algorithms = receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
@@ -1399,8 +1418,10 @@ public final class AutofillManager {
public boolean isAutofillSupported() {
if (mService == null) return false;
final SyncResultReceiver receiver = new SyncResultReceiver();
try {
return mService.isServiceSupported(mContext.getUserId());
mService.isServiceSupported(mContext.getUserId(), receiver);
return receiver.getIntResult() == 1;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1521,10 +1542,12 @@ public final class AutofillManager {
final AutofillClient client = getClient();
if (client == null) return; // NOTE: getClient() already logged it..
mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
final SyncResultReceiver receiver = new SyncResultReceiver();
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, client.autofillClientGetComponentName(),
isCompatibilityModeEnabledLocked());
isCompatibilityModeEnabledLocked(), receiver);
mSessionId = receiver.getIntResult();
if (mSessionId != NO_SESSION) {
mState = STATE_ACTIVE;
}
@@ -1602,7 +1625,9 @@ public final class AutofillManager {
mServiceClient = new AutofillManagerClient(this);
try {
final int userId = mContext.getUserId();
final int flags = mService.addClient(mServiceClient, userId);
final SyncResultReceiver receiver = new SyncResultReceiver();
mService.addClient(mServiceClient, userId, receiver);
final int flags = receiver.getIntResult();
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
@@ -2818,4 +2843,104 @@ 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.getString(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;
}
}
}

View File

@@ -28,41 +28,39 @@ import android.service.autofill.UserData;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.os.IResultReceiver;
/**
* Mediator between apps being auto-filled and auto-fill service implementations.
*
* {@hide}
*/
// TODO(b/73536867) STOPSHIP : this whole interface should be either oneway or not, and we're
// gradually converting the methods (as some of them return a value form the server and must be
// refactored).
interface IAutoFillManager {
oneway interface IAutoFillManager {
// Returns flags: FLAG_ADD_CLIENT_ENABLED | FLAG_ADD_CLIENT_DEBUG | FLAG_ADD_CLIENT_VERBOSE
int addClient(in IAutoFillManagerClient client, int userId);
void addClient(in IAutoFillManagerClient client, int userId, in IResultReceiver result);
void removeClient(in IAutoFillManagerClient client, int userId);
int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
in ComponentName componentName, boolean compatMode);
FillEventHistory getFillEventHistory();
boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
oneway void updateSession(int sessionId, in AutofillId id, in Rect bounds,
in AutofillValue value, int action, int flags, int userId);
oneway void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
oneway void finishSession(int sessionId, int userId);
oneway void cancelSession(int sessionId, int userId);
oneway void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId,
int userId);
oneway void setHasCallback(int sessionId, int userId, boolean hasIt);
void startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
in ComponentName componentName, boolean compatMode, in IResultReceiver result);
void getFillEventHistory(in IResultReceiver result);
void restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback,
in IResultReceiver result);
void updateSession(int sessionId, in AutofillId id, in Rect bounds,
in AutofillValue value, int action, int flags, int userId);
void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
void finishSession(int sessionId, int userId);
void cancelSession(int sessionId, int userId);
void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
void setHasCallback(int sessionId, int userId, boolean hasIt);
void disableOwnedAutofillServices(int userId);
boolean isServiceSupported(int userId);
boolean isServiceEnabled(int userId, String packageName);
void isServiceSupported(int userId, in IResultReceiver result);
void isServiceEnabled(int userId, String packageName, in IResultReceiver result);
void onPendingSaveUi(int operation, IBinder token);
UserData getUserData();
String getUserDataId();
void getUserData(in IResultReceiver result);
void getUserDataId(in IResultReceiver result);
void setUserData(in UserData userData);
boolean isFieldClassificationEnabled();
ComponentName getAutofillServiceComponentName();
String[] getAvailableFieldClassificationAlgorithms();
String getDefaultFieldClassificationAlgorithm();
void isFieldClassificationEnabled(in IResultReceiver result);
void getAutofillServiceComponentName(in IResultReceiver result);
void getAvailableFieldClassificationAlgorithms(in IResultReceiver result);
void getDefaultFieldClassificationAlgorithm(in IResultReceiver result);
}

View File

@@ -46,6 +46,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -622,6 +623,38 @@ public final class AutofillManagerService extends SystemService {
return getWhitelistedCompatModePackages(getWhitelistedCompatModePackagesFromSettings());
}
private void send(@NonNull IResultReceiver receiver, int value) {
try {
receiver.send(value, null);
} catch (RemoteException e) {
Slog.w(TAG, "Error async reporting result to client: " + e);
}
}
private void send(@NonNull IResultReceiver receiver, @NonNull Bundle value) {
try {
receiver.send(0, value);
} catch (RemoteException e) {
Slog.w(TAG, "Error async reporting result to client: " + e);
}
}
private void send(@NonNull IResultReceiver receiver, @Nullable String value) {
send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
}
private void send(@NonNull IResultReceiver receiver, @Nullable String[] value) {
send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
}
private void send(@NonNull IResultReceiver receiver, @Nullable Parcelable value) {
send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
}
private void send(@NonNull IResultReceiver receiver, boolean value) {
send(receiver, value ? 1 : 0);
}
@Nullable
@VisibleForTesting
static Map<String, String[]> getWhitelistedCompatModePackages(String setting) {
@@ -827,9 +860,10 @@ public final class AutofillManagerService extends SystemService {
final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
@Override
public int addClient(IAutoFillManagerClient client, int userId) {
public void addClient(IAutoFillManagerClient client, int userId,
@NonNull IResultReceiver receiver) {
int flags = 0;
synchronized (mLock) {
int flags = 0;
if (getServiceForUserLocked(userId).addClientLocked(client)) {
flags |= AutofillManager.FLAG_ADD_CLIENT_ENABLED;
}
@@ -839,8 +873,8 @@ public final class AutofillManagerService extends SystemService {
if (sVerbose) {
flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
}
return flags;
}
send(receiver, flags);
}
@Override
@@ -874,9 +908,9 @@ public final class AutofillManagerService extends SystemService {
}
@Override
public int startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
public void startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags,
ComponentName componentName, boolean compatMode) {
ComponentName componentName, boolean compatMode, IResultReceiver receiver) {
activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
@@ -892,61 +926,63 @@ public final class AutofillManagerService extends SystemService {
throw new IllegalArgumentException(packageName + " is not a valid package", e);
}
final int sessionId;
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
return service.startSessionLocked(activityToken, getCallingUid(), appCallback,
sessionId = service.startSessionLocked(activityToken, getCallingUid(), appCallback,
autofillId, bounds, value, hasCallback, componentName, compatMode,
mAllowInstantService, flags);
}
send(receiver, sessionId);
}
@Override
public FillEventHistory getFillEventHistory() throws RemoteException {
public void getFillEventHistory(@NonNull IResultReceiver receiver) throws RemoteException {
final int userId = UserHandle.getCallingUserId();
FillEventHistory fillEventHistory = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
return service.getFillEventHistory(getCallingUid());
fillEventHistory = service.getFillEventHistory(getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "getFillEventHistory(): no service for " + userId);
}
}
return null;
send(receiver, fillEventHistory);
}
@Override
public UserData getUserData() throws RemoteException {
public void getUserData(@NonNull IResultReceiver receiver) throws RemoteException {
final int userId = UserHandle.getCallingUserId();
UserData userData = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
return service.getUserData(getCallingUid());
userData = service.getUserData(getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "getUserData(): no service for " + userId);
}
}
return null;
send(receiver, userData);
}
@Override
public String getUserDataId() throws RemoteException {
public void getUserDataId(@NonNull IResultReceiver receiver) throws RemoteException {
final int userId = UserHandle.getCallingUserId();
UserData userData = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
final UserData userData = service.getUserData(getCallingUid());
return userData == null ? null : userData.getId();
userData = service.getUserData(getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "getUserDataId(): no service for " + userId);
}
}
return null;
final String userDataId = userData == null ? null : userData.getId();
send(receiver, userDataId);
}
@Override
@@ -964,89 +1000,96 @@ public final class AutofillManagerService extends SystemService {
}
@Override
public boolean isFieldClassificationEnabled() throws RemoteException {
public void isFieldClassificationEnabled(@NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
boolean enabled = false;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
return service.isFieldClassificationEnabled(getCallingUid());
enabled = service.isFieldClassificationEnabled(getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId);
}
}
return false;
send(receiver, enabled);
}
@Override
public String getDefaultFieldClassificationAlgorithm() throws RemoteException {
public void getDefaultFieldClassificationAlgorithm(@NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
String algorithm = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
return service.getDefaultFieldClassificationAlgorithm(getCallingUid());
algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid());
} else {
if (sVerbose) {
Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId);
}
return null;
}
}
send(receiver, algorithm);
}
@Override
public String[] getAvailableFieldClassificationAlgorithms() throws RemoteException {
public void getAvailableFieldClassificationAlgorithms(@NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
String[] algorithms = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
return service.getAvailableFieldClassificationAlgorithms(getCallingUid());
algorithms = service.getAvailableFieldClassificationAlgorithms(getCallingUid());
} else {
if (sVerbose) {
Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId);
}
return null;
}
}
send(receiver, algorithms);
}
@Override
public ComponentName getAutofillServiceComponentName() throws RemoteException {
public void getAutofillServiceComponentName(@NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
ComponentName componentName = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
return service.getServiceComponentName();
componentName = service.getServiceComponentName();
} else if (sVerbose) {
Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId);
}
}
return null;
send(receiver, componentName);
}
@Override
public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
public void restoreSession(int sessionId, @NonNull IBinder activityToken,
@NonNull IBinder appCallback, @NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
boolean restored = false;
synchronized (mLock) {
final AutofillManagerServiceImpl service = mServicesCache.get(userId);
if (service != null) {
return service.restoreSession(sessionId, getCallingUid(), activityToken,
restored = service.restoreSession(sessionId, getCallingUid(), activityToken,
appCallback);
} else if (sVerbose) {
Slog.v(TAG, "restoreSession(): no service for " + userId);
}
}
return false;
send(receiver, restored);
}
@Override
@@ -1112,23 +1155,27 @@ public final class AutofillManagerService extends SystemService {
}
@Override
public boolean isServiceSupported(int userId) {
public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) {
boolean supported = false;
synchronized (mLock) {
return !mDisabledUsers.get(userId);
supported = !mDisabledUsers.get(userId);
}
send(receiver, supported);
}
@Override
public boolean isServiceEnabled(int userId, String packageName) {
public void isServiceEnabled(int userId, @NonNull String packageName,
@NonNull IResultReceiver receiver) {
boolean enabled = false;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
return Objects.equals(packageName, service.getServicePackageName());
enabled = Objects.equals(packageName, service.getServicePackageName());
} else if (sVerbose) {
Slog.v(TAG, "isServiceEnabled(): no service for " + userId);
}
return false;
}
send(receiver, enabled);
}
@Override