Merge "Rewrite field classification remote callback" into udc-dev

This commit is contained in:
Haoran Zhang
2023-06-01 21:00:04 +00:00
committed by Android (Google) Code Review
2 changed files with 161 additions and 67 deletions

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2023 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 android.annotation.Nullable;
import android.os.Bundle;
import android.os.RemoteCallback;
import android.service.autofill.FieldClassification;
import android.service.autofill.FillEventHistory.Event.NoSaveReason;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager.AutofillCommitReason;
import java.util.ArrayList;
class LogFieldClassificationScoreOnResultListener implements
RemoteCallback.OnResultListener {
private static final String TAG = "LogFieldClassificationScoreOnResultListener";
private Session mSession;
private final @NoSaveReason int mSaveDialogNotShowReason;
private final @AutofillCommitReason int mCommitReason;
private final int mViewsSize;
private final AutofillId[] mAutofillIds;
private final String[] mUserValues;
private final String[] mCategoryIds;
private final ArrayList<AutofillId> mDetectedFieldIds;
private final ArrayList<FieldClassification> mDetectedFieldClassifications;
LogFieldClassificationScoreOnResultListener(Session session,
int saveDialogNotShowReason,
int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues,
String[] categoryIds, ArrayList<AutofillId> detectedFieldIds,
ArrayList<FieldClassification> detectedFieldClassifications) {
this.mSession = session;
this.mSaveDialogNotShowReason = saveDialogNotShowReason;
this.mCommitReason = commitReason;
this.mViewsSize = viewsSize;
this.mAutofillIds = autofillIds;
this.mUserValues = userValues;
this.mCategoryIds = categoryIds;
this.mDetectedFieldIds = detectedFieldIds;
this.mDetectedFieldClassifications = detectedFieldClassifications;
}
public void onResult(@Nullable Bundle result) {
// Create a local copy to safe guard race condition
Session session = mSession;
if (session == null) {
Slog.wtf(TAG, "session is null when calling onResult()");
return;
}
session.handleLogFieldClassificationScore(
result,
mSaveDialogNotShowReason,
mCommitReason,
mViewsSize,
mAutofillIds,
mUserValues,
mCategoryIds,
mDetectedFieldIds,
mDetectedFieldClassifications);
mSession = null;
}
}

View File

@@ -3063,78 +3063,93 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// Then use the results, asynchronously
final RemoteCallback callback = new RemoteCallback((result) -> {
if (result == null) {
if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
return;
}
final Scores scores = result.getParcelable(EXTRA_SCORES, android.service.autofill.AutofillFieldClassificationService.Scores.class);
if (scores == null) {
Slog.w(TAG, "No field classification score on " + result);
return;
}
int i = 0, j = 0;
try {
// Iteract over all autofill fields first
for (i = 0; i < viewsSize; i++) {
final AutofillId autofillId = autofillIds[i];
// Search the best scores for each category (as some categories could have
// multiple user values
ArrayMap<String, Float> scoresByField = null;
for (j = 0; j < userValues.length; j++) {
final String categoryId = categoryIds[j];
final float score = scores.scores[i][j];
if (score > 0) {
if (scoresByField == null) {
scoresByField = new ArrayMap<>(userValues.length);
}
final Float currentScore = scoresByField.get(categoryId);
if (currentScore != null && currentScore > score) {
if (sVerbose) {
Slog.v(TAG, "skipping score " + score
+ " because it's less than " + currentScore);
}
continue;
}
if (sVerbose) {
Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
+ autofillId);
}
scoresByField.put(categoryId, score);
} else if (sVerbose) {
Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId);
}
}
if (scoresByField == null) {
if (sVerbose) Slog.v(TAG, "no score for autofillId=" + autofillId);
continue;
}
// Then create the matches for that autofill id
final ArrayList<Match> matches = new ArrayList<>(scoresByField.size());
for (j = 0; j < scoresByField.size(); j++) {
final String fieldId = scoresByField.keyAt(j);
final float score = scoresByField.valueAt(j);
matches.add(new Match(fieldId, score));
}
detectedFieldIds.add(autofillId);
detectedFieldClassifications.add(new FieldClassification(matches));
} // for i
} catch (ArrayIndexOutOfBoundsException e) {
wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
return;
}
logContextCommitted(detectedFieldIds, detectedFieldClassifications,
saveDialogNotShowReason, commitReason);
});
final RemoteCallback callback = new RemoteCallback(
new LogFieldClassificationScoreOnResultListener(
this,
saveDialogNotShowReason,
commitReason,
viewsSize,
autofillIds,
userValues,
categoryIds,
detectedFieldIds,
detectedFieldClassifications));
fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
defaultAlgorithm, defaultArgs, algorithms, args);
}
void handleLogFieldClassificationScore(@Nullable Bundle result, int saveDialogNotShowReason,
int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues,
String[] categoryIds, ArrayList<AutofillId> detectedFieldIds,
ArrayList<FieldClassification> detectedFieldClassifications) {
if (result == null) {
if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
return;
}
final Scores scores = result.getParcelable(EXTRA_SCORES,
android.service.autofill.AutofillFieldClassificationService.Scores.class);
if (scores == null) {
Slog.w(TAG, "No field classification score on " + result);
return;
}
int i = 0, j = 0;
try {
// Iteract over all autofill fields first
for (i = 0; i < viewsSize; i++) {
final AutofillId autofillId = autofillIds[i];
// Search the best scores for each category (as some categories could have
// multiple user values
ArrayMap<String, Float> scoresByField = null;
for (j = 0; j < userValues.length; j++) {
final String categoryId = categoryIds[j];
final float score = scores.scores[i][j];
if (score > 0) {
if (scoresByField == null) {
scoresByField = new ArrayMap<>(userValues.length);
}
final Float currentScore = scoresByField.get(categoryId);
if (currentScore != null && currentScore > score) {
if (sVerbose) {
Slog.v(TAG, "skipping score " + score
+ " because it's less than " + currentScore);
}
continue;
}
if (sVerbose) {
Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
+ autofillId);
}
scoresByField.put(categoryId, score);
} else if (sVerbose) {
Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId);
}
}
if (scoresByField == null) {
if (sVerbose) Slog.v(TAG, "no score for autofillId=" + autofillId);
continue;
}
// Then create the matches for that autofill id
final ArrayList<Match> matches = new ArrayList<>(scoresByField.size());
for (j = 0; j < scoresByField.size(); j++) {
final String fieldId = scoresByField.keyAt(j);
final float score = scoresByField.valueAt(j);
matches.add(new Match(fieldId, score));
}
detectedFieldIds.add(autofillId);
detectedFieldClassifications.add(new FieldClassification(matches));
} // for i
} catch (ArrayIndexOutOfBoundsException e) {
wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
return;
}
logContextCommitted(detectedFieldIds, detectedFieldClassifications,
saveDialogNotShowReason, commitReason);
}
/**
* Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN}
* when necessary.