From c50403c60689e225e8784dd2ce1c62be93f1cfe7 Mon Sep 17 00:00:00 2001 From: Christine Franks Date: Wed, 15 Mar 2017 15:13:44 -0700 Subject: [PATCH] Prevent apps from displaying over Settings Specifically, the DrawOverlayDetails fragment only. Bug: 36070413 Test: make RunSettingsRoboTests -j100 Change-Id: I0069f0cacf8c963c8c6878dc89a32b205983a568 --- .../applications/DrawOverlayDetails.java | 18 +++++++++ .../settings/core/TouchOverlayManager.java | 40 +++++++++++++++++++ .../applications/DrawOverlayDetailsTest.java | 25 +++++++++++- .../shadow/ShadowPreferenceFragment.java | 35 ++++++++++++++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/com/android/settings/core/TouchOverlayManager.java create mode 100644 tests/robotests/src/com/android/settings/testutils/shadow/ShadowPreferenceFragment.java diff --git a/src/com/android/settings/applications/DrawOverlayDetails.java b/src/com/android/settings/applications/DrawOverlayDetails.java index 39b8919430a..72564bc3f8e 100644 --- a/src/com/android/settings/applications/DrawOverlayDetails.java +++ b/src/com/android/settings/applications/DrawOverlayDetails.java @@ -36,6 +36,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; import com.android.settings.applications.AppStateOverlayBridge.OverlayState; +import com.android.settings.core.TouchOverlayManager; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.applications.ApplicationsState.AppEntry; @@ -61,6 +62,8 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc private Intent mSettingsIntent; private OverlayState mOverlayState; + private TouchOverlayManager mTouchOverlayManager; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -68,6 +71,7 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc Context context = getActivity(); mOverlayBridge = new AppStateOverlayBridge(context, mState, null); mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mTouchOverlayManager = new TouchOverlayManager(context); // find preferences addPreferencesFromResource(R.xml.app_ops_permissions_details); @@ -89,6 +93,20 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc .setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); } + @Override + public void onStart() { + super.onStart(); + + mTouchOverlayManager.setOverlayAllowed(false); + } + + @Override + public void onStop() { + super.onStop(); + + mTouchOverlayManager.setOverlayAllowed(true); + } + @Override public void onDestroy() { super.onDestroy(); diff --git a/src/com/android/settings/core/TouchOverlayManager.java b/src/com/android/settings/core/TouchOverlayManager.java new file mode 100644 index 00000000000..f69d1bfbb6c --- /dev/null +++ b/src/com/android/settings/core/TouchOverlayManager.java @@ -0,0 +1,40 @@ +/* + * 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.settings.core; + +import android.app.AppOpsManager; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; + +public class TouchOverlayManager { + + private final Context mContext; + private final IBinder mToken = new Binder(); + + public TouchOverlayManager(Context context) { + mContext = context; + } + + public void setOverlayAllowed(boolean allowed) { + final AppOpsManager aom = mContext.getSystemService(AppOpsManager.class); + if (aom != null) { + aom.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, !allowed, mToken); + aom.setUserRestriction(AppOpsManager.OP_TOAST_WINDOW, !allowed, mToken); + } + } +} diff --git a/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java b/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java index a5306a2ca5c..ef6351abfb2 100644 --- a/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java +++ b/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java @@ -19,10 +19,11 @@ package com.android.settings.applications; import android.content.Context; import com.android.internal.logging.nano.MetricsProto; -import com.android.internal.telephony.SmsUsageMonitor; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settings.core.TouchOverlayManager; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowPreferenceFragment; import org.junit.Before; import org.junit.Test; @@ -32,6 +33,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; +import org.robolectric.util.ReflectionHelpers; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; @@ -47,17 +49,22 @@ public class DrawOverlayDetailsTest { private FakeFeatureFactory mFeatureFactory; private DrawOverlayDetails mFragment; + @Mock + private TouchOverlayManager mTouchOverlayManager; + @Before public void setUp() { MockitoAnnotations.initMocks(this); FakeFeatureFactory.setupForTest(mContext); mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + mFragment = new DrawOverlayDetails(); - mFragment.onAttach(ShadowApplication.getInstance().getApplicationContext()); + ReflectionHelpers.setField(mFragment, "mTouchOverlayManager", mTouchOverlayManager); } @Test public void logSpecialPermissionChange() { + mFragment.onAttach(ShadowApplication.getInstance().getApplicationContext()); mFragment.logSpecialPermissionChange(true, "app"); verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class), eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_APPDRAW_ALLOW), eq("app")); @@ -66,4 +73,18 @@ public class DrawOverlayDetailsTest { verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class), eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_APPDRAW_DENY), eq("app")); } + + @Test + @Config(shadows = ShadowPreferenceFragment.class) + public void onStart_disableOverlay() { + mFragment.onStart(); + verify(mTouchOverlayManager).setOverlayAllowed(false); + } + + @Test + @Config(shadows = ShadowPreferenceFragment.class) + public void onStop_enableOverlay() { + mFragment.onStop(); + verify(mTouchOverlayManager).setOverlayAllowed(true); + } } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPreferenceFragment.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPreferenceFragment.java new file mode 100644 index 00000000000..cfd0ce931d6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPreferenceFragment.java @@ -0,0 +1,35 @@ +/* + * 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.settings.testutils.shadow; + +import android.support.v14.preference.PreferenceFragment; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(PreferenceFragment.class) +public class ShadowPreferenceFragment { + + @Implementation + public void onStart() { + // No-op. + } + + @Implementation + public void onStop() { + // No-op. + } +}