Merge "CTS/STS test for Android Security b/37469795" into lmp-dev
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 89498d0..eaf5cae 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application>
<uses-library android:name="android.test.runner" />
@@ -40,6 +41,7 @@
<service android:name="android.security.cts.IsolatedService"
android:process=":Isolated"
android:isolatedProcess="true"/>
+ <activity android:name="android.security.cts.activity.MotionEventTestActivity" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/security/src/android/security/cts/MotionEventTest.java b/tests/tests/security/src/android/security/cts/MotionEventTest.java
new file mode 100644
index 0000000..5a930a6
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/MotionEventTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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 android.security.cts;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import android.security.cts.activity.MotionEventTestActivity;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+
+public class MotionEventTest extends ActivityInstrumentationTestCase2<MotionEventTestActivity> {
+
+ private Instrumentation mInstrumentation;
+ private MotionEventTestActivity mActivity;
+
+ public MotionEventTest() {
+ super(MotionEventTestActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // Start the activity and get a reference to it.
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ // Wait for the UI Thread to become idle.
+ getInstrumentation().waitForIdleSync();
+ boolean hasFocus = mActivity.waitForWindowFocus(10000); // 10 s wait
+ assertTrue(hasFocus);
+ }
+
+ /**
+ * Test for whether ACTION_OUTSIDE events contain information about whether touches are
+ * obscured.
+ *
+ * If ACTION_OUTSIDE_EVENTS contain information about whether the touch is obscured, then a
+ * pattern of invisible, untouchable, unfocusable SYSTEM_ALERT_WINDOWS can be placed across the
+ * screen to determine approximate locations of touch events without the user knowing.
+ * @throws Exception
+ */
+ public void testActionOutsideDoesNotContainedObscuredInformation() throws Exception {
+ final OnTouchListener listener = new OnTouchListener();
+ final Point size = new Point();
+ final View[] viewHolder = new View[1];
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ final WindowManager wm =
+ (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getSize(size);
+
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+ WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+ wmlp.width = size.x / 4;
+ wmlp.height = size.y / 4;
+ wmlp.gravity = Gravity.TOP | Gravity.LEFT;
+ wmlp.setTitle(mActivity.getPackageName());
+
+ ViewGroup.LayoutParams vglp = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+
+ View v = new View(mActivity);
+ v.setOnTouchListener(listener);
+ v.setBackgroundColor(Color.GREEN);
+ v.setLayoutParams(vglp);
+ wm.addView(v, wmlp);
+
+ wmlp.gravity = Gravity.TOP | Gravity.RIGHT;
+
+ v = new View(mActivity);
+ v.setBackgroundColor(Color.BLUE);
+ v.setOnTouchListener(listener);
+ v.setLayoutParams(vglp);
+ viewHolder[0] = v;
+
+ wm.addView(v, wmlp);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ FutureTask<Point> task = new FutureTask<Point> (new Callable<Point>() {
+
+ @Override
+ public Point call() throws Exception {
+ final int[] viewLocation = new int[2];
+ viewHolder[0].getLocationOnScreen(viewLocation);
+ return new Point(viewLocation[0], viewLocation[1]);
+ }
+ });
+ mActivity.runOnUiThread(task);
+ Point viewLocation = task.get(5, TimeUnit.SECONDS);
+ injectTap(viewLocation.x, viewLocation.y);
+
+ List<MotionEvent> outsideEvents = listener.getOutsideEvents();
+ assertEquals(2, outsideEvents.size());
+ for (MotionEvent e : outsideEvents) {
+ assertEquals(0, e.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED);
+ }
+ }
+
+ private void injectTap(int x, int y) {
+ long downTime = SystemClock.uptimeMillis();
+ injectEvent(MotionEvent.ACTION_DOWN, x, y, downTime);
+ injectEvent(MotionEvent.ACTION_UP, x, y, downTime);
+ }
+
+ private void injectEvent(int action, int x, int y, long downTime) {
+ final UiAutomation automation = mInstrumentation.getUiAutomation();
+ final long eventTime = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ automation.injectInputEvent(event, true);
+ event.recycle();
+ }
+
+ private static class OnTouchListener implements View.OnTouchListener {
+ private List<MotionEvent> mOutsideEvents;
+
+ public OnTouchListener() {
+ mOutsideEvents = new ArrayList<MotionEvent>();
+ }
+
+ public boolean onTouch(View v, MotionEvent e) {
+ if (e.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ mOutsideEvents.add(MotionEvent.obtain(e));
+ }
+ return true;
+ }
+
+ public List<MotionEvent> getOutsideEvents() {
+ return mOutsideEvents;
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/activity/MotionEventTestActivity.java b/tests/tests/security/src/android/security/cts/activity/MotionEventTestActivity.java
new file mode 100644
index 0000000..d0d36da
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/activity/MotionEventTestActivity.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.security.cts.activity;
+
+import android.app.Activity;
+import android.os.SystemClock;
+
+
+public class MotionEventTestActivity extends Activity {
+ private boolean mHasWindowFocus = false;
+ private Object mHasWindowFocusLock = new Object();
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ synchronized(mHasWindowFocusLock) {
+ mHasWindowFocus = hasFocus;
+ mHasWindowFocusLock.notify();
+ }
+ }
+
+ /**
+ * Blocks the calling thread until the {@link MotionEventTestActivity}
+ * has window focus or the specified duration (in milliseconds) has passed.
+ */
+ public boolean waitForWindowFocus(long durationMillis) {
+ long elapsedMillis = SystemClock.elapsedRealtime();
+ synchronized(mHasWindowFocusLock) {
+ while (!mHasWindowFocus && durationMillis > 0) {
+ long newElapsedMillis = SystemClock.elapsedRealtime();
+ durationMillis -= (newElapsedMillis - elapsedMillis);
+ elapsedMillis = newElapsedMillis;
+ if (durationMillis > 0) {
+ try {
+ mHasWindowFocusLock.wait(durationMillis);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ return mHasWindowFocus;
+ }
+ }
+ }