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

Commit 33948042 authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Check callbacks when operting on UI

This makes sure that only the session that current owns the UI can make
changes to the UI:

The callback is the ID of the current owner. The only caller that does
not ID itself (and thereby causes the Ui to close in any way) is the
ACTION_CLOSE_SYSTEM_DIALOGS path.

Change-Id: Ib396864411f362b59deb500251c37896ecbc7de7
Fixes: 38042845
Test: CtsAutoFillServicesTestCases
parent 1c38d294
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -118,7 +118,7 @@ public final class AutofillManagerService extends SystemService {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
                mUi.hideAll();
                mUi.hideAll(null);
            }
        }
    };
+13 −12
Original line number Diff line number Diff line
@@ -419,7 +419,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        }
        if (response == null) {
            if ((requestFlags & FLAG_MANUAL_REQUEST) != 0) {
                getUiForShowing().showError(R.string.autofill_error_cannot_autofill);
                getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
            }
            // Nothing to be done, but need to notify client.
            notifyUnavailableToClient();
@@ -469,7 +469,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
        mMetricsLogger.write(log);

        getUiForShowing().showError(message);
        getUiForShowing().showError(message, this);
        removeSelf();
    }

@@ -516,7 +516,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
        mMetricsLogger.write(log);

        getUiForShowing().showError(message);
        getUiForShowing().showError(message, this);
        removeSelf();
    }

@@ -706,7 +706,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                        // refactor auth to support partitions; if it doesn't, we need to
                        // investigate it further (it can be reproduced by running
                        // LoginActivityTest.testFillResponseAuthServiceHasNoData())
                        mUi.hideAll();
                        mUi.hideAll(this);
                    }
                    processResponseLocked(response);
                } else {
@@ -851,7 +851,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            }
            if (atLeastOneChanged) {
                mService.setSaveShown();
                getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName);
                getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName,
                        this);

                mIsSaving = true;
                return false;
@@ -1062,9 +1063,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

                    //..and the UI
                    if (value.isText()) {
                        getUiForShowing().filterFillUi(value.getTextValue().toString());
                        getUiForShowing().filterFillUi(value.getTextValue().toString(), this);
                    } else {
                        getUiForShowing().filterFillUi(null);
                        getUiForShowing().filterFillUi(null, this);
                    }
                }
                break;
@@ -1076,7 +1077,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

                // Remove the UI if the ViewState has changed.
                if (mCurrentViewId != viewState.id) {
                    mUi.hideFillUi(mCurrentViewId != null ? mCurrentViewId : null);
                    mUi.hideFillUi(this);
                    mCurrentViewId = viewState.id;
                }

@@ -1086,7 +1087,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            case ACTION_VIEW_EXITED:
                if (mCurrentViewId == viewState.id) {
                    if (sVerbose) Slog.d(TAG, "Exiting view " + id);
                    mUi.hideFillUi(viewState.id);
                    mUi.hideFillUi(this);
                    mCurrentViewId = null;
                }
                break;
@@ -1123,7 +1124,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            filterText = value.getTextValue().toString();
        }

        getUiForShowing().showFillUi(filledId, response, filterText, mPackageName);
        getUiForShowing().showFillUi(filledId, response, filterText, mPackageName, this);
    }

    boolean isDestroyed() {
@@ -1434,8 +1435,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            return;
        }
        mRemoteFillService.destroy();
        mUi.hideAll();
        mUi.setCallback(null);
        mUi.hideAll(this);
        mUi.clearCallback(this);
        mDestroyed = true;
        mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName);
    }
+46 −36
Original line number Diff line number Diff line
@@ -74,31 +74,43 @@ public final class AutoFillUI {
        mContext = context;
    }

    public void setCallback(@Nullable AutoFillUiCallback callback) {
    public void setCallback(@NonNull AutoFillUiCallback callback) {
        mHandler.post(() -> {
            if (mCallback != callback) {
                hideAllUiThread();
                if (mCallback != null) {
                    hideAllUiThread(callback);
                }

                mCallback = callback;
            }
        });
    }

    public void clearCallback(@NonNull AutoFillUiCallback callback) {
        mHandler.post(() -> {
            if (mCallback == callback) {
                hideAllUiThread(callback);
                mCallback = null;
            }
        });
    }

    /**
     * Displays an error message to the user.
     */
    public void showError(int resId) {
        showError(mContext.getString(resId));
    public void showError(int resId, @NonNull AutoFillUiCallback callback) {
        showError(mContext.getString(resId), callback);
    }

    /**
     * Displays an error message to the user.
     */
    public void showError(@Nullable CharSequence message) {
    public void showError(@Nullable CharSequence message, @NonNull AutoFillUiCallback callback) {
        mHandler.post(() -> {
            if (!hasCallback()) {
            if (mCallback != callback) {
                return;
            }
            hideAllUiThread();
            hideAllUiThread(callback);
            if (!TextUtils.isEmpty(message)) {
                Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
            }
@@ -108,8 +120,8 @@ public final class AutoFillUI {
    /**
     * Hides the fill UI.
     */
    public void hideFillUi(AutofillId id) {
        mHandler.post(this::hideFillUiUiThread);
    public void hideFillUi(@NonNull AutoFillUiCallback callback) {
        mHandler.post(() -> hideFillUiUiThread(callback));
    }

    /**
@@ -117,12 +129,12 @@ public final class AutoFillUI {
     *
     * @param filterText The filter prefix.
     */
    public void filterFillUi(@Nullable String filterText) {
    public void filterFillUi(@Nullable String filterText, @NonNull AutoFillUiCallback callback) {
        mHandler.post(() -> {
            if (!hasCallback()) {
            if (callback != mCallback) {
                return;
            }
            hideSaveUiUiThread();
            hideSaveUiUiThread(callback);
            if (mFillUi != null) {
                mFillUi.setFilterText(filterText);
            }
@@ -136,9 +148,11 @@ public final class AutoFillUI {
     * @param response the current fill response
     * @param filterText text of the view to be filled
     * @param packageName package name of the activity that is filled
     * @param callback Identifier for the caller
     */
    public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
            @Nullable String filterText, @NonNull String packageName) {
            @Nullable String filterText, @NonNull String packageName,
            @NonNull AutoFillUiCallback callback) {
        if (sDebug) {
            Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + filterText);
        }
@@ -150,16 +164,16 @@ public final class AutoFillUI {
                        response.getDatasets() == null ? 0 : response.getDatasets().size());

        mHandler.post(() -> {
            if (!hasCallback()) {
            if (callback != mCallback) {
                return;
            }
            hideAllUiThread();
            hideAllUiThread(callback);
            mFillUi = new FillUi(mContext, response, focusedId,
                    filterText, new FillUi.Callback() {
                @Override
                public void onResponsePicked(FillResponse response) {
                    log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL);
                    hideFillUiUiThread();
                    hideFillUiUiThread(callback);
                    if (mCallback != null) {
                        mCallback.authenticate(response.getRequestId(),
                                response.getAuthentication(), response.getClientState());
@@ -169,7 +183,7 @@ public final class AutoFillUI {
                @Override
                public void onDatasetPicked(Dataset dataset) {
                    log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
                    hideFillUiUiThread();
                    hideFillUiUiThread(callback);
                    if (mCallback != null) {
                        mCallback.fill(response.getRequestId(), dataset);
                    }
@@ -178,7 +192,7 @@ public final class AutoFillUI {
                @Override
                public void onCanceled() {
                    log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
                    hideFillUiUiThread();
                    hideFillUiUiThread(callback);
                }

                @Override
@@ -218,7 +232,7 @@ public final class AutoFillUI {
     * Shows the UI asking the user to save for autofill.
     */
    public void showSaveUi(@NonNull CharSequence providerLabel, @NonNull SaveInfo info,
            @NonNull String packageName) {
            @NonNull String packageName, @NonNull AutoFillUiCallback callback) {
        int numIds = 0;
        numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
        numIds += info.getOptionalIds() == null ? 0 : info.getOptionalIds().length;
@@ -228,16 +242,16 @@ public final class AutoFillUI {
                        MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);

        mHandler.post(() -> {
            if (!hasCallback()) {
            if (callback != mCallback) {
                return;
            }
            hideAllUiThread();
            hideAllUiThread(callback);
            mSaveUi = new SaveUi(mContext, providerLabel, info,
                    new SaveUi.OnSaveListener() {
                @Override
                public void onSave() {
                    log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
                    hideSaveUiUiThread();
                    hideSaveUiUiThread(callback);
                    if (mCallback != null) {
                        mCallback.save();
                    }
@@ -246,7 +260,7 @@ public final class AutoFillUI {
                @Override
                public void onCancel(IntentSender listener) {
                    log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
                    hideSaveUiUiThread();
                    hideSaveUiUiThread(callback);
                    if (listener != null) {
                        try {
                            listener.sendIntent(mContext, 0, null, null, null);
@@ -278,8 +292,8 @@ public final class AutoFillUI {
    /**
     * Hides all UI affordances.
     */
    public void hideAll() {
        mHandler.post(this::hideAllUiThread);
    public void hideAll(@Nullable AutoFillUiCallback callback) {
        mHandler.post(() -> hideAllUiThread(callback));
    }

    public void dump(PrintWriter pw) {
@@ -301,28 +315,24 @@ public final class AutoFillUI {
    }

    @android.annotation.UiThread
    private void hideFillUiUiThread() {
        if (mFillUi != null) {
    private void hideFillUiUiThread(@Nullable AutoFillUiCallback callback) {
        if (mFillUi != null && (callback == null || callback == mCallback)) {
            mFillUi.destroy();
            mFillUi = null;
        }
    }

    @android.annotation.UiThread
    private void hideSaveUiUiThread() {
        if (mSaveUi != null) {
    private void hideSaveUiUiThread(@Nullable AutoFillUiCallback callback) {
        if (mSaveUi != null && (callback == null || callback == mCallback)) {
            mSaveUi.destroy();
            mSaveUi = null;
        }
    }

    @android.annotation.UiThread
    private void hideAllUiThread() {
        hideFillUiUiThread();
        hideSaveUiUiThread();
    }

    private boolean hasCallback() {
        return mCallback != null;
    private void hideAllUiThread(@Nullable AutoFillUiCallback callback) {
        hideFillUiUiThread(callback);
        hideSaveUiUiThread(callback);
    }
}