summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/autofill/java/com/android/server/autofill/RequestId.java94
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java63
-rw-r--r--services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java185
3 files changed, 296 insertions, 46 deletions
diff --git a/services/autofill/java/com/android/server/autofill/RequestId.java b/services/autofill/java/com/android/server/autofill/RequestId.java
new file mode 100644
index 000000000000..29ad786dbd4b
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/RequestId.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 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.server.autofill;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+// Helper class containing various methods to deal with FillRequest Ids.
+// For authentication flows, there needs to be a way to know whether to retrieve the Fill
+// Response from the primary provider or the secondary provider from the requestId. A simple
+// way to achieve this is by assigning odd number request ids to secondary provider and
+// even numbers to primary provider.
+public class RequestId {
+
+ private AtomicInteger sIdCounter;
+
+ // Mainly used for tests
+ RequestId(int start) {
+ sIdCounter = new AtomicInteger(start);
+ }
+
+ public RequestId() {
+ this((int) (Math.floor(Math.random() * 0xFFFF)));
+ }
+
+ public static int getLastRequestIdIndex(List<Integer> requestIds) {
+ int lastId = -1;
+ int indexOfBiggest = -1;
+ // Biggest number is usually the latest request, since IDs only increase
+ // The only exception is when the request ID wraps around back to 0
+ for (int i = requestIds.size() - 1; i >= 0; i--) {
+ if (requestIds.get(i) > lastId) {
+ lastId = requestIds.get(i);
+ indexOfBiggest = i;
+ }
+ }
+
+ // 0xFFFE + 2 == 0x1 (for secondary)
+ // 0xFFFD + 2 == 0x0 (for primary)
+ // Wrap has occurred
+ if (lastId >= 0xFFFD) {
+ // Calculate the biggest size possible
+ // If list only has one kind of request ids - we need to multiple by 2
+ // (since they skip odd ints)
+ // Also subtract one from size because at least one integer exists pre-wrap
+ int calcSize = (requestIds.size()) * 2;
+ //Biggest possible id after wrapping
+ int biggestPossible = (lastId + calcSize) % 0xFFFF;
+ lastId = -1;
+ indexOfBiggest = -1;
+ for (int i = 0; i < requestIds.size(); i++) {
+ int currentId = requestIds.get(i);
+ if (currentId <= biggestPossible && currentId > lastId) {
+ lastId = currentId;
+ indexOfBiggest = i;
+ }
+ }
+ }
+
+ return indexOfBiggest;
+ }
+
+ public int nextId(boolean isSecondary) {
+ // For authentication flows, there needs to be a way to know whether to retrieve the Fill
+ // Response from the primary provider or the secondary provider from the requestId. A simple
+ // way to achieve this is by assigning odd number request ids to secondary provider and
+ // even numbers to primary provider.
+ int requestId;
+
+ do {
+ requestId = sIdCounter.incrementAndGet() % 0xFFFF;
+ sIdCounter.set(requestId);
+ } while (isSecondaryProvider(requestId) != isSecondary);
+ return requestId;
+ }
+
+ public static boolean isSecondaryProvider(int requestId) {
+ return requestId % 2 == 1;
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6c76aebc0288..1c544c8e2567 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -263,7 +263,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
static final int AUGMENTED_AUTOFILL_REQUEST_ID = 1;
- private static AtomicInteger sIdCounter = new AtomicInteger(2);
+ private static RequestId mRequestId = new RequestId();
private static AtomicInteger sIdCounterForPcc = new AtomicInteger(2);
@@ -1333,7 +1333,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
viewState.setState(newState);
- int requestId = getRequestId(isSecondary);
+ int requestId = mRequestId.nextId(isSecondary);
// Create a metrics log for the request
final int ordinal = mRequestLogs.size() + 1;
@@ -1415,25 +1415,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
requestAssistStructureLocked(requestId, flags);
}
- private static int getRequestId(boolean isSecondary) {
- // For authentication flows, there needs to be a way to know whether to retrieve the Fill
- // Response from the primary provider or the secondary provider from the requestId. A simple
- // way to achieve this is by assigning odd number request ids to secondary provider and
- // even numbers to primary provider.
- int requestId;
- // TODO(b/158623971): Update this to prevent possible overflow
- if (isSecondary) {
- do {
- requestId = sIdCounter.getAndIncrement();
- } while (!isSecondaryProviderRequestId(requestId));
- } else {
- do {
- requestId = sIdCounter.getAndIncrement();
- } while (requestId == INVALID_REQUEST_ID || isSecondaryProviderRequestId(requestId));
- }
- return requestId;
- }
-
private boolean isRequestSupportFillDialog(int flags) {
return (flags & FLAG_SUPPORTS_FILL_DIALOG) != 0;
}
@@ -1441,7 +1422,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private void requestAssistStructureForPccLocked(int flags) {
if (!mClassificationState.shouldTriggerRequest()) return;
- mFillRequestIdSnapshot = sIdCounter.get();
+ mFillRequestIdSnapshot = sIdCounterForPcc.get();
mClassificationState.updatePendingRequest();
// Get request id
int requestId;
@@ -2879,18 +2860,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// the auth UI.
Slog.w(TAG, "setAuthenticationResultLocked(" + authenticationId + "): no responses");
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
- AUTHENTICATION_RESULT_FAILURE);
+ AUTHENTICATION_RESULT_FAILURE);
mPresentationStatsEventLogger.logAndEndEvent();
removeFromService();
return;
}
- final FillResponse authenticatedResponse = isSecondaryProviderRequestId(requestId)
+ final FillResponse authenticatedResponse = mRequestId.isSecondaryProvider(requestId)
? mSecondaryResponses.get(requestId)
: mResponses.get(requestId);
if (authenticatedResponse == null || data == null) {
Slog.w(TAG, "no authenticated response");
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
- AUTHENTICATION_RESULT_FAILURE);
+ AUTHENTICATION_RESULT_FAILURE);
mPresentationStatsEventLogger.logAndEndEvent();
removeFromService();
return;
@@ -2905,7 +2886,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (dataset == null) {
Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
- AUTHENTICATION_RESULT_FAILURE);
+ AUTHENTICATION_RESULT_FAILURE);
mPresentationStatsEventLogger.logAndEndEvent();
removeFromService();
return;
@@ -2946,7 +2927,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
- AUTHENTICATION_RESULT_SUCCESS);
+ AUTHENTICATION_RESULT_SUCCESS);
replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
} else if (result instanceof GetCredentialResponse) {
if (sDebug) {
@@ -2980,9 +2961,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
- AUTHENTICATION_RESULT_SUCCESS);
+ AUTHENTICATION_RESULT_SUCCESS);
if (newClientState != null) {
- if (sDebug) Slog.d(TAG, "Updating client state from auth dataset");
+ if (sDebug)
+ Slog.d(TAG, "Updating client state from auth dataset");
mClientState = newClientState;
}
Dataset datasetFromResult = getEffectiveDatasetForAuthentication((Dataset) result);
@@ -2997,7 +2979,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
- AUTHENTICATION_RESULT_FAILURE);
+ AUTHENTICATION_RESULT_FAILURE);
}
} else {
if (result != null) {
@@ -3006,15 +2988,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
- AUTHENTICATION_RESULT_FAILURE);
+ AUTHENTICATION_RESULT_FAILURE);
processNullResponseLocked(requestId, 0);
}
}
- private static boolean isSecondaryProviderRequestId(int requestId) {
- return requestId % 2 == 1;
- }
-
private Dataset getDatasetFromCredentialResponse(GetCredentialResponse result) {
if (result == null) {
return null;
@@ -6923,22 +6901,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private int getLastResponseIndexLocked() {
- // The response ids are monotonically increasing so
- // we just find the largest id which is the last. We
- // do not rely on the internal ordering in sparse
- // array to avoid - wow this stopped working!?
- int lastResponseIdx = -1;
- int lastResponseId = -1;
if (mResponses != null) {
+ List<Integer> requestIdList = new ArrayList<>();
final int responseCount = mResponses.size();
for (int i = 0; i < responseCount; i++) {
- if (mResponses.keyAt(i) > lastResponseId) {
- lastResponseIdx = i;
- lastResponseId = mResponses.keyAt(i);
- }
+ requestIdList.add(mResponses.keyAt(i));
}
+ return mRequestId.getLastRequestIdIndex(requestIdList);
}
- return lastResponseIdx;
+ return -1;
}
private LogMaker newLogMaker(int category) {
diff --git a/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java b/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java
new file mode 100644
index 000000000000..6d56c417f789
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 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.server.autofill;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RequestIdTest {
+
+ List<Integer> datasetPrimaryNoWrap = new ArrayList<>();
+ List<Integer> datasetPrimaryWrap = new ArrayList<>();
+ List<Integer> datasetSecondaryNoWrap = new ArrayList<>();
+ List<Integer> datasetSecondaryWrap = new ArrayList<>();
+ List<Integer> datasetMixedNoWrap = new ArrayList<>();
+ List<Integer> datasetMixedWrap = new ArrayList<>();
+
+ @Before
+ public void setup() throws Exception {
+ int datasetSize = 300;
+
+ { // Generate primary only ids that do not wrap
+ RequestId requestId = new RequestId(0);
+ for (int i = 0; i < datasetSize; i++) {
+ datasetPrimaryNoWrap.add(requestId.nextId(false));
+ }
+ }
+
+ { // Generate primary only ids that wrap
+ RequestId requestId = new RequestId(0xff00);
+ for (int i = 0; i < datasetSize; i++) {
+ datasetPrimaryWrap.add(requestId.nextId(false));
+ }
+ }
+
+ { // Generate SECONDARY only ids that do not wrap
+ RequestId requestId = new RequestId(0);
+ for (int i = 0; i < datasetSize; i++) {
+ datasetSecondaryNoWrap.add(requestId.nextId(true));
+ }
+ }
+
+ { // Generate SECONDARY only ids that wrap
+ RequestId requestId = new RequestId(0xff00);
+ for (int i = 0; i < datasetSize; i++) {
+ datasetSecondaryWrap.add(requestId.nextId(true));
+ }
+ }
+
+ { // Generate MIXED only ids that do not wrap
+ RequestId requestId = new RequestId(0);
+ for (int i = 0; i < datasetSize; i++) {
+ datasetMixedNoWrap.add(requestId.nextId(i % 2 != 0));
+ }
+ }
+
+ { // Generate MIXED only ids that wrap
+ RequestId requestId = new RequestId(0xff00);
+ for (int i = 0; i < datasetSize; i++) {
+ datasetMixedWrap.add(requestId.nextId(i % 2 != 0));
+ }
+ }
+ }
+
+ @Test
+ public void testRequestIdLists() {
+ for (int id : datasetPrimaryNoWrap) {
+ assertThat(RequestId.isSecondaryProvider(id)).isFalse();
+ assertThat(id >= 0).isTrue();
+ assertThat(id < 0xffff).isTrue();
+ }
+
+ for (int id : datasetPrimaryWrap) {
+ assertThat(RequestId.isSecondaryProvider(id)).isFalse();
+ assertThat(id >= 0).isTrue();
+ assertThat(id < 0xffff).isTrue();
+ }
+
+ for (int id : datasetSecondaryNoWrap) {
+ assertThat(RequestId.isSecondaryProvider(id)).isTrue();
+ assertThat(id >= 0).isTrue();
+ assertThat(id < 0xffff).isTrue();
+ }
+
+ for (int id : datasetSecondaryWrap) {
+ assertThat(RequestId.isSecondaryProvider(id)).isTrue();
+ assertThat(id >= 0).isTrue();
+ assertThat(id < 0xffff).isTrue();
+ }
+ }
+
+ @Test
+ public void testRequestIdGeneration() {
+ RequestId requestId = new RequestId(0);
+
+ // Large Primary
+ for (int i = 0; i < 100000; i++) {
+ int y = requestId.nextId(false);
+ assertThat(RequestId.isSecondaryProvider(y)).isFalse();
+ assertThat(y >= 0).isTrue();
+ assertThat(y < 0xffff).isTrue();
+ }
+
+ // Large Secondary
+ requestId = new RequestId(0);
+ for (int i = 0; i < 100000; i++) {
+ int y = requestId.nextId(true);
+ assertThat(RequestId.isSecondaryProvider(y)).isTrue();
+ assertThat(y >= 0).isTrue();
+ assertThat(y < 0xffff).isTrue();
+ }
+
+ // Large Mixed
+ requestId = new RequestId(0);
+ for (int i = 0; i < 50000; i++) {
+ int y = requestId.nextId(i % 2 != 0);
+ assertThat(RequestId.isSecondaryProvider(y)).isEqualTo(i % 2 == 0);
+ assertThat(y >= 0).isTrue();
+ assertThat(y < 0xffff).isTrue();
+ }
+ }
+
+ @Test
+ public void testGetLastRequestId() {
+ // In this test, request ids are generated FIFO, so the last entry is also the last
+ // request
+
+ { // Primary no wrap
+ int lastIdIndex = datasetPrimaryNoWrap.size() - 1;
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryNoWrap);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ }
+
+ { // Primary wrap
+ int lastIdIndex = datasetPrimaryWrap.size() - 1;
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryWrap);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ }
+
+ { // Secondary no wrap
+ int lastIdIndex = datasetSecondaryNoWrap.size() - 1;
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetSecondaryNoWrap);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ }
+
+ { // Secondary wrap
+ int lastIdIndex = datasetSecondaryWrap.size() - 1;
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetSecondaryWrap);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ }
+
+ { // Mixed no wrap
+ int lastIdIndex = datasetMixedNoWrap.size() - 1;
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetMixedNoWrap);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ }
+
+ { // Mixed wrap
+ int lastIdIndex = datasetMixedWrap.size() - 1;
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetMixedWrap);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ }
+
+ }
+}