Merge "Protect autofill UI against redressing" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0541862ec5
@@ -61,6 +61,8 @@ public final class AutoFillUI {
|
||||
|
||||
private final MetricsLogger mMetricsLogger = new MetricsLogger();
|
||||
|
||||
private final @NonNull OverlayControl mOverlayControl;
|
||||
|
||||
public interface AutoFillUiCallback {
|
||||
void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent,
|
||||
@Nullable Bundle extras);
|
||||
@@ -75,6 +77,7 @@ public final class AutoFillUI {
|
||||
|
||||
public AutoFillUI(@NonNull Context context) {
|
||||
mContext = context;
|
||||
mOverlayControl = new OverlayControl(context);
|
||||
}
|
||||
|
||||
public void setCallback(@NonNull AutoFillUiCallback callback) {
|
||||
@@ -174,7 +177,7 @@ public final class AutoFillUI {
|
||||
}
|
||||
hideAllUiThread(callback);
|
||||
mFillUi = new FillUi(mContext, response, focusedId,
|
||||
filterText, new FillUi.Callback() {
|
||||
filterText, mOverlayControl, new FillUi.Callback() {
|
||||
@Override
|
||||
public void onResponsePicked(FillResponse response) {
|
||||
log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL);
|
||||
@@ -255,7 +258,7 @@ public final class AutoFillUI {
|
||||
}
|
||||
hideAllUiThread(callback);
|
||||
mSaveUi = new SaveUi(mContext, providerLabel, info,
|
||||
new SaveUi.OnSaveListener() {
|
||||
mOverlayControl, new SaveUi.OnSaveListener() {
|
||||
@Override
|
||||
public void onSave() {
|
||||
log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
|
||||
|
||||
@@ -100,7 +100,7 @@ final class FillUi {
|
||||
|
||||
FillUi(@NonNull Context context, @NonNull FillResponse response,
|
||||
@NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
|
||||
@NonNull Callback callback) {
|
||||
@NonNull OverlayControl overlayControl, @NonNull Callback callback) {
|
||||
mContext = context;
|
||||
mCallback = callback;
|
||||
|
||||
@@ -146,7 +146,7 @@ final class FillUi {
|
||||
mContentWidth = content.getMeasuredWidth();
|
||||
mContentHeight = content.getMeasuredHeight();
|
||||
|
||||
mWindow = new AnchoredWindow(decor);
|
||||
mWindow = new AnchoredWindow(decor, overlayControl);
|
||||
mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
|
||||
} else {
|
||||
final int datasetCount = response.getDatasets().size();
|
||||
@@ -193,7 +193,7 @@ final class FillUi {
|
||||
}
|
||||
|
||||
applyNewFilterText();
|
||||
mWindow = new AnchoredWindow(decor);
|
||||
mWindow = new AnchoredWindow(decor, overlayControl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,6 +366,7 @@ final class FillUi {
|
||||
}
|
||||
|
||||
final class AnchoredWindow implements View.OnTouchListener {
|
||||
private final @NonNull OverlayControl mOverlayControl;
|
||||
private final WindowManager mWm;
|
||||
private final View mContentView;
|
||||
private boolean mShowing;
|
||||
@@ -375,9 +376,10 @@ final class FillUi {
|
||||
*
|
||||
* @param contentView content of the window
|
||||
*/
|
||||
AnchoredWindow(View contentView) {
|
||||
AnchoredWindow(View contentView, @NonNull OverlayControl overlayControl) {
|
||||
mWm = contentView.getContext().getSystemService(WindowManager.class);
|
||||
mContentView = contentView;
|
||||
mOverlayControl = overlayControl;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,6 +393,7 @@ final class FillUi {
|
||||
.getString(R.string.autofill_picker_accessibility_title);
|
||||
mWm.addView(mContentView, params);
|
||||
mContentView.setOnTouchListener(this);
|
||||
mOverlayControl.hideOverlays();
|
||||
mShowing = true;
|
||||
} else {
|
||||
mWm.updateViewLayout(mContentView, params);
|
||||
@@ -423,6 +426,8 @@ final class FillUi {
|
||||
// does, it should not crash the system.
|
||||
Slog.e(TAG, "Exception hiding window ", e);
|
||||
mCallback.onDestroy();
|
||||
} finally {
|
||||
mOverlayControl.showOverlays();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.ui;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
|
||||
/**
|
||||
* This class controls showing/hiding overlays. We don't
|
||||
* hide all overlays (toast/system alerts) while sensitive
|
||||
* autofill UI is up.
|
||||
*/
|
||||
class OverlayControl {
|
||||
|
||||
private final IBinder mToken = new Binder();
|
||||
|
||||
private final @NonNull AppOpsManager mAppOpsManager;
|
||||
|
||||
OverlayControl(@NonNull Context context) {
|
||||
mAppOpsManager = context.getSystemService(AppOpsManager.class);
|
||||
}
|
||||
|
||||
void hideOverlays() {
|
||||
setOverlayAllowed(false);
|
||||
}
|
||||
|
||||
void showOverlays() {
|
||||
setOverlayAllowed(true);
|
||||
}
|
||||
|
||||
private void setOverlayAllowed(boolean allowed) {
|
||||
if (mAppOpsManager != null) {
|
||||
mAppOpsManager.setUserRestriction(
|
||||
AppOpsManager.OP_SYSTEM_ALERT_WINDOW, !allowed, mToken);
|
||||
mAppOpsManager.setUserRestriction(
|
||||
AppOpsManager.OP_TOAST_WINDOW, !allowed, mToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,14 +99,17 @@ final class SaveUi {
|
||||
|
||||
private final @NonNull OneTimeListener mListener;
|
||||
|
||||
private final @NonNull OverlayControl mOverlayControl;
|
||||
|
||||
private final CharSequence mTitle;
|
||||
private final CharSequence mSubTitle;
|
||||
|
||||
private boolean mDestroyed;
|
||||
|
||||
SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
|
||||
@NonNull OnSaveListener listener) {
|
||||
@NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener) {
|
||||
mListener = new OneTimeListener(listener);
|
||||
mOverlayControl = overlayControl;
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||
final View view = inflater.inflate(R.layout.autofill_save, null);
|
||||
@@ -197,16 +200,21 @@ final class SaveUi {
|
||||
|
||||
Slog.i(TAG, "Showing save dialog: " + mTitle);
|
||||
mDialog.show();
|
||||
mOverlayControl.hideOverlays();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
if (sDebug) Slog.d(TAG, "destroy()");
|
||||
throwIfDestroyed();
|
||||
mListener.onDestroy();
|
||||
mHandler.removeCallbacksAndMessages(mListener);
|
||||
if (sVerbose) Slog.v(TAG, "destroy(): dismissing dialog");
|
||||
mDialog.dismiss();
|
||||
mDestroyed = true;
|
||||
try {
|
||||
if (sDebug) Slog.d(TAG, "destroy()");
|
||||
throwIfDestroyed();
|
||||
mListener.onDestroy();
|
||||
mHandler.removeCallbacksAndMessages(mListener);
|
||||
if (sVerbose) Slog.v(TAG, "destroy(): dismissing dialog");
|
||||
mDialog.dismiss();
|
||||
mDestroyed = true;
|
||||
} finally {
|
||||
mOverlayControl.showOverlays();
|
||||
}
|
||||
}
|
||||
|
||||
private void throwIfDestroyed() {
|
||||
|
||||
Reference in New Issue
Block a user