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

Commit f71dbc4e authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Autofill Better Request ID" into main

parents 3cec4edf 6f5572de
Loading
Loading
Loading
Loading
+94 −0
Original line number Diff line number Diff line
/*
 * 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;
  }
}
+17 −46
Original line number Diff line number Diff line
@@ -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;
@@ -2884,7 +2865,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            removeFromService();
            return;
        }
        final FillResponse authenticatedResponse = isSecondaryProviderRequestId(requestId)
        final FillResponse authenticatedResponse = mRequestId.isSecondaryProvider(requestId)
                ? mSecondaryResponses.get(requestId)
                : mResponses.get(requestId);
        if (authenticatedResponse == null || data == null) {
@@ -2982,7 +2963,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                mPresentationStatsEventLogger.maybeSetAuthenticationResult(
                        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);
@@ -3011,10 +2993,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        }
    }

    private static boolean isSecondaryProviderRequestId(int requestId) {
        return requestId % 2 == 1;
    }

    private Dataset getDatasetFromCredentialResponse(GetCredentialResponse result) {
        if (result == null) {
            return null;
@@ -6929,22 +6907,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) {
+185 −0
Original line number Diff line number Diff line
/*
 * 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);
        }

    }
}